From 803bd538f4c4c40e1d60df10cb6937bfea9dab26 Mon Sep 17 00:00:00 2001 From: Bogdan Gheorghe Date: Fri, 12 Feb 2010 16:17:39 +0000 Subject: CTabFolder renderer work --- .../common/org/eclipse/swt/custom/CTabFolder.java | 1294 ++++------------ .../org/eclipse/swt/custom/CTabFolderLayout.java | 20 +- .../org/eclipse/swt/custom/CTabFolderRenderer.java | 1618 ++++++++++++++++++++ .../common/org/eclipse/swt/custom/CTabItem.java | 628 +------- 4 files changed, 1914 insertions(+), 1646 deletions(-) create mode 100644 bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderRenderer.java (limited to 'bundles') diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java index e9c5af2c84..81203034ea 100755 --- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java @@ -107,15 +107,16 @@ public class CTabFolder extends Composite { public static RGB borderOutsideRGB = new RGB (171, 168, 165); /* sizing, positioning */ - int xClient, yClient; boolean onBottom = false; boolean single = false; boolean simple = true; int fixedTabHeight = SWT.DEFAULT; int tabHeight; int minChars = 20; + boolean borderVisible = false; /* item management */ + CTabFolderRenderer renderer; CTabItem items[] = new CTabItem[0]; int firstIndex = -1; // index of the left most visible tab. int selectedIndex = -1; @@ -127,7 +128,7 @@ public class CTabFolder extends Composite { /* External Listener management */ CTabFolder2Listener[] folderListeners = new CTabFolder2Listener[0]; // support for deprecated listener mechanism - CTabFolderListener[] tabListeners = new CTabFolderListener[0]; + CTabFolderListener[] tabListeners = new CTabFolderListener[0]; /* Selected item appearance */ Image selectionBgImage; @@ -135,68 +136,37 @@ public class CTabFolder extends Composite { int[] selectionGradientPercents; boolean selectionGradientVertical; Color selectionForeground; - Color selectionBackground; //selection fade end - Color selectionFadeStart; - - Color selectionHighlightGradientBegin = null; //null == 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 - - /* Colors for anti-aliasing */ - Color selectedOuterColor = null; - Color selectedInnerColor = null; - Color tabAreaColor = null; + Color selectionBackground; /* Unselected item appearance */ - Color[] gradientColors; + Color[] gradientColors; int[] gradientPercents; boolean gradientVertical; boolean showUnselectedImage = true; // close, min/max and chevron buttons - Color fillColor; boolean showClose = false; boolean showUnselectedClose = true; Rectangle chevronRect = new Rectangle(0, 0, 0, 0); - int chevronImageState = NORMAL; + int chevronImageState = SWT.NONE; boolean showChevron = false; Menu showMenu; boolean showMin = false; Rectangle minRect = new Rectangle(0, 0, 0, 0); boolean minimized = false; - int minImageState = NORMAL; + int minImageState = SWT.NONE; boolean showMax = false; Rectangle maxRect = new Rectangle(0, 0, 0, 0); boolean maximized = false; - int maxImageState = NORMAL; + int maxImageState = SWT.NONE; Control topRight; Rectangle topRightRect = new Rectangle(0, 0, 0, 0); int topRightAlignment = SWT.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. boolean inDispose = false; @@ -205,63 +175,22 @@ public class CTabFolder extends Composite { // on Resize Point oldSize; Font oldFont; - /* - * Border color that was used in computing the cached anti-alias Colors. - * We have to recompute the colors if the border color changes - */ - Color lastBorderColor = null; // internal constants static final int DEFAULT_WIDTH = 64; static final int DEFAULT_HEIGHT = 64; - static final int BUTTON_SIZE = 18; - - static final 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 final int[] TOP_LEFT_CORNER_HILITE = new int[] {5,2, 4,2, 3,3, 2,4, 2,5, 1,6}; - - static final int[] TOP_RIGHT_CORNER = new int[] {-6,0, -5,1, -4,1, -1,4, -1,5, 0,6}; - static final int[] BOTTOM_LEFT_CORNER = new int[] {0,-6, 1,-5, 1,-4, 4,-1, 5,-1, 6,0}; - static final int[] BOTTOM_RIGHT_CORNER = new int[] {-6,0, -5,-1, -4,-1, -1,-4, -1,-5, 0,-6}; - - static final int[] SIMPLE_TOP_LEFT_CORNER = new int[] {0,2, 1,1, 2,0}; - static final int[] SIMPLE_TOP_RIGHT_CORNER = new int[] {-2,0, -1,1, 0,2}; - static final int[] SIMPLE_BOTTOM_LEFT_CORNER = new int[] {0,-2, 1,-1, 2,0}; - static final int[] SIMPLE_BOTTOM_RIGHT_CORNER = new int[] {-2,0, -1,-1, 0,-2}; - static final int[] SIMPLE_UNSELECTED_INNER_CORNER = new int[] {0,0}; - - static final int[] TOP_LEFT_CORNER_BORDERLESS = new int[] {0,6, 1,5, 1,4, 4,1, 5,1, 6,0}; - static final int[] TOP_RIGHT_CORNER_BORDERLESS = new int[] {-7,0, -6,1, -5,1, -2,4, -2,5, -1,6}; - static final int[] BOTTOM_LEFT_CORNER_BORDERLESS = new int[] {0,-6, 1,-6, 1,-5, 2,-4, 4,-2, 5,-1, 6,-1, 6,0}; - static final int[] BOTTOM_RIGHT_CORNER_BORDERLESS = new int[] {-7,0, -7,-1, -6,-1, -5,-2, -3,-4, -2,-5, -2,-6, -1,-6}; - - static final int[] SIMPLE_TOP_LEFT_CORNER_BORDERLESS = new int[] {0,2, 1,1, 2,0}; - static final int[] SIMPLE_TOP_RIGHT_CORNER_BORDERLESS= new int[] {-3,0, -2,1, -1,2}; - static final int[] SIMPLE_BOTTOM_LEFT_CORNER_BORDERLESS = new int[] {0,-3, 1,-2, 2,-1, 3,0}; - static final int[] SIMPLE_BOTTOM_RIGHT_CORNER_BORDERLESS = new int[] {-4,0, -3,-1, -2,-2, -1,-3}; - + static final int SELECTION_FOREGROUND = SWT.COLOR_LIST_FOREGROUND; static final int SELECTION_BACKGROUND = SWT.COLOR_LIST_BACKGROUND; - static final int BORDER1_COLOR = SWT.COLOR_WIDGET_NORMAL_SHADOW; + static final int FOREGROUND = SWT.COLOR_WIDGET_FOREGROUND; static final int BACKGROUND = SWT.COLOR_WIDGET_BACKGROUND; - static final int BUTTON_BORDER = SWT.COLOR_WIDGET_DARK_SHADOW; - static final int BUTTON_FILL = SWT.COLOR_LIST_BACKGROUND; - - static final int NONE = 0; - static final int NORMAL = 1; - static final int HOT = 2; - static final int SELECTED = 3; - static final RGB CLOSE_FILL = new RGB(252, 160, 160); static final int CHEVRON_CHILD_ID = 0; static final int MINIMIZE_CHILD_ID = 1; static final int MAXIMIZE_CHILD_ID = 2; static final 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. @@ -307,15 +236,12 @@ void init(int style) { // showMin = (style2 & SWT.MIN) != 0; - conflicts with SWT.TOP // showMax = (style2 & SWT.MAX) != 0; - conflicts with SWT.BOTTOM single = (style2 & SWT.SINGLE) != 0; - borderLeft = borderRight = (style & SWT.BORDER) != 0 ? 1 : 0; - borderTop = onBottom ? borderLeft : 0; - borderBottom = onBottom ? 0 : borderLeft; - highlight_header = (style & SWT.FLAT) != 0 ? 1 : 3; - highlight_margin = (style & SWT.FLAT) != 0 ? 0 : 2; + borderVisible = (style & SWT.BORDER) != 0; //set up default colors Display display = getDisplay(); selectionForeground = display.getSystemColor(SELECTION_FOREGROUND); selectionBackground = display.getSystemColor(SELECTION_BACKGROUND); + renderer = new CTabFolderRenderer(this); updateTabHeight(false); // Add all listeners @@ -396,16 +322,7 @@ static int checkStyle (Composite parent, int style) { return style | SWT.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 @@ -505,49 +422,7 @@ public void addSelectionListener(SelectionListener listener) { addListener(SWT.Selection, typedListener); addListener(SWT.DefaultSelection, typedListener); } -void antialias (int[] shape, Color innerColor, Color outerColor, 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) return; - String platform = SWT.getPlatform(); - if ("cocoa".equals(platform)) return; //$NON-NLS-1$ - if ("carbon".equals(platform)) return; //$NON-NLS-1$ - if ("wpf".equals(platform)) return; //$NON-NLS-1$ - // Don't perform anti-aliasing on low resolution displays - if (getDisplay().getDepth() < 15) return; - if (outerColor != null) { - int index = 0; - boolean 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++]; - } - gc.setForeground(outerColor); - gc.drawPolyline(outer); - } - if (innerColor != null) { - int[] inner = new int[shape.length]; - int index = 0; - boolean 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++]; - } - gc.setForeground(innerColor); - gc.drawPolyline(inner); - } -} + /* * This class was not intended to be subclassed but this restriction * cannot be enforced without breaking backward compatibility. @@ -561,17 +436,7 @@ void antialias (int[] shape, Color innerColor, Color outerColor, GC gc){ //} 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); - } + return renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.NONE, x, y, width, height); } void createItem (CTabItem item, int index) { if (0 > index || index > getItemCount ())SWT.error (SWT.ERROR_INVALID_RANGE); @@ -617,7 +482,9 @@ void destroyItem (CTabItem item) { control.setVisible(false); } setToolTipText(null); - setButtonBounds(); + GC gc = new GC(this); + setButtonBounds(gc); + gc.dispose(); redraw(); return; } @@ -651,518 +518,7 @@ void destroyItem (CTabItem item) { updateItems(); redrawTabs(); } -void drawBackground(GC gc, int[] shape, boolean selected) { - Color defaultBackground = selected ? selectionBackground : getBackground(); - Image image = selected ? selectionBgImage : null; - Color[] colors = selected ? selectionGradientColors : gradientColors; - int[] percents = selected ? selectionGradientPercents : gradientPercents; - boolean 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, boolean vertical) { - Region clipping = new Region(); - gc.getClipping(clipping); - Region region = new Region(); - region.add(shape); - region.intersect(clipping); - gc.setClipping(region); - - if (image != 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 != null) { - // draw gradient - if (colors.length == 1) { - Color background = colors[0] != 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 = (100 - percents[percents.length - 1]) * height / 100; - gc.setBackground(defaultBackground); - gc.fillRectangle(x, y, width, pos); - } - Color lastColor = colors[colors.length-1]; - if (lastColor == null) lastColor = defaultBackground; - for (int i = percents.length-1; i >= 0; i--) { - gc.setForeground(lastColor); - lastColor = colors[i]; - if (lastColor == null) lastColor = defaultBackground; - gc.setBackground(lastColor); - int percentage = i > 0 ? percents[i] - percents[i-1] : percents[i]; - int gradientHeight = percentage * height / 100; - gc.fillGradientRectangle(x, y+pos, width, gradientHeight, true); - pos += gradientHeight; - } - } else { - Color lastColor = colors[0]; - if (lastColor == null) lastColor = defaultBackground; - int pos = 0; - for (int i = 0; i < percents.length; i++) { - gc.setForeground(lastColor); - lastColor = colors[i + 1]; - if (lastColor == null) lastColor = defaultBackground; - gc.setBackground(lastColor); - int percentage = i > 0 ? percents[i] - percents[i-1] : percents[i]; - int gradientHeight = percentage * 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 == null) lastColor = defaultBackground; - int pos = 0; - for (int i = 0; i < percents.length; ++i) { - gc.setForeground(lastColor); - lastColor = colors[i + 1]; - if (lastColor == 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() & SWT.NO_BACKGROUND) != 0 || !defaultBackground.equals(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 != -1 && selectionGradientColors != null && selectionGradientColors.length > 1 && !selectionGradientVertical) { - drawBackground(gc, shape, true); - } else if (selectedIndex == -1 && gradientColors != null && gradientColors.length > 1 && !gradientVertical) { - drawBackground(gc, shape, false); - } else { - gc.setBackground(selectedIndex == -1 ? getBackground() : selectionBackground); - gc.fillPolygon(shape); - } - } - //Draw client area - if ((getStyle() & SWT.NO_BACKGROUND) != 0) { - gc.setBackground(getBackground()); - gc.fillRectangle(xClient - marginWidth, yClient - marginHeight, width, height); - } - } else { - if ((getStyle() & SWT.NO_BACKGROUND) != 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(getDisplay().getSystemColor(BORDER1_COLOR)); - 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 == 0 || chevronRect.height == 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 == -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 == 0 || maxRect.height == 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 == 0 || minRect.height == 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; - Color borderColor = getDisplay().getSystemColor(BORDER1_COLOR); - - if (tabHeight == 0) { - int style = getStyle(); - if ((style & SWT.FLAT) != 0 && (style & SWT.BORDER) == 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 != -1 && selectionGradientColors != null && selectionGradientColors.length > 1 && !selectionGradientVertical) { - drawBackground(gc, shape, true); - } else if (selectedIndex == -1 && gradientColors != null && gradientColors.length > 1 && !gradientVertical) { - drawBackground(gc, shape, false); - } else { - gc.setBackground(selectedIndex == -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() & SWT.BORDER) != 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 == 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 == 0) shape[index-1] += 1; - } - shape[index++] = x+width; - shape[index++] = y-highlight_header; - } else { - int[] left, right; - if ((getStyle() & SWT.BORDER) != 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 - boolean bkSelected = single && selectedIndex != -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 != selectedIndex && event.getBounds().intersects(items[i].getBounds())) { - items[i].onPaint(gc, false); - } - } - } - - // Draw selected tab - if (selectedIndex != -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) { - if (! borderColor.equals(lastBorderColor)) createAntialiasColors(); - antialias(shape, null, tabAreaColor, gc); - gc.setForeground(borderColor); - gc.drawPolyline(shape); - } -} /** * Returns true if the receiver's border is visible. * @@ -1177,23 +533,18 @@ void drawTabArea(Event event) { */ public boolean getBorderVisible() { checkWidget(); - return borderLeft == 1; + return borderVisible; } public Rectangle getClientArea() { checkWidget(); - if (minimized) return new Rectangle(xClient, yClient, 0, 0); + Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.NONE, 0, 0, 0, 0); + if (minimized) return new Rectangle(-trim.x, -trim.y, 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); -} -Color getFillColor() { - if (fillColor == null) { - fillColor = new Color(getDisplay(), CTabFolder.CLOSE_FILL); - } - return fillColor; + int width = size.x - trim.width; + int height = size.y - trim.height; + return new Rectangle(-trim.x, -trim.y, width, height); } + /** * Return the tab that is located at the specified index. * @@ -1229,7 +580,8 @@ public CTabItem getItem (Point pt) { //checkWidget(); if (items.length == 0) return null; Point size = getSize(); - if (size.x <= borderLeft + borderRight) return null; + Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_HEADER, SWT.NONE, 0, 0, 0, 0); + if (size.x <= trim.width) return null; if (showChevron && chevronRect.contains(pt)) return null; for (int i = 0; i < priority.length; i++) { CTabItem item = items[priority[i]]; @@ -1268,6 +620,7 @@ public CTabItem [] getItems() { 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 '&' @@ -1343,6 +696,7 @@ public int getMinimumCharacters() { checkWidget(); return minChars; } + /** * Returns true if the receiver is maximized. *

@@ -1410,11 +764,16 @@ public boolean 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; +/*public*/ CTabFolderRenderer getRenderer() { + checkWidget(); + return renderer; +} +int getRightItemEdge (GC gc){ + Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_HEADER, SWT.NONE, 0, 0, 0, 0); + int x = getSize().x - (trim.width + trim.x) - 3; //TODO: add setter for spacing? + if (showMin) x -= renderer.computeSize(CTabFolderRenderer.PART_MIN_BUTTON, SWT.NONE, gc).x; + if (showMax) x -= renderer.computeSize(CTabFolderRenderer.PART_MAX_BUTTON, SWT.NONE, gc).x;; + if (showChevron) x -= renderer.computeSize(CTabFolderRenderer.PART_CHEVRON_BUTTON, SWT.NONE, gc).x; if (topRight != null && topRightAlignment != SWT.FILL) { Point rightSize = topRight.computeSize(SWT.DEFAULT, SWT.DEFAULT); x -= rightSize.x + 3; @@ -1514,7 +873,7 @@ public int getStyle() { style |= onBottom ? SWT.BOTTOM : SWT.TOP; style &= ~(SWT.SINGLE | SWT.MULTI); style |= single ? SWT.SINGLE : SWT.MULTI; - if (borderLeft != 0) style |= SWT.BORDER; + if (borderVisible) style |= SWT.BORDER; style &= ~SWT.CLOSE; if (showClose) style |= SWT.CLOSE; return style; @@ -1532,7 +891,7 @@ public int getStyle() { public int getTabHeight(){ checkWidget(); if (fixedTabHeight != SWT.DEFAULT) return fixedTabHeight; - return tabHeight - 1; // -1 for line drawn across top of tab + return tabHeight - 1; // -1 for line drawn across top of tab //TODO: replace w/ computeTrim of tab area? } /** * Returns the position of the tab. Possible values are SWT.TOP or SWT.BOTTOM. @@ -1565,6 +924,23 @@ public Control getTopRight() { checkWidget(); return topRight; } +/** + * Returns the alignment of the top right control. + * + * @return the alignment of the top right control which is either + * SWT.RIGHT or SWT.FILL + * + * @exception SWTException

+ * + * @since 3.6 + */ +public int getTopRightAlignment() { + checkWidget(); + return topRightAlignment; +} /** * Returns true if the close button appears * when the user hovers over an unselected tabs. @@ -1892,10 +1268,7 @@ void onDispose(Event event) { items[i].dispose(); } } - if (fillColor != null) { - fillColor.dispose(); - fillColor = null; - } + selectionGradientColors = null; selectionGradientPercents = null; @@ -1904,8 +1277,8 @@ void onDispose(Event event) { selectionBackground = null; selectionForeground = null; - disposeSelectionHighlightGradientColors(); - disposeAntialiasColors(); + if (renderer != null) renderer.dispose(); + renderer = null; } void onDragDetect(Event event) { boolean consume = false; @@ -1969,26 +1342,26 @@ void onMouse(Event event) { break; } case SWT.MouseExit: { - if (minImageState != NORMAL) { - minImageState = NORMAL; + if (minImageState != SWT.NONE) { + minImageState = SWT.NONE; redraw(minRect.x, minRect.y, minRect.width, minRect.height, false); } - if (maxImageState != NORMAL) { - maxImageState = NORMAL; + if (maxImageState != SWT.NONE) { + maxImageState = SWT.NONE; redraw(maxRect.x, maxRect.y, maxRect.width, maxRect.height, false); } - if (chevronImageState != NORMAL) { - chevronImageState = NORMAL; + if (chevronImageState != SWT.NONE) { + chevronImageState = SWT.NONE; redraw(chevronRect.x, chevronRect.y, chevronRect.width, chevronRect.height, false); } for (int i=0; i 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; + maxRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - maxSize.y)/2: borderTop + (tabHeight - maxSize.y)/2; + maxRect.width = maxSize.x; + maxRect.height = maxSize.y; } if (oldX != maxRect.x || oldWidth != maxRect.width || oldY != maxRect.y || oldHeight != maxRect.height) { @@ -2671,7 +2083,7 @@ void setButtonBounds() { int top = onBottom ? size.y - borderBottom - tabHeight: borderTop + 1; redraw(left, top, right - left, tabHeight, false); } - + // min button oldX = minRect.x; oldY = minRect.y; @@ -2679,11 +2091,12 @@ void setButtonBounds() { oldHeight = minRect.height; minRect.x = minRect.y = minRect.width = minRect.height = 0; if (showMin) { - minRect.x = size.x - borderRight - maxRect.width - BUTTON_SIZE - 3; + Point minSize = renderer.computeSize(CTabFolderRenderer.PART_MIN_BUTTON, SWT.NONE, gc); + minRect.x = size.x - borderRight - maxRect.width - minSize.x - 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; + minRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - minSize.y)/2: borderTop + (tabHeight - minSize.y)/2; + minRect.width = minSize.x; + minRect.height = minSize.y; } if (oldX != minRect.x || oldWidth != minRect.width || oldY != minRect.y || oldHeight != minRect.height) { @@ -2711,8 +2124,9 @@ void setButtonBounds() { } 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; + int chevronWidth = renderer.computeSize(CTabFolderRenderer.PART_CHEVRON_BUTTON, SWT.NONE, gc).x; + if (item.x + item.width + 7 + chevronWidth >= rightEdge) break; + topRightRect.x = item.x + item.width + 7 + chevronWidth; topRightRect.width = rightEdge - topRightRect.x; } } else { @@ -2721,9 +2135,9 @@ void setButtonBounds() { if (items.length == 0) { topRightRect.x = borderLeft + 3; } else { - CTabItem item = items[items.length - 1]; - topRightRect.x = item.x + item.width; - if (!simple && items.length - 1 == selectedIndex) topRightRect.x += curveWidth - curveIndent; + int lastIndex = items.length - 1; + CTabItem lastItem = items[lastIndex]; + topRightRect.x = lastItem.x + lastItem.width; } topRightRect.width = Math.max(0, rightEdge - topRightRect.x); } @@ -2757,10 +2171,11 @@ void setButtonBounds() { oldWidth = chevronRect.width; oldHeight = chevronRect.height; chevronRect.x = chevronRect.y = chevronRect.height = chevronRect.width = 0; + Point chevronSize = renderer.computeSize(CTabFolderRenderer.PART_CHEVRON_BUTTON, SWT.NONE, gc); if (single) { if (selectedIndex == -1 || items.length > 1) { - chevronRect.width = 3*BUTTON_SIZE/2; - chevronRect.height = BUTTON_SIZE; + chevronRect.width = chevronSize.x; + chevronRect.height = chevronSize.y; chevronRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - chevronRect.height)/2 : borderTop + (tabHeight - chevronRect.height)/2; if (selectedIndex == -1) { chevronRect.x = size.x - borderRight - 3 - minRect.width - maxRect.width - topRightRect.width - chevronRect.width; @@ -2774,8 +2189,8 @@ void setButtonBounds() { } } else { if (showChevron) { - chevronRect.width = 3*BUTTON_SIZE/2; - chevronRect.height = BUTTON_SIZE; + chevronRect.width = chevronSize.x; + chevronRect.height = chevronSize.y; int i = 0, lastIndex = -1; while (i < priority.length && items[priority[i]].showing) { lastIndex = Math.max(lastIndex, priority[i++]); @@ -2783,8 +2198,8 @@ void setButtonBounds() { if (lastIndex == -1) lastIndex = firstIndex; CTabItem lastItem = items[lastIndex]; int w = lastItem.x + lastItem.width + 3; - if (!simple && lastIndex == selectedIndex) w += curveWidth - 2*curveIndent; - chevronRect.x = Math.min(w, getRightItemEdge()); + if (!simple && lastIndex == selectedIndex) w -= renderer.curveIndent; //TODO: fix chevron position + chevronRect.x = Math.min(w, getRightItemEdge(gc)); chevronRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - chevronRect.height)/2 : borderTop + (tabHeight - chevronRect.height)/2; } } @@ -2850,11 +2265,16 @@ public void setInsertMark(int index, boolean after) { SWT.error(SWT.ERROR_INVALID_ARGUMENT); } } -boolean setItemLocation() { +boolean setItemLocation(GC gc) { boolean changed = false; if (items.length == 0) return false; + Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BORDER, SWT.NONE, 0, 0, 0, 0); + int borderLeft = -trim.x; + int borderBottom = trim.height + trim.y; + int borderTop = -trim.y; Point size = getSize(); int y = onBottom ? Math.max(borderBottom, size.y - borderBottom - tabHeight) : borderTop; + Point closeButtonSize = renderer.computeSize(CTabFolderRenderer.PART_CLOSE_BUTTON, 0, gc); if (single) { int defaultX = getDisplay().getBounds().width + 10; // off screen for (int i = 0; i < items.length; i++) { @@ -2866,8 +2286,8 @@ boolean setItemLocation() { 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; + item.closeRect.x = borderLeft - renderer.computeTrim(i, SWT.NONE, 0, 0, 0, 0).x; + item.closeRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - closeButtonSize.y)/2: borderTop + (tabHeight - closeButtonSize.y)/2; } if (item.x != oldX || item.y != oldY) changed = true; } else { @@ -2876,14 +2296,13 @@ boolean setItemLocation() { } } } else { - int rightItemEdge = getRightItemEdge(); + int rightItemEdge = getRightItemEdge(gc); 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 == 0 ? true : item.width > 0 && width <= maxWidth; - if (!simple && priority[i] == selectedIndex) width += curveWidth - 2*curveIndent; } int x = 0; int defaultX = getDisplay().getBounds().width + 10; // off screen @@ -2898,40 +2317,35 @@ boolean setItemLocation() { if (item.x != x || item.y != y) changed = true; item.x = x; item.y = y; - if (i == 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; + int state = SWT.NONE; + if (i == selectedIndex) state |= SWT.SELECTED; + Rectangle edgeTrim = renderer.computeTrim(i, state, 0, 0, 0, 0); + item.closeRect.x = item.x + item.width - (edgeTrim.width + edgeTrim.x) - closeButtonSize.x; + item.closeRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - closeButtonSize.y)/2: borderTop + (tabHeight - closeButtonSize.y)/2; x = x + item.width; - if (!simple && i == selectedIndex) x += curveWidth - 2*curveIndent; + if (!simple && i == selectedIndex) x -= renderer.curveIndent; //TODO: fix next item position } } } return changed; } -boolean setItemSize() { +boolean setItemSize(GC gc) { boolean 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; - } + + Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_HEADER, SWT.NONE, 0, 0, 0, 0); + int borderRight = trim.width + trim.x; + int borderLeft = -trim.x; + showChevron = false; if (single) { showChevron = true; if (selectedIndex != -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); + int width = renderer.computeSize(selectedIndex, SWT.SELECTED, gc).x; + width = Math.min(width, getRightItemEdge(gc) - borderLeft); if (tab.height != tabHeight || tab.width != width) { changed = true; tab.shortenedText = null; @@ -2940,8 +2354,9 @@ boolean setItemSize() { tab.width = width; tab.closeRect.width = tab.closeRect.height = 0; if (showClose || tab.showClose) { - tab.closeRect.width = BUTTON_SIZE; - tab.closeRect.height = BUTTON_SIZE; + Point closeSize = renderer.computeSize(selectedIndex, SWT.SELECTED, gc); + tab.closeRect.width = closeSize.x; + tab.closeRect.height = closeSize.y; } } } @@ -2951,30 +2366,30 @@ boolean setItemSize() { if (items.length == 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 (showMin) tabAreaWidth -= renderer.computeSize(CTabFolderRenderer.PART_MIN_BUTTON, SWT.NONE, gc).x; + if (showMax) tabAreaWidth -= renderer.computeSize(CTabFolderRenderer.PART_MAX_BUTTON, SWT.NONE, gc).x; if (topRightAlignment == SWT.RIGHT && topRight != null) { Point rightSize = topRight.computeSize(SWT.DEFAULT, SWT.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 == selectedIndex, true); + int state = CTabFolderRenderer.MINIMUM_SIZE; + if (index == selectedIndex) state |= SWT.SELECTED; + minWidths[index] = renderer.computeSize(index, state, gc).x; 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; + if (showChevron) tabAreaWidth -= renderer.computeSize(CTabFolderRenderer.PART_CHEVRON_BUTTON, SWT.NONE, gc).x; widths = minWidths; int index = selectedIndex != -1 ? selectedIndex : 0; if (tabAreaWidth < widths[index]) { @@ -2984,7 +2399,9 @@ boolean setItemSize() { int maxWidth = 0; int[] maxWidths = new int[items.length]; for (int i = 0; i < items.length; i++) { - maxWidths[i] = items[i].preferredWidth(gc, i == selectedIndex, false); + int state = 0; + if (i == selectedIndex) state |= SWT.SELECTED; + maxWidths[i] = renderer.computeSize(i, state, gc).x; maxWidth += maxWidths[i]; } if (maxWidth <= tabAreaWidth) { @@ -3016,7 +2433,6 @@ boolean setItemSize() { } } } - gc.dispose(); for (int i = 0; i < items.length; i++) { CTabItem tab = items[i]; @@ -3030,8 +2446,9 @@ boolean setItemSize() { tab.closeRect.width = tab.closeRect.height = 0; if (showClose || tab.showClose) { if (i == selectedIndex || showUnselectedClose) { - tab.closeRect.width = BUTTON_SIZE; - tab.closeRect.height = BUTTON_SIZE; + Point closeSize = renderer.computeSize(CTabFolderRenderer.PART_CLOSE_BUTTON, SWT.NONE, gc); + tab.closeRect.width = closeSize.x; + tab.closeRect.height = closeSize.y; } } } @@ -3202,6 +2619,16 @@ public void setMRUVisible(boolean show) { if (updateItems()) redrawTabs(); } } +/*public*/ void setRenderer(CTabFolderRenderer renderer) { + checkWidget(); + if (this.renderer != null) { + this.renderer.dispose(); + } + if (renderer == null) { + renderer = new CTabFolderRenderer(this); + } + this.renderer = renderer; +} /** * Set the selection to the tab at the specified item. * @@ -3244,9 +2671,9 @@ public void setSelection(int index) { int oldIndex = selectedIndex; selectedIndex = index; if (oldIndex != -1) { - items[oldIndex].closeImageState = NONE; + items[oldIndex].closeImageState = SWT.BACKGROUND; } - selection.closeImageState = NORMAL; + selection.closeImageState = SWT.NONE; selection.showing = false; Control newControl = selection.control; @@ -3299,7 +2726,7 @@ public void setSelectionBackground (Color color) { if (selectionBackground == color) return; if (color == null) color = getDisplay().getSystemColor(SELECTION_BACKGROUND); selectionBackground = color; - createAntialiasColors(); + renderer.createAntialiasColors(); //TODO: need better caching strategy if (selectedIndex > -1) redraw(); } /** @@ -3450,58 +2877,8 @@ public void setSelectionBackground(Color[] colors, int[] percents, boolean verti * 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 == 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. - */ -boolean isSelectionHighlightColorsCacheHit(Color start) { - - if(selectionHighlightGradientColorsCache == 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.equals(start)) - return false; - - //Compare number of colours we have vs. we'd compute - if(selectionHighlightGradientColorsCache.length != tabHeight) - return false; - - //Compare existing highlight end to what it would be (selectionBackground) - if(! highlightEnd.equals(selectionBackground)) - return false; - - return true; + renderer.setSelectionHighlightGradientColor(start); //TODO: need better caching strategy } /** @@ -3522,10 +2899,10 @@ public void setSelectionBackground(Image image) { if (image != null) { selectionGradientColors = null; selectionGradientPercents = null; - disposeSelectionHighlightGradientColors(); + renderer.disposeSelectionHighlightGradientColors(); //TODO: need better caching strategy } selectionBgImage = image; - createAntialiasColors(); + renderer.createAntialiasColors(); //TODO: need better caching strategy if (selectedIndex > -1) redraw(); } /** @@ -3546,59 +2923,6 @@ public void setSelectionForeground (Color 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 == 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 == 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 == null) - return getSelectionBackground(); - if (selectionGradientColors.length == 0) - return getSelectionBackground(); - return selectionGradientColors[0]; -} - /** * Sets the shape that the CTabFolder will use to render itself. * @@ -3642,8 +2966,8 @@ public void setSingle(boolean single) { this.single = single; if (!single) { for (int i = 0; i < items.length; i++) { - if (i != selectedIndex && items[i].closeImageState == NORMAL) { - items[i].closeImageState = NONE; + if (i != selectedIndex && items[i].closeImageState == SWT.NONE) { + items[i].closeImageState = SWT.BACKGROUND; } } } @@ -3698,8 +3022,6 @@ public void setTabPosition(int position) { } if (onBottom != (position == SWT.BOTTOM)) { onBottom = position == SWT.BOTTOM; - borderTop = onBottom ? borderLeft : 0; - borderBottom = onBottom ? 0 : borderRight; updateTabHeight(true); Rectangle rectBefore = getClientArea(); updateItems(); @@ -3917,24 +3239,29 @@ boolean updateItems() { } boolean updateItems(int showIndex) { + GC gc = new GC(this); if (!single && !mru && showIndex != -1) { // make sure selected item will be showing int firstIndex = showIndex; if (priority[0] < showIndex) { - int maxWidth = getRightItemEdge() - borderLeft; - if (!simple) maxWidth -= curveWidth - 2*curveIndent; + Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_HEADER, SWT.NONE, 0, 0, 0, 0); + int borderLeft = -trim.x; + int maxWidth = getRightItemEdge(gc) - borderLeft; 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 == selectedIndex, true); + int state = CTabFolderRenderer.MINIMUM_SIZE; + if (i == selectedIndex) state |= SWT.SELECTED; + widths[i] = renderer.computeSize(i, state, gc).x; width += widths[i]; if (width > maxWidth) break; } if (width > maxWidth) { width = 0; for (int i = showIndex; i >= 0; i--) { - if (widths[i] == 0) widths[i] = items[i].preferredWidth(gc, i == selectedIndex, true); + int state = CTabFolderRenderer.MINIMUM_SIZE; + if (i == selectedIndex) state |= SWT.SELECTED; + if (widths[i] == 0) widths[i] = renderer.computeSize(i, state, gc).x; width += widths[i]; if (width > maxWidth) break; firstIndex = i; @@ -3942,20 +3269,24 @@ boolean updateItems(int showIndex) { } else { firstIndex = priority[0]; for (int i = showIndex + 1; i < items.length; i++) { - widths[i] = items[i].preferredWidth(gc, i == selectedIndex, true); + int state = CTabFolderRenderer.MINIMUM_SIZE; + if (i == selectedIndex) state |= SWT.SELECTED; + widths[i] = renderer.computeSize(i, state, gc).x; width += widths[i]; if (width >= maxWidth) break; } if (width < maxWidth) { for (int i = priority[0] - 1; i >= 0; i--) { - if (widths[i] == 0) widths[i] = items[i].preferredWidth(gc, i == selectedIndex, true); + int state = CTabFolderRenderer.MINIMUM_SIZE; + if (i == selectedIndex) state |= SWT.SELECTED; + if (widths[i] == 0) widths[i] = renderer.computeSize(i, state, gc).x; width += widths[i]; if (width > maxWidth) break; firstIndex = i; } } } - gc.dispose(); + } if (firstIndex != priority[0]) { int index = 0; @@ -3969,72 +3300,25 @@ boolean updateItems(int showIndex) { } boolean oldShowChevron = showChevron; - boolean changed = setItemSize(); - changed |= setItemLocation(); - setButtonBounds(); + boolean changed = setItemSize(gc); + changed |= setItemLocation(gc); + setButtonBounds(gc); changed |= showChevron != oldShowChevron; if (changed && getToolTipText() != null) { Point pt = getDisplay().getCursorLocation(); pt = toControl(pt); _setToolTipText(pt.x, pt.y); } + gc.dispose(); return changed; } boolean updateTabHeight(boolean force){ - int style = getStyle(); - if (fixedTabHeight == 0 && (style & SWT.FLAT) != 0 && (style & SWT.BORDER) == 0) highlight_header = 0; int oldHeight = tabHeight; - if (fixedTabHeight != SWT.DEFAULT) { - tabHeight = fixedTabHeight == 0 ? 0 : fixedTabHeight + 1; // +1 for line drawn across top of tab - } else { - int tempHeight = 0; - GC gc = new GC(this); - if (items.length == 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; - } + GC gc = new GC(this); + tabHeight = renderer.computeSize(CTabFolderRenderer.PART_HEADER, SWT.NONE, gc).y; + gc.dispose(); if (!force && tabHeight == 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(SWT.Resize, new Event()); return true; } @@ -4050,52 +3334,4 @@ String _getToolTip(int x, int y) { } return item.getToolTipText(); } -void createAntialiasColors() { - disposeAntialiasColors(); - lastBorderColor = getDisplay().getSystemColor(BORDER1_COLOR); - RGB lineRGB = lastBorderColor.getRGB(); - /* compute the selected color */ - RGB innerRGB = selectionBackground.getRGB(); - if (selectionBgImage != null || - (selectionGradientColors != null && selectionGradientColors.length > 1)) { - innerRGB = null; - } - RGB outerRGB = getBackground().getRGB(); - if (gradientColors != null && gradientColors.length > 1) { - outerRGB = null; - } - if (outerRGB != null) { - 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; - selectedOuterColor = new Color(getDisplay(), red, green, blue); - } - if (innerRGB != null) { - 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; - selectedInnerColor = new Color(getDisplay(), red, green, blue); - } - /* compute the tabArea color */ - outerRGB = getParent().getBackground().getRGB(); - if (outerRGB != null) { - 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; - tabAreaColor = new Color(getDisplay(), red, green, blue); - } -} - -void disposeAntialiasColors() { - if (tabAreaColor != null) tabAreaColor.dispose(); - if (selectedInnerColor != null) selectedInnerColor.dispose(); - if (selectedOuterColor != null) selectedOuterColor.dispose(); - tabAreaColor = selectedInnerColor = selectedOuterColor = null; -} } diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderLayout.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderLayout.java index e01fac6e55..2bba73a552 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderLayout.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderLayout.java @@ -23,26 +23,32 @@ class CTabFolderLayout extends Layout { protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { CTabFolder folder = (CTabFolder)composite; CTabItem[] items = folder.items; + CTabFolderRenderer renderer = folder.renderer; // preferred width of tab area to show all tabs int tabW = 0; + int selectedIndex = folder.selectedIndex; + if (selectedIndex == -1) selectedIndex = 0; GC gc = new GC(folder); for (int i = 0; i < items.length; i++) { if (folder.single) { - tabW = Math.max(tabW, items[i].preferredWidth(gc, true, false)); + tabW = Math.max(tabW, renderer.computeSize(i, SWT.SELECTED, gc).x); } else { - tabW += items[i].preferredWidth(gc, i == folder.selectedIndex, false); + int state = 0; + if (i == selectedIndex) state |= SWT.SELECTED; + tabW += renderer.computeSize(i, state, gc).x; } } - gc.dispose(); tabW += 3; - if (folder.showMax) tabW += CTabFolder.BUTTON_SIZE; - if (folder.showMin) tabW += CTabFolder.BUTTON_SIZE; - if (folder.single) tabW += 3*CTabFolder.BUTTON_SIZE/2; //chevron + + if (folder.showMax) tabW += renderer.computeSize(CTabFolderRenderer.PART_MAX_BUTTON, SWT.NONE, gc).x; + if (folder.showMin) tabW += renderer.computeSize(CTabFolderRenderer.PART_MIN_BUTTON, SWT.NONE, gc).x; + if (folder.single) tabW += renderer.computeSize(CTabFolderRenderer.PART_CHEVRON_BUTTON, SWT.NONE, gc).x; if (folder.topRight != null) { Point pt = folder.topRight.computeSize(SWT.DEFAULT, folder.tabHeight, flushCache); tabW += 3 + pt.x; } - if (!folder.single && !folder.simple) tabW += folder.curveWidth - 2*folder.curveIndent; + + gc.dispose(); int controlW = 0; int controlH = 0; diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderRenderer.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderRenderer.java new file mode 100644 index 0000000000..61d54b0600 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderRenderer.java @@ -0,0 +1,1618 @@ +/******************************************************************************* + * Copyright (c) 2000, 2010 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 + *******************************************************************************/ +package org.eclipse.swt.custom; + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.widgets.*; + +/*public*/ class CTabFolderRenderer { + + protected CTabFolder parent; + + // borders and shapes + int highlight_margin = 0; + int highlight_header = 0; + + int[] curve; + int[] topCurveHighlightStart; + int[] topCurveHighlightEnd; + int curveWidth = 0; + int curveIndent = 0; + int lastTabHeight = -1; + + Color fillColor; + /* Selected item appearance */ + Color selectionHighlightGradientBegin = null; //null == 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 + /* Colors for anti-aliasing */ + Color selectedOuterColor = null; + Color selectedInnerColor = null; + Color tabAreaColor = null; + /* + * Border color that was used in computing the cached anti-alias Colors. + * We have to recompute the colors if the border color changes + */ + Color lastBorderColor = null; + + //TOP_LEFT_CORNER_HILITE is laid out in reverse (ie. top to bottom) + //so can fade in same direction as right swoop curve + static final int[] TOP_LEFT_CORNER_HILITE = new int[] {5,2, 4,2, 3,3, 2,4, 2,5, 1,6}; + + static final int[] TOP_LEFT_CORNER = new int[] {0,6, 1,5, 1,4, 4,1, 5,1, 6,0}; + static final int[] TOP_RIGHT_CORNER = new int[] {-6,0, -5,1, -4,1, -1,4, -1,5, 0,6}; + static final int[] BOTTOM_LEFT_CORNER = new int[] {0,-6, 1,-5, 1,-4, 4,-1, 5,-1, 6,0}; + static final int[] BOTTOM_RIGHT_CORNER = new int[] {-6,0, -5,-1, -4,-1, -1,-4, -1,-5, 0,-6}; + + static final int[] SIMPLE_TOP_LEFT_CORNER = new int[] {0,2, 1,1, 2,0}; + static final int[] SIMPLE_TOP_RIGHT_CORNER = new int[] {-2,0, -1,1, 0,2}; + static final int[] SIMPLE_BOTTOM_LEFT_CORNER = new int[] {0,-2, 1,-1, 2,0}; + static final int[] SIMPLE_BOTTOM_RIGHT_CORNER = new int[] {-2,0, -1,-1, 0,-2}; + static final int[] SIMPLE_UNSELECTED_INNER_CORNER = new int[] {0,0}; + + static final int[] TOP_LEFT_CORNER_BORDERLESS = new int[] {0,6, 1,5, 1,4, 4,1, 5,1, 6,0}; + static final int[] TOP_RIGHT_CORNER_BORDERLESS = new int[] {-7,0, -6,1, -5,1, -2,4, -2,5, -1,6}; + static final int[] BOTTOM_LEFT_CORNER_BORDERLESS = new int[] {0,-6, 1,-6, 1,-5, 2,-4, 4,-2, 5,-1, 6,-1, 6,0}; + static final int[] BOTTOM_RIGHT_CORNER_BORDERLESS = new int[] {-7,0, -7,-1, -6,-1, -5,-2, -3,-4, -2,-5, -2,-6, -1,-6}; + + static final int[] SIMPLE_TOP_LEFT_CORNER_BORDERLESS = new int[] {0,2, 1,1, 2,0}; + static final int[] SIMPLE_TOP_RIGHT_CORNER_BORDERLESS= new int[] {-3,0, -2,1, -1,2}; + static final int[] SIMPLE_BOTTOM_LEFT_CORNER_BORDERLESS = new int[] {0,-3, 1,-2, 2,-1, 3,0}; + static final int[] SIMPLE_BOTTOM_RIGHT_CORNER_BORDERLESS = new int[] {-4,0, -3,-1, -2,-2, -1,-3}; + + static final RGB CLOSE_FILL = new RGB(252, 160, 160); + + static final int BUTTON_SIZE = 18; + + static final int BUTTON_BORDER = SWT.COLOR_WIDGET_DARK_SHADOW; + static final int BUTTON_FILL = SWT.COLOR_LIST_BACKGROUND; + static final int BORDER1_COLOR = SWT.COLOR_WIDGET_NORMAL_SHADOW; + + static final int ITEM_TOP_MARGIN = 2; + static final int ITEM_BOTTOM_MARGIN = 2; + static final int ITEM_LEFT_MARGIN = 4; + static final int ITEM_RIGHT_MARGIN = 4; + static final int INTERNAL_SPACING = 4; + static final int FLAGS = SWT.DRAW_TRANSPARENT | SWT.DRAW_MNEMONIC; + static final String ELLIPSIS = "..."; //$NON-NLS-1$ + + //Part constants + public static final int PART_BODY = -1; + public static final int PART_HEADER = -2; + public static final int PART_BORDER = -3; + public static final int PART_MAX_BUTTON = -4; + public static final int PART_MIN_BUTTON = -5; + public static final int PART_CHEVRON_BUTTON = -6; + public static final int PART_CLOSE_BUTTON = -7; + + public static final int MINIMUM_SIZE = 1 << 24; //TODO: Should this be a state? + + protected CTabFolderRenderer(CTabFolder parent) { + this.parent = parent; + int style = parent.getStyle(); + highlight_header = (style & SWT.FLAT) != 0 ? 1 : 3; + highlight_margin = (style & SWT.FLAT) != 0 ? 0 : 2; + } + + void antialias (int[] shape, Color innerColor, Color outerColor, 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 (parent.simple) return; + String platform = SWT.getPlatform(); + if ("cocoa".equals(platform)) return; //$NON-NLS-1$ + if ("carbon".equals(platform)) return; //$NON-NLS-1$ + if ("wpf".equals(platform)) return; //$NON-NLS-1$ + // Don't perform anti-aliasing on low resolution displays + if (parent.getDisplay().getDepth() < 15) return; + if (outerColor != null) { + int index = 0; + boolean left = true; + int oldY = parent.onBottom ? 0 : parent.getSize().y; + int[] outer = new int[shape.length]; + for (int i = 0; i < shape.length/2; i++) { + if (left && (index + 3 < shape.length)) { + left = parent.onBottom ? oldY <= shape[index+3] : oldY >= shape[index+3]; + oldY = shape[index+1]; + } + outer[index] = shape[index++] + (left ? -1 : +1); + outer[index] = shape[index++]; + } + gc.setForeground(outerColor); + gc.drawPolyline(outer); + } + if (innerColor != null) { + int[] inner = new int[shape.length]; + int index = 0; + boolean left = true; + int oldY = parent.onBottom ? 0 : parent.getSize().y; + for (int i = 0; i < shape.length/2; i++) { + if (left && (index + 3 < shape.length)) { + left = parent.onBottom ? oldY <= shape[index+3] : oldY >= shape[index+3]; + oldY = shape[index+1]; + } + inner[index] = shape[index++] + (left ? +1 : -1); + inner[index] = shape[index++]; + } + gc.setForeground(innerColor); + gc.drawPolyline(inner); + } + } + + protected Point computeSize (int part, int state, GC gc) { + int width = 0, height = 0; + switch (part) { + case PART_HEADER: + if (parent.fixedTabHeight != SWT.DEFAULT) { + height = parent.fixedTabHeight == 0 ? 0 : parent.fixedTabHeight + 1; // +1 for line drawn across top of tab + } else { + CTabItem[] items = parent.items; + if (items.length == 0) { + height = gc.textExtent("Default", FLAGS).y + ITEM_TOP_MARGIN + ITEM_BOTTOM_MARGIN; //$NON-NLS-1$ + } else { + for (int i=0; i < items.length; i++) { + height = Math.max(height, computeSize(i, SWT.NONE, gc).y); + } + } + gc.dispose(); + } + break; + case PART_MAX_BUTTON: + case PART_MIN_BUTTON: + case PART_CLOSE_BUTTON: + width = height = BUTTON_SIZE; + break; + case PART_CHEVRON_BUTTON: + width = 3*BUTTON_SIZE/2; + height = BUTTON_SIZE; + break; + default: + if (0 <= part && part < parent.getItemCount()) { + updateCurves(); + CTabItem item = parent.items[part]; + if (item.isDisposed()) return new Point(0,0); + Image image = item.getImage(); + if (image != null) { + Rectangle bounds = image.getBounds(); + if ((state & SWT.SELECTED) != 0 || parent.showUnselectedImage) { + width += bounds.width; + } + height = bounds.height; + } + String text = null; + if ((state & MINIMUM_SIZE) != 0) { + int minChars = parent.minChars; + text = minChars == 0 ? null : item.getText(); + if (text != null && text.length() > minChars) { + if (useEllipses()) { + int end = minChars < ELLIPSIS.length() + 1 ? minChars : minChars - ELLIPSIS.length(); + text = text.substring(0, end); + if (minChars > ELLIPSIS.length() + 1) text += ELLIPSIS; + } else { + int end = minChars; + text = text.substring(0, end); + } + } + } else { + text = item.getText(); + } + if (text != null) { + if (width > 0) width += INTERNAL_SPACING; + if (item.font == null) { + Point size = gc.textExtent(text, FLAGS); + width += size.x; + height = Math.max(height, size.y); + } else { + Font gcFont = gc.getFont(); + gc.setFont(item.font); + Point size = gc.textExtent(text, FLAGS); + width += size.x; + height = Math.max(height, size.y); + gc.setFont(gcFont); + } + } + if (parent.showClose || item.showClose) { + if ((state & SWT.SELECTED) != 0 || parent.showUnselectedClose) { + if (width > 0) width += INTERNAL_SPACING; + width += computeSize(PART_CLOSE_BUTTON, SWT.NONE, gc).x; + } + } + } + break; + } + Rectangle trim = computeTrim(part, state, 0, 0, width, height); + width = trim.width; + height = trim.height; + return new Point(width, height); + } + + protected Rectangle computeTrim (int part, int state, int x, int y, int width, int height) { + int borderLeft = parent.borderVisible ? 1 : 0; + int borderRight = borderLeft; + int borderTop = parent.onBottom ? borderLeft : 0; + int borderBottom = parent.onBottom ? 0 : borderLeft; + int tabHeight = parent.tabHeight; + switch (part) { + case PART_BODY: + int highlight_header = this.highlight_header; + int style = parent.getStyle(); + if (parent.fixedTabHeight == 0 && (style & SWT.FLAT) != 0 && (style & SWT.BORDER) == 0) { + highlight_header = 0; + } + int marginWidth = parent.marginWidth; + int marginHeight = parent.marginHeight; + x = x - marginWidth - highlight_margin - borderLeft; + width = width + borderLeft + borderRight + 2*marginWidth + 2*highlight_margin; + if (parent.minimized) { + y = parent.onBottom ? y - borderTop : y - highlight_header - tabHeight - borderTop; + height = borderTop + borderBottom + tabHeight + highlight_header; + } else { + y = parent.onBottom ? y - marginHeight - highlight_margin - borderTop: y - marginHeight - highlight_header - tabHeight - borderTop; + height = height + borderTop + borderBottom + 2*marginHeight + tabHeight + highlight_header + highlight_margin; + } + break; + case PART_HEADER: + x = x - borderLeft; + width = width + borderLeft + borderRight; + break; + case PART_BORDER: + x = x - borderLeft; + width = width + borderLeft + borderRight; + y = y - borderTop; + height = height + borderTop + borderBottom; + break; + default: + if (0 <= part && part < parent.getItemCount()) { + updateCurves(); + x = x - ITEM_LEFT_MARGIN; + width = width + ITEM_LEFT_MARGIN + ITEM_RIGHT_MARGIN; + if (!parent.simple && !parent.single && (state & SWT.SELECTED) != 0) { + width += curveWidth - curveIndent; + } + y = y - ITEM_TOP_MARGIN; + height = height + ITEM_TOP_MARGIN + ITEM_BOTTOM_MARGIN; + } + break; + } + return new Rectangle(x, y, width, height); + } + + void createAntialiasColors() { + disposeAntialiasColors(); + lastBorderColor = parent.getDisplay().getSystemColor(BORDER1_COLOR); + RGB lineRGB = lastBorderColor.getRGB(); + /* compute the selected color */ + RGB innerRGB = parent.selectionBackground.getRGB(); + if (parent.selectionBgImage != null || + (parent.selectionGradientColors != null && parent.selectionGradientColors.length > 1)) { + innerRGB = null; + } + RGB outerRGB = parent.getBackground().getRGB(); + if (parent.gradientColors != null && parent.gradientColors.length > 1) { + outerRGB = null; + } + if (outerRGB != null) { + 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; + selectedOuterColor = new Color(parent.getDisplay(), red, green, blue); + } + if (innerRGB != null) { + 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; + selectedInnerColor = new Color(parent.getDisplay(), red, green, blue); + } + /* compute the tabArea color */ + outerRGB = parent.getParent().getBackground().getRGB(); + if (outerRGB != null) { + 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; + tabAreaColor = new Color(parent.getDisplay(), red, green, blue); + } + } + + /* + * 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 == 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 = parent.tabHeight; + + RGB from = start.getRGB(); + RGB to = parent.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(parent.getDisplay(), red, green, blue); + } + } + + protected void dispose() { + disposeAntialiasColors(); + disposeSelectionHighlightGradientColors(); + if (fillColor != null) { + fillColor.dispose(); + fillColor = null; + } + } + + void disposeAntialiasColors() { + if (tabAreaColor != null) tabAreaColor.dispose(); + if (selectedInnerColor != null) selectedInnerColor.dispose(); + if (selectedOuterColor != null) selectedOuterColor.dispose(); + tabAreaColor = selectedInnerColor = selectedOuterColor = null; + } + + void disposeSelectionHighlightGradientColors() { + if(selectionHighlightGradientColorsCache == null) + return; + for (int i = 0; i < selectionHighlightGradientColorsCache.length; i++) { + selectionHighlightGradientColorsCache[i].dispose(); + } + selectionHighlightGradientColorsCache = null; + } + + protected void draw (int part, int state, Rectangle bounds, GC gc) { + switch (part) { + case PART_BODY: + drawBody(gc, bounds, state); + break; + case PART_HEADER: + drawTabArea(gc, bounds, state); + break; + case PART_MAX_BUTTON: + drawMaximize(gc, bounds, state); + break; + case PART_MIN_BUTTON: + drawMinimize(gc, bounds, state); + break; + case PART_CHEVRON_BUTTON: + drawChevron(gc, bounds, state); + break; + default: + if (0 <= part && part < parent.getItemCount()) { + if (bounds.width == 0 || bounds.height == 0) return; + if ((state & SWT.SELECTED) != 0 ) { + drawSelected(part, gc, bounds, state); + } else { + drawUnselected(part, gc, bounds, state); + } + } + break; + } + } + + void drawBackground(GC gc, int[] shape, boolean selected) { + Color defaultBackground = selected ? parent.selectionBackground : parent.getBackground(); + Image image = selected ? parent.selectionBgImage : null; + Color[] colors = selected ? parent.selectionGradientColors : parent.gradientColors; + int[] percents = selected ? parent.selectionGradientPercents : parent.gradientPercents; + boolean vertical = selected ? parent.selectionGradientVertical : parent.gradientVertical; + Point size = parent.getSize(); + int width = size.x; + int height = parent.tabHeight + highlight_header; + int x = 0; + + int borderLeft = parent.borderVisible ? 1 : 0; + int borderTop = parent.onBottom ? borderLeft : 0; + int borderBottom = parent.onBottom ? 0 : borderLeft; + + if (borderLeft > 0) { + x += 1; width -= 2; + } + int y = parent.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, boolean vertical) { + Region clipping = new Region(); + gc.getClipping(clipping); + Region region = new Region(); + region.add(shape); + region.intersect(clipping); + gc.setClipping(region); + + if (image != 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 != null) { + // draw gradient + if (colors.length == 1) { + Color background = colors[0] != null ? colors[0] : defaultBackground; + gc.setBackground(background); + gc.fillRectangle(x, y, width, height); + } else { + if (vertical) { + if (parent.onBottom) { + int pos = 0; + if (percents[percents.length - 1] < 100) { + pos = (100 - percents[percents.length - 1]) * height / 100; + gc.setBackground(defaultBackground); + gc.fillRectangle(x, y, width, pos); + } + Color lastColor = colors[colors.length-1]; + if (lastColor == null) lastColor = defaultBackground; + for (int i = percents.length-1; i >= 0; i--) { + gc.setForeground(lastColor); + lastColor = colors[i]; + if (lastColor == null) lastColor = defaultBackground; + gc.setBackground(lastColor); + int percentage = i > 0 ? percents[i] - percents[i-1] : percents[i]; + int gradientHeight = percentage * height / 100; + gc.fillGradientRectangle(x, y+pos, width, gradientHeight, true); + pos += gradientHeight; + } + } else { + Color lastColor = colors[0]; + if (lastColor == null) lastColor = defaultBackground; + int pos = 0; + for (int i = 0; i < percents.length; i++) { + gc.setForeground(lastColor); + lastColor = colors[i + 1]; + if (lastColor == null) lastColor = defaultBackground; + gc.setBackground(lastColor); + int percentage = i > 0 ? percents[i] - percents[i-1] : percents[i]; + int gradientHeight = percentage * 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 = parent.getSize().y; + Color lastColor = colors[0]; + if (lastColor == null) lastColor = defaultBackground; + int pos = 0; + for (int i = 0; i < percents.length; ++i) { + gc.setForeground(lastColor); + lastColor = colors[i + 1]; + if (lastColor == 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 ((parent.getStyle() & SWT.NO_BACKGROUND) != 0 || !defaultBackground.equals(parent.getBackground())) { + gc.setBackground(defaultBackground); + gc.fillRectangle(x, y, width, height); + } + } + gc.setClipping(clipping); + clipping.dispose(); + region.dispose(); + } + + /* + * Draw the border of the tab + * + * @param gc + * @param shape + */ + void drawBorder(GC gc, int[] shape) { + + gc.setForeground(parent.getDisplay().getSystemColor(BORDER1_COLOR)); + gc.drawPolyline(shape); + } + + void drawBody(GC gc, Rectangle bounds, int state) { + Point size = new Point(bounds.width, bounds.height); + int selectedIndex = parent.selectedIndex; + int tabHeight = parent.tabHeight; + + int borderLeft = parent.borderVisible ? 1 : 0; + int borderRight = borderLeft; + int borderTop = parent.onBottom ? borderLeft : 0; + int borderBottom = parent.onBottom ? 0 : borderLeft; + + // fill in body + if (!parent.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 (parent.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 != -1 && parent.selectionGradientColors != null && parent.selectionGradientColors.length > 1 && !parent.selectionGradientVertical) { + drawBackground(gc, shape, true); + } else if (selectedIndex == -1 && parent.gradientColors != null && parent.gradientColors.length > 1 && !parent.gradientVertical) { + drawBackground(gc, shape, false); + } else { + gc.setBackground(selectedIndex == -1 ? parent.getBackground() : parent.selectionBackground); + gc.fillPolygon(shape); + } + } + //Draw client area + if ((parent.getStyle() & SWT.NO_BACKGROUND) != 0) { + gc.setBackground(parent.getBackground()); + int marginWidth = parent.marginWidth; + int marginHeight = parent.marginHeight; + int xClient = borderLeft + marginWidth + highlight_margin, yClient; + if (parent.onBottom) { + yClient = borderTop + highlight_margin + marginHeight; + } else { + yClient = borderTop + tabHeight + highlight_header + marginHeight; + } + gc.fillRectangle(xClient - marginWidth, yClient - marginHeight, width, height); + } + } else { + if ((parent.getStyle() & SWT.NO_BACKGROUND) != 0) { + int height = borderTop + tabHeight + highlight_header + borderBottom; + if (size.y > height) { + gc.setBackground(parent.getParent().getBackground()); + gc.fillRectangle(0, height, size.x, size.y - height); + } + } + } + + //draw 1 pixel border around outside + if (borderLeft > 0) { + gc.setForeground(parent.getDisplay().getSystemColor(BORDER1_COLOR)); + int x1 = borderLeft - 1; + int x2 = size.x - borderRight; + int y1 = parent.onBottom ? borderTop - 1 : borderTop + tabHeight; + int y2 = parent.onBottom ? size.y - tabHeight - borderBottom - 1 : size.y - borderBottom; + gc.drawLine(x1, y1, x1, y2); // left + gc.drawLine(x2, y1, x2, y2); // right + if (parent.onBottom) { + gc.drawLine(x1, y1, x2, y1); // top + } else { + gc.drawLine(x1, y2, x2, y2); // bottom + } + } + } + + void drawClose(GC gc, Rectangle closeRect, int closeImageState) { + if (closeRect.width == 0 || closeRect.height == 0) return; + Display display = parent.getDisplay(); + + // draw X 9x9 + int x = closeRect.x + Math.max(1, (closeRect.width-9)/2); + int y = closeRect.y + Math.max(1, (closeRect.height-9)/2); + y += parent.onBottom ? -1 : 1; + + Color closeBorder = display.getSystemColor(BUTTON_BORDER); + switch (closeImageState & (SWT.HOT | SWT.SELECTED | SWT.BACKGROUND)) { + case SWT.NONE: { + int[] shape = new int[] {x,y, x+2,y, x+4,y+2, x+5,y+2, x+7,y, x+9,y, + x+9,y+2, x+7,y+4, x+7,y+5, x+9,y+7, x+9,y+9, + x+7,y+9, x+5,y+7, x+4,y+7, x+2,y+9, x,y+9, + x,y+7, x+2,y+5, x+2,y+4, x,y+2}; + gc.setBackground(display.getSystemColor(BUTTON_FILL)); + gc.fillPolygon(shape); + gc.setForeground(closeBorder); + gc.drawPolygon(shape); + break; + } + case SWT.HOT: { + int[] shape = new int[] {x,y, x+2,y, x+4,y+2, x+5,y+2, x+7,y, x+9,y, + x+9,y+2, x+7,y+4, x+7,y+5, x+9,y+7, x+9,y+9, + x+7,y+9, x+5,y+7, x+4,y+7, x+2,y+9, x,y+9, + x,y+7, x+2,y+5, x+2,y+4, x,y+2}; + gc.setBackground(getFillColor()); + gc.fillPolygon(shape); + gc.setForeground(closeBorder); + gc.drawPolygon(shape); + break; + } + case SWT.SELECTED: { + int[] shape = new int[] {x+1,y+1, x+3,y+1, x+5,y+3, x+6,y+3, x+8,y+1, x+10,y+1, + x+10,y+3, x+8,y+5, x+8,y+6, x+10,y+8, x+10,y+10, + x+8,y+10, x+6,y+8, x+5,y+8, x+3,y+10, x+1,y+10, + x+1,y+8, x+3,y+6, x+3,y+5, x+1,y+3}; + gc.setBackground(getFillColor()); + gc.fillPolygon(shape); + gc.setForeground(closeBorder); + gc.drawPolygon(shape); + break; + } + case SWT.BACKGROUND: { + int[] shape = new int[] {x,y, x+10,y, x+10,y+10, x,y+10}; + if (parent.gradientColors != null && !parent.gradientVertical) { + drawBackground(gc, shape, false); + } else { + Color defaultBackground = parent.getBackground(); + Color[] colors = parent.gradientColors; + int[] percents = parent.gradientPercents; + boolean vertical = parent.gradientVertical; + drawBackground(gc, shape, x, y, 10, 10, defaultBackground, null, colors, percents, vertical); + } + break; + } + } + } + + void drawChevron(GC gc, Rectangle chevronRect, int chevronImageState) { + if (chevronRect.width == 0 || chevronRect.height == 0) return; + int selectedIndex = parent.selectedIndex; + // draw chevron (10x7) + Display display = parent.getDisplay(); + Point dpi = display.getDPI(); + int fontHeight = 72 * 10 / dpi.y; + FontData fd = parent.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; + int itemCount = parent.getItemCount(); + if (parent.single) { + count = selectedIndex == -1 ? itemCount : itemCount - 1; + } else { + int showCount = 0; + while (showCount < parent.priority.length && parent.items[parent.priority[showCount]].showing) { + showCount++; + } + count = itemCount - showCount; + } + String chevronString = count > 99 ? "99+" : String.valueOf(count); //$NON-NLS-1$ + switch (chevronImageState & (SWT.HOT | SWT.SELECTED)) { + case SWT.NONE: { + Color chevronBorder = parent.single ? parent.getSelectionForeground() : parent.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 SWT.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 SWT.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(); + } + + /* + * Draw a highlight effect along the left, top, and right edges of the tab. + * Only for curved tabs, on top. + * Do not draw if insufficient colors. + */ + void drawHighlight(GC gc, Rectangle bounds, int state, int rightEdge) { + //only draw for curvy tabs and only draw for top tabs + if(parent.simple || parent.onBottom) + return; + + if(selectionHighlightGradientBegin == null) + return; + + Color[] gradients = selectionHighlightGradientColorsCache; + if(gradients == null) + return; + int gradientsSize = gradients.length; + if(gradientsSize == 0) + return; //shouldn't happen but just to be tidy + + int x = bounds.x; + int y = bounds.y; + + gc.setForeground(gradients[0]); + + //draw top horizontal line + gc.drawLine( + TOP_LEFT_CORNER_HILITE[0] + x + 1, //rely on fact that first pair is top/right of curve + 1 + y, + rightEdge - curveIndent, + 1 + y); + + int[] leftHighlightCurve = TOP_LEFT_CORNER_HILITE; + + int d = parent.tabHeight - topCurveHighlightEnd.length /2; + + int lastX = 0; + int lastY = 0; + int lastColorIndex = 0; + + //draw upper left curve highlight + for (int i = 0; i < leftHighlightCurve.length /2; i++) { + int rawX = leftHighlightCurve[i * 2]; + int rawY = leftHighlightCurve[i * 2 + 1]; + lastX = rawX + x; + lastY = rawY + y; + lastColorIndex = rawY - 1; + gc.setForeground(gradients[lastColorIndex]); + gc.drawPoint(lastX, lastY); + } + //draw left vertical line highlight + for(int i = lastColorIndex; i < gradientsSize; i++) { + gc.setForeground(gradients[i]); + gc.drawPoint(lastX, 1 + lastY++); + } + + int rightEdgeOffset = rightEdge - curveIndent; + + //draw right swoop highlight up to diagonal portion + for (int i = 0; i < topCurveHighlightStart.length /2; i++) { + int rawX = topCurveHighlightStart[i * 2]; + int rawY = topCurveHighlightStart[i * 2 + 1]; + lastX = rawX + rightEdgeOffset; + lastY = rawY + y; + lastColorIndex = rawY - 1; + if(lastColorIndex >= gradientsSize) + break; //can happen if tabs are unusually short and cut off the curve + gc.setForeground(gradients[lastColorIndex]); + gc.drawPoint(lastX, lastY); + } + //draw right diagonal line highlight + for(int i = lastColorIndex; i < lastColorIndex + d; i++) { + if(i >= gradientsSize) + break; //can happen if tabs are unusually short and cut off the curve + gc.setForeground(gradients[i]); + gc.drawPoint(1 + lastX++, 1 + lastY++); + } + + //draw right swoop highlight from diagonal portion to end + for (int i = 0; i < topCurveHighlightEnd.length /2; i++) { + int rawX = topCurveHighlightEnd[i * 2]; //d is already encoded in this value + int rawY = topCurveHighlightEnd[i * 2 + 1]; //d already encoded + lastX = rawX + rightEdgeOffset; + lastY = rawY + y; + lastColorIndex = rawY - 1; + if(lastColorIndex >= gradientsSize) + break; //can happen if tabs are unusually short and cut off the curve + gc.setForeground(gradients[lastColorIndex]); + gc.drawPoint(lastX, lastY); + } + } + + /* + * Draw the unselected border for the receiver on the left. + * + * @param gc + */ + void drawLeftUnselectedBorder(GC gc, Rectangle bounds, int state) { + int x = bounds.x; + int y = bounds.y; + int height = bounds.height; + + int[] shape = null; + if (parent.onBottom) { + int[] left = parent.simple + ? SIMPLE_UNSELECTED_INNER_CORNER + : BOTTOM_LEFT_CORNER; + + shape = new int[left.length + 2]; + int index = 0; + shape[index++] = x; + shape[index++] = y - 1; + for (int i = 0; i < left.length / 2; i++) { + shape[index++] = x + left[2 * i]; + shape[index++] = y + height + left[2 * i + 1] - 1; + } + } else { + int[] left = parent.simple + ? SIMPLE_UNSELECTED_INNER_CORNER + : TOP_LEFT_CORNER; + + shape = new int[left.length + 2]; + int index = 0; + shape[index++] = x; + shape[index++] = y + height; + for (int i = 0; i < left.length / 2; i++) { + shape[index++] = x + left[2 * i]; + shape[index++] = y + left[2 * i + 1]; + } + + } + + drawBorder(gc, shape); + } + + void drawMaximize(GC gc, Rectangle maxRect, int maxImageState) { + if (maxRect.width == 0 || maxRect.height == 0) return; + Display display = parent.getDisplay(); + // 5x4 or 7x9 + int x = maxRect.x + (maxRect.width - 10)/2; + int y = maxRect.y + 3; + + gc.setForeground(display.getSystemColor(BUTTON_BORDER)); + gc.setBackground(display.getSystemColor(BUTTON_FILL)); + + switch (maxImageState & (SWT.HOT | SWT.SELECTED)) { + case SWT.NONE: { + if (!parent.getMaximized()) { + 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 SWT.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 (!parent.getMaximized()) { + 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 SWT.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 (!parent.getMaximized()) { + 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, Rectangle minRect, int minImageState) { + if (minRect.width == 0 || minRect.height == 0) return; + Display display = parent.getDisplay(); + // 5x4 or 9x3 + int x = minRect.x + (minRect.width - 10)/2; + int y = minRect.y + 3; + + gc.setForeground(display.getSystemColor(BUTTON_BORDER)); + gc.setBackground(display.getSystemColor(BUTTON_FILL)); + + switch (minImageState & (SWT.HOT | SWT.SELECTED)) { + case SWT.NONE: { + if (!parent.getMinimized()) { + 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 SWT.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 (!parent.getMinimized()) { + 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 SWT.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 (!parent.getMinimized()) { + 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; + } + } + } + + /* + * Draw the unselected border for the receiver on the right. + * + * @param gc + */ + void drawRightUnselectedBorder(GC gc, Rectangle bounds, int state) { + int x = bounds.x; + int y = bounds.y; + int width = bounds.width; + int height = bounds.height; + + int[] shape = null; + int startX = x + width - 1; + + if (parent.onBottom) { + int[] right = parent.simple + ? SIMPLE_UNSELECTED_INNER_CORNER + : BOTTOM_RIGHT_CORNER; + + shape = new int[right.length + 2]; + int index = 0; + + for (int i = 0; i < right.length / 2; i++) { + shape[index++] = startX + right[2 * i]; + shape[index++] = y + height + right[2 * i + 1] - 1; + } + shape[index++] = startX; + shape[index++] = y - 1; + } else { + int[] right = parent.simple + ? SIMPLE_UNSELECTED_INNER_CORNER + : TOP_RIGHT_CORNER; + + shape = new int[right.length + 2]; + int index = 0; + + for (int i = 0; i < right.length / 2; i++) { + shape[index++] = startX + right[2 * i]; + shape[index++] = y + right[2 * i + 1]; + } + + shape[index++] = startX; + shape[index++] = y + height; + + } + + drawBorder(gc, shape); + + } + + void drawSelected(int itemIndex, GC gc, Rectangle bounds, int state ) { + CTabItem item = parent.items[itemIndex]; + int x = bounds.x; + int y = bounds.y; + int height = bounds.height; + int width = bounds.width; + if (!parent.simple && !parent.single) width -= (curveWidth - curveIndent); + int borderLeft = parent.borderVisible ? 1 : 0; + int borderRight = borderLeft; + int borderTop = parent.onBottom ? borderLeft : 0; + int borderBottom = parent.onBottom ? 0 : borderLeft; + + Point size = parent.getSize(); + + int rightEdge = Math.min (x + width, parent.getRightItemEdge(gc)); + // Draw selection border across all tabs + + if ((state & SWT.BACKGROUND) != 0) { + int xx = borderLeft; + int yy = parent.onBottom ? size.y - borderBottom - parent.tabHeight - highlight_header : borderTop + parent.tabHeight + 1; + int ww = size.x - borderLeft - borderRight; + int hh = highlight_header - 1; + int[] shape = new int[] {xx,yy, xx+ww,yy, xx+ww,yy+hh, xx,yy+hh}; + if (parent.selectionGradientColors != null && !parent.selectionGradientVertical) { + drawBackground(gc, shape, true); + } else { + gc.setBackground(parent.selectionBackground); + gc.fillRectangle(xx, yy, ww, hh); + } + + if (parent.single) { + if (!item.showing) return; + } else { + // if selected tab scrolled out of view or partially out of view + // just draw bottom line + if (!item.showing){ + int x1 = Math.max(0, borderLeft - 1); + int y1 = (parent.onBottom) ? y - 1 : y + height; + int x2 = size.x - borderRight; + gc.setForeground(parent.getDisplay().getSystemColor(BORDER1_COLOR)); + gc.drawLine(x1, y1, x2, y1); + return; + } + + // draw selected tab background and outline + shape = null; + if (parent.onBottom) { + int[] left = parent.simple ? SIMPLE_BOTTOM_LEFT_CORNER : BOTTOM_LEFT_CORNER; + int[] right = parent.simple ? SIMPLE_BOTTOM_RIGHT_CORNER : curve; + if (borderLeft == 0 && itemIndex == parent.firstIndex) { + left = new int[]{x, y+height}; + } + shape = new int[left.length+right.length+8]; + int index = 0; + shape[index++] = x; // first point repeated here because below we reuse shape to draw outline + shape[index++] = y - 1; + shape[index++] = x; + shape[index++] = y - 1; + for (int i = 0; i < left.length/2; i++) { + shape[index++] = x + left[2*i]; + shape[index++] = y + height + left[2*i+1] - 1; + } + for (int i = 0; i < right.length/2; i++) { + shape[index++] = parent.simple ? rightEdge - 1 + right[2*i] : rightEdge - curveIndent + right[2*i]; + shape[index++] = parent.simple ? y + height + right[2*i+1] - 1 : y + right[2*i+1] - 2; + } + shape[index++] = parent.simple ? rightEdge - 1 : rightEdge + curveWidth - curveIndent; + shape[index++] = y - 1; + shape[index++] = parent.simple ? rightEdge - 1 : rightEdge + curveWidth - curveIndent; + shape[index++] = y - 1; + } else { + int[] left = parent.simple ? SIMPLE_TOP_LEFT_CORNER : TOP_LEFT_CORNER; + int[] right = parent.simple ? SIMPLE_TOP_RIGHT_CORNER : curve; + if (borderLeft == 0 && itemIndex == parent.firstIndex) { + left = new int[]{x, y}; + } + shape = new int[left.length+right.length+8]; + int index = 0; + shape[index++] = x; // first point repeated here because below we reuse shape to draw outline + shape[index++] = y + height + 1; + shape[index++] = x; + shape[index++] = y + height + 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++] = parent.simple ? rightEdge - 1 + right[2*i] : rightEdge - curveIndent + right[2*i]; + shape[index++] = y + right[2*i+1]; + } + shape[index++] = parent.simple ? rightEdge - 1 : rightEdge + curveWidth - curveIndent; + shape[index++] = y + height + 1; + shape[index++] = parent.simple ? rightEdge - 1 : rightEdge + curveWidth - curveIndent; + shape[index++] = y + height + 1; + } + + Rectangle clipping = gc.getClipping(); + Rectangle clipBounds = item.getBounds(); + clipBounds.height += 1; + if (parent.onBottom) clipBounds.y -= 1; + boolean tabInPaint = clipping.intersects(clipBounds); + + if (tabInPaint) { + // fill in tab background + if (parent.selectionGradientColors != null && !parent.selectionGradientVertical) { + drawBackground(gc, shape, true); + } else { + Color defaultBackground = parent.selectionBackground; + Image image = parent.selectionBgImage; + Color[] colors = parent.selectionGradientColors; + int[] percents = parent.selectionGradientPercents; + boolean vertical = parent.selectionGradientVertical; + xx = x; + yy = parent.onBottom ? y -1 : y + 1; + ww = width; + hh = height; + if (!parent.single && !parent.simple) ww += curveWidth - curveIndent; + drawBackground(gc, shape, xx, yy, ww, hh, defaultBackground, image, colors, percents, vertical); + } + } + + //Highlight MUST be drawn before the outline so that outline can cover it in the right spots (start of swoop) + //otherwise the curve looks jagged + drawHighlight(gc, bounds, state, rightEdge); + + // draw outline + shape[0] = Math.max(0, borderLeft - 1); + if (borderLeft == 0 && itemIndex == parent.firstIndex) { + shape[1] = parent.onBottom ? y + height - 1 : y; + shape[5] = shape[3] = shape[1]; + } + shape[shape.length - 2] = size.x - borderRight + 1; + for (int i = 0; i < shape.length/2; i++) { + if (shape[2*i + 1] == y + height + 1) shape[2*i + 1] -= 1; + } + Color borderColor = parent.getDisplay().getSystemColor(BORDER1_COLOR); + if (! borderColor.equals(lastBorderColor)) createAntialiasColors(); + antialias(shape, selectedInnerColor, selectedOuterColor, gc); + gc.setForeground(borderColor); + gc.drawPolyline(shape); + + if (!tabInPaint) return; + } + } + + if ((state & SWT.FOREGROUND) != 0) { + // draw Image + Rectangle trim = computeTrim(itemIndex, SWT.NONE, 0, 0, 0, 0); + int xDraw = x - trim.x; + if (parent.single && (parent.showClose || item.showClose)) xDraw += item.closeRect.width; + Image image = item.getImage(); + if (image != null) { + Rectangle imageBounds = image.getBounds(); + // only draw image if it won't overlap with close button + int maxImageWidth = rightEdge - xDraw - (trim.width + trim.x); + if (!parent.single && item.closeRect.width > 0) maxImageWidth -= item.closeRect.width + INTERNAL_SPACING; + if (imageBounds.width < maxImageWidth) { + int imageX = xDraw; + int imageY = y + (height - imageBounds.height) / 2; + imageY += parent.onBottom ? -1 : 1; + gc.drawImage(image, imageX, imageY); + xDraw += imageBounds.width + INTERNAL_SPACING; + } + } + + // draw Text + int textWidth = rightEdge - xDraw - (trim.width + trim.x); + if (!parent.single && item.closeRect.width > 0) textWidth -= item.closeRect.width + INTERNAL_SPACING; + if (textWidth > 0) { + Font gcFont = gc.getFont(); + gc.setFont(item.font == null ? parent.getFont() : item.font); + + if (item.shortenedText == null || item.shortenedTextWidth != textWidth) { + item.shortenedText = shortenText(gc, item.getText(), textWidth); + item.shortenedTextWidth = textWidth; + } + Point extent = gc.textExtent(item.shortenedText, FLAGS); + int textY = y + (height - extent.y) / 2; + textY += parent.onBottom ? -1 : 1; + + gc.setForeground(parent.selectionForeground); + gc.drawText(item.shortenedText, xDraw, textY, FLAGS); + gc.setFont(gcFont); + + // draw a Focus rectangle + if (parent.isFocusControl()) { + Display display = parent.getDisplay(); + if (parent.simple || parent.single) { + gc.setBackground(display.getSystemColor(SWT.COLOR_BLACK)); + gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE)); + gc.drawFocus(xDraw-1, textY-1, extent.x+2, extent.y+2); + } else { + gc.setForeground(display.getSystemColor(BUTTON_BORDER)); + gc.drawLine(xDraw, textY+extent.y+1, xDraw+extent.x+1, textY+extent.y+1); + } + } + } + if (parent.showClose || item.showClose) drawClose(gc, item.closeRect, item.closeImageState); + } + } + + void drawTabArea(GC gc, Rectangle bounds, int state) { + Point size = parent.getSize(); + int[] shape = null; + Color borderColor = parent.getDisplay().getSystemColor(BORDER1_COLOR); + int tabHeight = parent.tabHeight; + int style = parent.getStyle(); + + int borderLeft = parent.borderVisible ? 1 : 0; + int borderRight = borderLeft; + int borderTop = parent.onBottom ? borderLeft : 0; + int borderBottom = parent.onBottom ? 0 : borderLeft; + + int selectedIndex = parent.selectedIndex; + if (tabHeight == 0) { + if ((style & SWT.FLAT) != 0 && (style & SWT.BORDER) == 0) return; + int x1 = borderLeft - 1; + int x2 = size.x - borderRight; + int y1 = parent.onBottom ? size.y - borderBottom - highlight_header - 1 : borderTop + highlight_header; + int y2 = parent.onBottom ? size.y - borderBottom : borderTop; + if (borderLeft > 0 && parent.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 != -1 && parent.selectionGradientColors != null && parent.selectionGradientColors.length > 1 && !parent.selectionGradientVertical) { + drawBackground(gc, shape, true); + } else if (selectedIndex == -1 && parent.gradientColors != null && parent.gradientColors.length > 1 && !parent.gradientVertical) { + drawBackground(gc, shape, false); + } else { + gc.setBackground(selectedIndex == -1 ? parent.getBackground() : parent.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 = parent.onBottom ? size.y - borderBottom - tabHeight : borderTop; + int width = size.x - borderLeft - borderRight + 1; + int height = tabHeight - 1; + boolean simple = parent.simple; + // Draw Tab Header + if (parent.onBottom) { + int[] left, right; + if ((style & SWT.BORDER) != 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 == 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 == 0) shape[index-1] += 1; + } + shape[index++] = x+width; + shape[index++] = y-highlight_header; + } else { + int[] left, right; + if ((style & SWT.BORDER) != 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 + boolean single = parent.single; + boolean bkSelected = single && selectedIndex != -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(parent.getParent().getBackground()); + fillRegion(gc, r); + r.dispose(); + + // Draw selected tab + if (selectedIndex == -1) { + // if no selected tab - draw line across bottom of all tabs + int x1 = borderLeft; + int y1 = (parent.onBottom) ? size.y - borderBottom - tabHeight - 1 : borderTop + tabHeight; + int x2 = size.x - borderRight; + gc.setForeground(borderColor); + gc.drawLine(x1, y1, x2, y1); + } + + // Draw border line + if (borderLeft > 0) { + if (! borderColor.equals(lastBorderColor)) createAntialiasColors(); + antialias(shape, null, tabAreaColor, gc); + gc.setForeground(borderColor); + gc.drawPolyline(shape); + } + } + + void drawUnselected(int index, GC gc, Rectangle bounds, int state) { + CTabItem item = parent.items[index]; + int x = bounds.x; + int y = bounds.y; + int height = bounds.height; + int width = bounds.width; + + // Do not draw partial items + if (!item.showing) return; + + Rectangle clipping = gc.getClipping(); + if (!clipping.intersects(bounds)) return; + + if ((state & SWT.BACKGROUND) != 0) { + if (index > 0 && index < parent.selectedIndex) + drawLeftUnselectedBorder(gc, bounds, state); + // If it is the last one then draw a line + if (index > parent.selectedIndex) + drawRightUnselectedBorder(gc, bounds, state); + } + + if ((state & SWT.FOREGROUND) != 0) { + // draw Image + Rectangle trim = computeTrim(index, SWT.NONE, 0, 0, 0, 0); + int xDraw = x - trim.x; + Image image = item.getImage(); + if (image != null && parent.showUnselectedImage) { + Rectangle imageBounds = image.getBounds(); + // only draw image if it won't overlap with close button + int maxImageWidth = x + width - xDraw - (trim.width + trim.x); + if (parent.showUnselectedClose && (parent.showClose || item.showClose)) { + maxImageWidth -= item.closeRect.width + INTERNAL_SPACING; + } + if (imageBounds.width < maxImageWidth) { + int imageX = xDraw; + int imageHeight = imageBounds.height; + int imageY = y + (height - imageHeight) / 2; + imageY += parent.onBottom ? -1 : 1; + int imageWidth = imageBounds.width * imageHeight / imageBounds.height; + gc.drawImage(image, + imageBounds.x, imageBounds.y, imageBounds.width, imageBounds.height, + imageX, imageY, imageWidth, imageHeight); + xDraw += imageWidth + INTERNAL_SPACING; + } + } + // draw Text + int textWidth = x + width - xDraw - (trim.width + trim.x); + if (parent.showUnselectedClose && (parent.showClose || item.showClose)) { + textWidth -= item.closeRect.width + INTERNAL_SPACING; + } + if (textWidth > 0) { + Font gcFont = gc.getFont(); + gc.setFont(item.font == null ? parent.getFont() : item.font); + if (item.shortenedText == null || item.shortenedTextWidth != textWidth) { + item.shortenedText = shortenText(gc, item.getText(), textWidth); + item.shortenedTextWidth = textWidth; + } + Point extent = gc.textExtent(item.shortenedText, FLAGS); + int textY = y + (height - extent.y) / 2; + textY += parent.onBottom ? -1 : 1; + gc.setForeground(parent.getForeground()); + gc.drawText(item.shortenedText, xDraw, textY, FLAGS); + gc.setFont(gcFont); + } + // draw close + if (parent.showUnselectedClose && (parent.showClose || item.showClose)) drawClose(gc, item.closeRect, item.closeImageState); + } + } + + 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(); + } + + Color getFillColor() { + if (fillColor == null) { + fillColor = new Color(parent.getDisplay(), CLOSE_FILL); + } + return fillColor; + } + + /* + * Return true if given start color, the cache of highlight colors we have + * would match the highlight colors we'd compute. + */ + boolean isSelectionHighlightColorsCacheHit(Color start) { + + if(selectionHighlightGradientColorsCache == 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.equals(start)) + return false; + + //Compare number of colours we have vs. we'd compute + if(selectionHighlightGradientColorsCache.length != parent.tabHeight) + return false; + + //Compare existing highlight end to what it would be (selectionBackground) + if(! highlightEnd.equals(parent.selectionBackground)) + return false; + + return true; + } + + 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 == null) + return; + + //don't bother on low colour + if (parent.getDisplay().getDepth() < 15) + return; + + //don't bother if we don't have a background gradient + if(parent.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 + } + + String shortenText(GC gc, String text, int width) { + return useEllipses() + ? shortenText(gc, text, width, ELLIPSIS) + : shortenText(gc, text, width, ""); //$NON-NLS-1$ + } + + String shortenText(GC gc, String text, int width, String ellipses) { + if (gc.textExtent(text, FLAGS).x <= width) return text; + int ellipseWidth = gc.textExtent(ellipses, FLAGS).x; + int length = text.length(); + TextLayout layout = new TextLayout(parent.getDisplay()); + layout.setText(text); + int end = layout.getPreviousOffset(length, SWT.MOVEMENT_CLUSTER); + while (end > 0) { + text = text.substring(0, end); + int l = gc.textExtent(text, FLAGS).x; + if (l + ellipseWidth <= width) { + break; + } + end = layout.getPreviousOffset(end, SWT.MOVEMENT_CLUSTER); + } + layout.dispose(); + return end == 0 ? text.substring(0, 1) : text + ellipses; + } + + void updateCurves () { + int tabHeight = parent.tabHeight; + if (tabHeight == lastTabHeight) return; + if (parent.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 }; + } + } + + /* + * Return whether to use ellipses or just truncate labels + */ + boolean useEllipses() { + return parent.simple; + } +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabItem.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabItem.java index 392d7c6682..e5b2eee2ad 100755 --- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabItem.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabItem.java @@ -47,19 +47,10 @@ public class CTabItem extends Item { Image disabledImage; Rectangle closeRect = new Rectangle(0, 0, 0, 0); - int closeImageState = CTabFolder.NONE; + int closeImageState = SWT.BACKGROUND; boolean showClose = false; boolean showing = false; - // internal constants - static final int TOP_MARGIN = 2; - static final int BOTTOM_MARGIN = 2; - static final int LEFT_MARGIN = 4; - static final int RIGHT_MARGIN = 4; - static final int INTERNAL_SPACING = 4; - static final int FLAGS = SWT.DRAW_TRANSPARENT | SWT.DRAW_MNEMONIC; - static final String ELLIPSIS = "..."; //$NON-NLS-1$ // could use the ellipsis glyph on some platforms "\u2026" - /** * Constructs a new instance of this class given its parent * (which must be a CTabFolder) and a style value @@ -127,37 +118,6 @@ public CTabItem (CTabFolder parent, int style, int index) { parent.createItem (this, index); } -/* - * Return whether to use ellipses or just truncate labels - */ -boolean useEllipses() { - return parent.simple; -} - -String shortenText(GC gc, String text, int width) { - return useEllipses() - ? shortenText(gc, text, width, ELLIPSIS) - : shortenText(gc, text, width, ""); //$NON-NLS-1$ -} - -String shortenText(GC gc, String text, int width, String ellipses) { - if (gc.textExtent(text, FLAGS).x <= width) return text; - int ellipseWidth = gc.textExtent(ellipses, FLAGS).x; - int length = text.length(); - TextLayout layout = new TextLayout(getDisplay()); - layout.setText(text); - int end = layout.getPreviousOffset(length, SWT.MOVEMENT_CLUSTER); - while (end > 0) { - text = text.substring(0, end); - int l = gc.textExtent(text, FLAGS).x; - if (l + ellipseWidth <= width) { - break; - } - end = layout.getPreviousOffset(end, SWT.MOVEMENT_CLUSTER); - } - layout.dispose(); - return end == 0 ? text.substring(0, 1) : text + ellipses; -} public void dispose() { if (isDisposed ()) return; @@ -170,498 +130,7 @@ public void dispose() { shortenedText = null; font = null; } -void drawClose(GC gc) { - if (closeRect.width == 0 || closeRect.height == 0) return; - Display display = getDisplay(); - - // draw X 9x9 - int indent = Math.max(1, (CTabFolder.BUTTON_SIZE-9)/2); - int x = closeRect.x + indent; - int y = closeRect.y + indent; - y += parent.onBottom ? -1 : 1; - - Color closeBorder = display.getSystemColor(CTabFolder.BUTTON_BORDER); - switch (closeImageState) { - case CTabFolder.NORMAL: { - int[] shape = new int[] {x,y, x+2,y, x+4,y+2, x+5,y+2, x+7,y, x+9,y, - x+9,y+2, x+7,y+4, x+7,y+5, x+9,y+7, x+9,y+9, - x+7,y+9, x+5,y+7, x+4,y+7, x+2,y+9, x,y+9, - x,y+7, x+2,y+5, x+2,y+4, x,y+2}; - gc.setBackground(display.getSystemColor(CTabFolder.BUTTON_FILL)); - gc.fillPolygon(shape); - gc.setForeground(closeBorder); - gc.drawPolygon(shape); - break; - } - case CTabFolder.HOT: { - int[] shape = new int[] {x,y, x+2,y, x+4,y+2, x+5,y+2, x+7,y, x+9,y, - x+9,y+2, x+7,y+4, x+7,y+5, x+9,y+7, x+9,y+9, - x+7,y+9, x+5,y+7, x+4,y+7, x+2,y+9, x,y+9, - x,y+7, x+2,y+5, x+2,y+4, x,y+2}; - gc.setBackground(parent.getFillColor()); - gc.fillPolygon(shape); - gc.setForeground(closeBorder); - gc.drawPolygon(shape); - break; - } - case CTabFolder.SELECTED: { - int[] shape = new int[] {x+1,y+1, x+3,y+1, x+5,y+3, x+6,y+3, x+8,y+1, x+10,y+1, - x+10,y+3, x+8,y+5, x+8,y+6, x+10,y+8, x+10,y+10, - x+8,y+10, x+6,y+8, x+5,y+8, x+3,y+10, x+1,y+10, - x+1,y+8, x+3,y+6, x+3,y+5, x+1,y+3}; - gc.setBackground(parent.getFillColor()); - gc.fillPolygon(shape); - gc.setForeground(closeBorder); - gc.drawPolygon(shape); - break; - } - case CTabFolder.NONE: { - int[] shape = new int[] {x,y, x+10,y, x+10,y+10, x,y+10}; - if (parent.gradientColors != null && !parent.gradientVertical) { - parent.drawBackground(gc, shape, false); - } else { - Color defaultBackground = parent.getBackground(); - Color[] colors = parent.gradientColors; - int[] percents = parent.gradientPercents; - boolean vertical = parent.gradientVertical; - parent.drawBackground(gc, shape, x, y, 10, 10, defaultBackground, null, colors, percents, vertical); - } - break; - } - } -} -void drawSelected(GC gc ) { - Point size = parent.getSize(); - int rightEdge = Math.min (x + width, parent.getRightItemEdge()); - - // Draw selection border across all tabs - int xx = parent.borderLeft; - int yy = parent.onBottom ? size.y - parent.borderBottom - parent.tabHeight - parent.highlight_header : parent.borderTop + parent.tabHeight + 1; - int ww = size.x - parent.borderLeft - parent.borderRight; - int hh = parent.highlight_header - 1; - int[] shape = new int[] {xx,yy, xx+ww,yy, xx+ww,yy+hh, xx,yy+hh}; - if (parent.selectionGradientColors != null && !parent.selectionGradientVertical) { - parent.drawBackground(gc, shape, true); - } else { - gc.setBackground(parent.selectionBackground); - gc.fillRectangle(xx, yy, ww, hh); - } - - if (parent.single) { - if (!showing) return; - } else { - // if selected tab scrolled out of view or partially out of view - // just draw bottom line - if (!showing){ - int x1 = Math.max(0, parent.borderLeft - 1); - int y1 = (parent.onBottom) ? y - 1 : y + height; - int x2 = size.x - parent.borderRight; - gc.setForeground(getDisplay().getSystemColor(CTabFolder.BORDER1_COLOR)); - gc.drawLine(x1, y1, x2, y1); - return; - } - - // draw selected tab background and outline - shape = null; - if (this.parent.onBottom) { - int[] left = parent.simple ? CTabFolder.SIMPLE_BOTTOM_LEFT_CORNER : CTabFolder.BOTTOM_LEFT_CORNER; - int[] right = parent.simple ? CTabFolder.SIMPLE_BOTTOM_RIGHT_CORNER : parent.curve; - if (parent.borderLeft == 0 && parent.indexOf(this) == parent.firstIndex) { - left = new int[]{x, y+height}; - } - shape = new int[left.length+right.length+8]; - int index = 0; - shape[index++] = x; // first point repeated here because below we reuse shape to draw outline - shape[index++] = y - 1; - shape[index++] = x; - shape[index++] = y - 1; - for (int i = 0; i < left.length/2; i++) { - shape[index++] = x + left[2*i]; - shape[index++] = y + height + left[2*i+1] - 1; - } - for (int i = 0; i < right.length/2; i++) { - shape[index++] = parent.simple ? rightEdge - 1 + right[2*i] : rightEdge - parent.curveIndent + right[2*i]; - shape[index++] = parent.simple ? y + height + right[2*i+1] - 1 : y + right[2*i+1] - 2; - } - shape[index++] = parent.simple ? rightEdge - 1 : rightEdge + parent.curveWidth - parent.curveIndent; - shape[index++] = y - 1; - shape[index++] = parent.simple ? rightEdge - 1 : rightEdge + parent.curveWidth - parent.curveIndent; - shape[index++] = y - 1; - } else { - int[] left = parent.simple ? CTabFolder.SIMPLE_TOP_LEFT_CORNER : CTabFolder.TOP_LEFT_CORNER; - int[] right = parent.simple ? CTabFolder.SIMPLE_TOP_RIGHT_CORNER : parent.curve; - if (parent.borderLeft == 0 && parent.indexOf(this) == parent.firstIndex) { - left = new int[]{x, y}; - } - shape = new int[left.length+right.length+8]; - int index = 0; - shape[index++] = x; // first point repeated here because below we reuse shape to draw outline - shape[index++] = y + height + 1; - shape[index++] = x; - shape[index++] = y + height + 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++] = parent.simple ? rightEdge - 1 + right[2*i] : rightEdge - parent.curveIndent + right[2*i]; - shape[index++] = y + right[2*i+1]; - } - shape[index++] = parent.simple ? rightEdge - 1 : rightEdge + parent.curveWidth - parent.curveIndent; - shape[index++] = y + height + 1; - shape[index++] = parent.simple ? rightEdge - 1 : rightEdge + parent.curveWidth - parent.curveIndent; - shape[index++] = y + height + 1; - } - - Rectangle clipping = gc.getClipping(); - Rectangle bounds = getBounds(); - bounds.height += 1; - if (parent.onBottom) bounds.y -= 1; - boolean tabInPaint = clipping.intersects(bounds); - - if (tabInPaint) { - // fill in tab background - if (parent.selectionGradientColors != null && !parent.selectionGradientVertical) { - parent.drawBackground(gc, shape, true); - } else { - Color defaultBackground = parent.selectionBackground; - Image image = parent.selectionBgImage; - Color[] colors = parent.selectionGradientColors; - int[] percents = parent.selectionGradientPercents; - boolean vertical = parent.selectionGradientVertical; - xx = x; - yy = parent.onBottom ? y -1 : y + 1; - ww = width; - hh = height; - if (!parent.single && !parent.simple) ww += parent.curveWidth - parent.curveIndent; - parent.drawBackground(gc, shape, xx, yy, ww, hh, defaultBackground, image, colors, percents, vertical); - } - } - - //Highlight MUST be drawn before the outline so that outline can cover it in the right spots (start of swoop) - //otherwise the curve looks jagged - drawHighlight(gc, rightEdge); - - // draw outline - shape[0] = Math.max(0, parent.borderLeft - 1); - if (parent.borderLeft == 0 && parent.indexOf(this) == parent.firstIndex) { - shape[1] = parent.onBottom ? y + height - 1 : y; - shape[5] = shape[3] = shape[1]; - } - shape[shape.length - 2] = size.x - parent.borderRight + 1; - for (int i = 0; i < shape.length/2; i++) { - if (shape[2*i + 1] == y + height + 1) shape[2*i + 1] -= 1; - } - Color borderColor = getDisplay().getSystemColor(CTabFolder.BORDER1_COLOR); - if (! borderColor.equals(parent.lastBorderColor)) parent.createAntialiasColors(); - parent.antialias(shape, parent.selectedInnerColor, parent.selectedOuterColor, gc); - gc.setForeground(borderColor); - gc.drawPolyline(shape); - - if (!tabInPaint) return; - } - - // draw Image - int xDraw = x + LEFT_MARGIN; - if (parent.single && (parent.showClose || showClose)) xDraw += CTabFolder.BUTTON_SIZE; - Image image = getImage(); - if (image != null) { - Rectangle imageBounds = image.getBounds(); - // only draw image if it won't overlap with close button - int maxImageWidth = rightEdge - xDraw - RIGHT_MARGIN; - if (!parent.single && closeRect.width > 0) maxImageWidth -= closeRect.width + INTERNAL_SPACING; - if (imageBounds.width < maxImageWidth) { - int imageX = xDraw; - int imageY = y + (height - imageBounds.height) / 2; - imageY += parent.onBottom ? -1 : 1; - gc.drawImage(image, imageX, imageY); - xDraw += imageBounds.width + INTERNAL_SPACING; - } - } - - // draw Text - int textWidth = rightEdge - xDraw - RIGHT_MARGIN; - if (!parent.single && closeRect.width > 0) textWidth -= closeRect.width + INTERNAL_SPACING; - if (textWidth > 0) { - Font gcFont = gc.getFont(); - gc.setFont(font == null ? parent.getFont() : font); - - if (shortenedText == null || shortenedTextWidth != textWidth) { - shortenedText = shortenText(gc, getText(), textWidth); - shortenedTextWidth = textWidth; - } - Point extent = gc.textExtent(shortenedText, FLAGS); - int textY = y + (height - extent.y) / 2; - textY += parent.onBottom ? -1 : 1; - - gc.setForeground(parent.selectionForeground); - gc.drawText(shortenedText, xDraw, textY, FLAGS); - gc.setFont(gcFont); - - // draw a Focus rectangle - if (parent.isFocusControl()) { - Display display = getDisplay(); - if (parent.simple || parent.single) { - gc.setBackground(display.getSystemColor(SWT.COLOR_BLACK)); - gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE)); - gc.drawFocus(xDraw-1, textY-1, extent.x+2, extent.y+2); - } else { - gc.setForeground(display.getSystemColor(CTabFolder.BUTTON_BORDER)); - gc.drawLine(xDraw, textY+extent.y+1, xDraw+extent.x+1, textY+extent.y+1); - } - } - } - if (parent.showClose || showClose) drawClose(gc); -} -/* - * Draw a highlight effect along the left, top, and right edges of the tab. - * Only for curved tabs, on top. - * Do not draw if insufficient colors. - */ -void drawHighlight(GC gc, int rightEdge) { - //only draw for curvy tabs and only draw for top tabs - if(parent.simple || this.parent.onBottom) - return; - - if(parent.selectionHighlightGradientBegin == null) - return; - - Color[] gradients = parent.selectionHighlightGradientColorsCache; - if(gradients == null) - return; - int gradientsSize = gradients.length; - if(gradientsSize == 0) - return; //shouldn't happen but just to be tidy - - gc.setForeground(gradients[0]); - - //draw top horizontal line - gc.drawLine( - CTabFolder.TOP_LEFT_CORNER_HILITE[0] + x + 1, //rely on fact that first pair is top/right of curve - 1 + y, - rightEdge - parent.curveIndent, - 1 + y); - - int[] leftHighlightCurve = CTabFolder.TOP_LEFT_CORNER_HILITE; - - int d = parent.tabHeight - parent.topCurveHighlightEnd.length /2; - - int lastX = 0; - int lastY = 0; - int lastColorIndex = 0; - - //draw upper left curve highlight - for (int i = 0; i < leftHighlightCurve.length /2; i++) { - int rawX = leftHighlightCurve[i * 2]; - int rawY = leftHighlightCurve[i * 2 + 1]; - lastX = rawX + x; - lastY = rawY + y; - lastColorIndex = rawY - 1; - gc.setForeground(gradients[lastColorIndex]); - gc.drawPoint(lastX, lastY); - } - //draw left vertical line highlight - for(int i = lastColorIndex; i < gradientsSize; i++) { - gc.setForeground(gradients[i]); - gc.drawPoint(lastX, 1 + lastY++); - } - - int rightEdgeOffset = rightEdge - parent.curveIndent; - - //draw right swoop highlight up to diagonal portion - for (int i = 0; i < parent.topCurveHighlightStart.length /2; i++) { - int rawX = parent.topCurveHighlightStart[i * 2]; - int rawY = parent.topCurveHighlightStart[i * 2 + 1]; - lastX = rawX + rightEdgeOffset; - lastY = rawY + y; - lastColorIndex = rawY - 1; - if(lastColorIndex >= gradientsSize) - break; //can happen if tabs are unusually short and cut off the curve - gc.setForeground(gradients[lastColorIndex]); - gc.drawPoint(lastX, lastY); - } - //draw right diagonal line highlight - for(int i = lastColorIndex; i < lastColorIndex + d; i++) { - if(i >= gradientsSize) - break; //can happen if tabs are unusually short and cut off the curve - gc.setForeground(gradients[i]); - gc.drawPoint(1 + lastX++, 1 + lastY++); - } - - //draw right swoop highlight from diagonal portion to end - for (int i = 0; i < parent.topCurveHighlightEnd.length /2; i++) { - int rawX = parent.topCurveHighlightEnd[i * 2]; //d is already encoded in this value - int rawY = parent.topCurveHighlightEnd[i * 2 + 1]; //d already encoded - lastX = rawX + rightEdgeOffset; - lastY = rawY + y; - lastColorIndex = rawY - 1; - if(lastColorIndex >= gradientsSize) - break; //can happen if tabs are unusually short and cut off the curve - gc.setForeground(gradients[lastColorIndex]); - gc.drawPoint(lastX, lastY); - } -} - -/* - * Draw the unselected border for the receiver on the right. - * - * @param gc - */ -void drawRightUnselectedBorder(GC gc) { - - int[] shape = null; - int startX = x + width - 1; - - if (this.parent.onBottom) { - int[] right = parent.simple - ? CTabFolder.SIMPLE_UNSELECTED_INNER_CORNER - : CTabFolder.BOTTOM_RIGHT_CORNER; - - shape = new int[right.length + 2]; - int index = 0; - - for (int i = 0; i < right.length / 2; i++) { - shape[index++] = startX + right[2 * i]; - shape[index++] = y + height + right[2 * i + 1] - 1; - } - shape[index++] = startX; - shape[index++] = y - 1; - } else { - int[] right = parent.simple - ? CTabFolder.SIMPLE_UNSELECTED_INNER_CORNER - : CTabFolder.TOP_RIGHT_CORNER; - - shape = new int[right.length + 2]; - int index = 0; - - for (int i = 0; i < right.length / 2; i++) { - shape[index++] = startX + right[2 * i]; - shape[index++] = y + right[2 * i + 1]; - } - - shape[index++] = startX; - shape[index++] = y + height; - - } - - drawBorder(gc, shape); - -} - -/* - * Draw the border of the tab - * - * @param gc - * @param shape - */ -void drawBorder(GC gc, int[] shape) { - - gc.setForeground(getDisplay().getSystemColor(CTabFolder.BORDER1_COLOR)); - gc.drawPolyline(shape); -} - -/* - * Draw the unselected border for the receiver on the left. - * - * @param gc - */ -void drawLeftUnselectedBorder(GC gc) { - - int[] shape = null; - if (this.parent.onBottom) { - int[] left = parent.simple - ? CTabFolder.SIMPLE_UNSELECTED_INNER_CORNER - : CTabFolder.BOTTOM_LEFT_CORNER; - - shape = new int[left.length + 2]; - int index = 0; - shape[index++] = x; - shape[index++] = y - 1; - for (int i = 0; i < left.length / 2; i++) { - shape[index++] = x + left[2 * i]; - shape[index++] = y + height + left[2 * i + 1] - 1; - } - } else { - int[] left = parent.simple - ? CTabFolder.SIMPLE_UNSELECTED_INNER_CORNER - : CTabFolder.TOP_LEFT_CORNER; - - shape = new int[left.length + 2]; - int index = 0; - shape[index++] = x; - shape[index++] = y + height; - for (int i = 0; i < left.length / 2; i++) { - shape[index++] = x + left[2 * i]; - shape[index++] = y + left[2 * i + 1]; - } - - } - - drawBorder(gc, shape); -} - -void drawUnselected(GC gc) { - // Do not draw partial items - if (!showing) return; - - Rectangle clipping = gc.getClipping(); - Rectangle bounds = getBounds(); - if (!clipping.intersects(bounds)) return; - - // draw border - int index = parent.indexOf(this); - - if (index > 0 && index < parent.selectedIndex) - drawLeftUnselectedBorder(gc); - // If it is the last one then draw a line - if (index > parent.selectedIndex) - drawRightUnselectedBorder(gc); - - // draw Image - int xDraw = x + LEFT_MARGIN; - Image image = getImage(); - if (image != null && parent.showUnselectedImage) { - Rectangle imageBounds = image.getBounds(); - // only draw image if it won't overlap with close button - int maxImageWidth = x + width - xDraw - RIGHT_MARGIN; - if (parent.showUnselectedClose && (parent.showClose || showClose)) { - maxImageWidth -= closeRect.width + INTERNAL_SPACING; - } - if (imageBounds.width < maxImageWidth) { - int imageX = xDraw; - int imageHeight = imageBounds.height; - int imageY = y + (height - imageHeight) / 2; - imageY += parent.onBottom ? -1 : 1; - int imageWidth = imageBounds.width * imageHeight / imageBounds.height; - gc.drawImage(image, - imageBounds.x, imageBounds.y, imageBounds.width, imageBounds.height, - imageX, imageY, imageWidth, imageHeight); - xDraw += imageWidth + INTERNAL_SPACING; - } - } - // draw Text - int textWidth = x + width - xDraw - RIGHT_MARGIN; - if (parent.showUnselectedClose && (parent.showClose || showClose)) { - textWidth -= closeRect.width + INTERNAL_SPACING; - } - if (textWidth > 0) { - Font gcFont = gc.getFont(); - gc.setFont(font == null ? parent.getFont() : font); - if (shortenedText == null || shortenedTextWidth != textWidth) { - shortenedText = shortenText(gc, getText(), textWidth); - shortenedTextWidth = textWidth; - } - Point extent = gc.textExtent(shortenedText, FLAGS); - int textY = y + (height - extent.y) / 2; - textY += parent.onBottom ? -1 : 1; - gc.setForeground(parent.getForeground()); - gc.drawText(shortenedText, xDraw, textY, FLAGS); - gc.setFont(gcFont); - } - // draw close - if (parent.showUnselectedClose && (parent.showClose || showClose)) drawClose(gc); -} /** * Returns a rectangle describing the receiver's size and location * relative to its parent. @@ -675,9 +144,7 @@ void drawUnselected(GC gc) { */ public Rectangle getBounds () { //checkWidget(); - int w = width; - if (!parent.simple && !parent.single && parent.indexOf(this) == parent.selectedIndex) w += parent.curveWidth - parent.curveIndent; - return new Rectangle(x, y, w, height); + return new Rectangle(x, y, width, height); } /** * Gets the control that is displayed in the content area of the tab item. @@ -793,73 +260,7 @@ public boolean isShowing () { checkWidget(); return showing; } -void onPaint(GC gc, boolean isSelected) { - if (width == 0 || height == 0) return; - if (isSelected) { - drawSelected(gc); - } else { - drawUnselected(gc); - } -} -int preferredHeight(GC gc) { - Image image = getImage(); - int h = (image == null) ? 0 : image.getBounds().height; - String text = getText(); - if (font == null) { - h = Math.max(h, gc.textExtent(text, FLAGS).y); - } else { - Font gcFont = gc.getFont(); - gc.setFont(font); - h = Math.max(h, gc.textExtent(text, FLAGS).y); - gc.setFont(gcFont); - } - return h + TOP_MARGIN + BOTTOM_MARGIN; -} -int preferredWidth(GC gc, boolean isSelected, boolean minimum) { - // NOTE: preferred width does not include the "dead space" caused - // by the curve. - if (isDisposed()) return 0; - int w = 0; - Image image = getImage(); - if (image != null && (isSelected || parent.showUnselectedImage)) { - w += image.getBounds().width; - } - String text = null; - if (minimum) { - int minChars = parent.minChars; - text = minChars == 0 ? null : getText(); - if (text != null && text.length() > minChars) { - if (useEllipses()) { - int end = minChars < ELLIPSIS.length() + 1 ? minChars : minChars - ELLIPSIS.length(); - text = text.substring(0, end); - if (minChars > ELLIPSIS.length() + 1) text += ELLIPSIS; - } else { - int end = minChars; - text = text.substring(0, end); - } - } - } else { - text = getText(); - } - if (text != null) { - if (w > 0) w += INTERNAL_SPACING; - if (font == null) { - w += gc.textExtent(text, FLAGS).x; - } else { - Font gcFont = gc.getFont(); - gc.setFont(font); - w += gc.textExtent(text, FLAGS).x; - gc.setFont(gcFont); - } - } - if (parent.showClose || showClose) { - if (isSelected || parent.showUnselectedClose) { - if (w > 0) w += INTERNAL_SPACING; - w += CTabFolder.BUTTON_SIZE; - } - } - return w + LEFT_MARGIN + RIGHT_MARGIN; -} + /** * Sets the control that is used to fill the client area of * the tab folder when the user selects the tab item. @@ -969,18 +370,25 @@ public void setImage (Image image) { Rectangle bounds = image.getBounds(); if (bounds.width == oldBounds.width && bounds.height == oldBounds.height) { if (showing) { - boolean selected = parent.indexOf(this) == parent.selectedIndex; + int index = parent.indexOf(this); + boolean selected = index == parent.selectedIndex; if (selected || parent.showUnselectedImage) { - int imageX = x + LEFT_MARGIN, maxImageWidth; + CTabFolderRenderer renderer = parent.renderer; + Rectangle trim = renderer.computeTrim(index, SWT.NONE, 0, 0, 0, 0); + int imageX = x - trim.x, maxImageWidth; if (selected) { - if (parent.single && (parent.showClose || showClose)) imageX += CTabFolder.BUTTON_SIZE; - int rightEdge = Math.min (x + width, parent.getRightItemEdge()); - maxImageWidth = rightEdge - imageX - RIGHT_MARGIN; - if (!parent.single && closeRect.width > 0) maxImageWidth -= closeRect.width + INTERNAL_SPACING; + GC gc = new GC(parent); + if (parent.single && (parent.showClose || showClose)) { + imageX += renderer.computeSize(CTabFolderRenderer.PART_CLOSE_BUTTON, SWT.NONE, gc).x; + } + int rightEdge = Math.min (x + width, parent.getRightItemEdge(gc)); + gc.dispose(); + maxImageWidth = rightEdge - imageX - (trim.width + trim.x); + if (!parent.single && closeRect.width > 0) maxImageWidth -= closeRect.width + CTabFolderRenderer.INTERNAL_SPACING; } else { - maxImageWidth = x + width - imageX - RIGHT_MARGIN; + maxImageWidth = x + width - imageX - (trim.width + trim.x); if (parent.showUnselectedClose && (parent.showClose || showClose)) { - maxImageWidth -= closeRect.width + INTERNAL_SPACING; + maxImageWidth -= closeRect.width + CTabFolderRenderer.INTERNAL_SPACING; } } if (bounds.width < maxImageWidth) { -- cgit