| /******************************************************************************* |
| * Copyright (c) 2000, 2012 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.*; |
| |
| /** |
| * Instances of this class provide all of the measuring and drawing functionality |
| * required by <code>CTabFolder</code>. This class can be subclassed in order to |
| * customize the look of a CTabFolder. |
| * |
| * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> |
| * @since 3.6 |
| */ |
| public class CTabFolderRenderer { |
| |
| protected CTabFolder parent; |
| |
| 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 = 16; |
| static final int BUTTON_TRIM = 1; |
| |
| 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 |
| /** |
| * Part constant indicating the body of the tab folder. The body is the |
| * underlying container for all of the tab folder and all other parts are |
| * drawn on top of it. (value is -1). |
| * |
| * @see #computeSize(int, int, GC, int, int) |
| * @see #computeTrim(int, int, int, int, int, int) |
| * @see #draw(int, int, Rectangle, GC) |
| */ |
| public static final int PART_BODY = -1; |
| /** |
| * Part constant indicating the tab header of the folder (value is -2). The |
| * header is drawn on top of the body and provides an area for the tabs and |
| * other tab folder buttons to be rendered. |
| * |
| * @see #computeSize(int, int, GC, int, int) |
| * @see #computeTrim(int, int, int, int, int, int) |
| * @see #draw(int, int, Rectangle, GC) |
| */ |
| public static final int PART_HEADER = -2; |
| /** |
| * Part constant indicating the border of the tab folder. (value is -3). The |
| * border is drawn around the body and is part of the body trim. |
| * |
| * @see #computeSize(int, int, GC, int, int) |
| * @see #computeTrim(int, int, int, int, int, int) |
| * @see #draw(int, int, Rectangle, GC) |
| */ |
| public static final int PART_BORDER = -3; |
| /** |
| * Part constant indicating the background of the tab folder. (value is -4). |
| * |
| * @see #computeSize(int, int, GC, int, int) |
| * @see #computeTrim(int, int, int, int, int, int) |
| * @see #draw(int, int, Rectangle, GC) |
| */ |
| public static final int PART_BACKGROUND = -4; |
| /** |
| * Part constant indicating the maximize button of the tab folder. (value is |
| * -5). |
| * |
| * @see #computeSize(int, int, GC, int, int) |
| * @see #computeTrim(int, int, int, int, int, int) |
| * @see #draw(int, int, Rectangle, GC) |
| */ |
| public static final int PART_MAX_BUTTON = -5; |
| /** |
| * Part constant indicating the minimize button of the tab folder. (value is |
| * -6). |
| * |
| * @see #computeSize(int, int, GC, int, int) |
| * @see #computeTrim(int, int, int, int, int, int) |
| * @see #draw(int, int, Rectangle, GC) |
| */ |
| public static final int PART_MIN_BUTTON = -6; |
| /** |
| * Part constant indicating the chevron button of the tab folder. (value is |
| * -7). |
| * |
| * @see #computeSize(int, int, GC, int, int) |
| * @see #computeTrim(int, int, int, int, int, int) |
| * @see #draw(int, int, Rectangle, GC) |
| */ |
| public static final int PART_CHEVRON_BUTTON = -7; |
| /** |
| * Part constant indicating the close button of a tab item. (value is -8). |
| * |
| * @see #computeSize(int, int, GC, int, int) |
| * @see #computeTrim(int, int, int, int, int, int) |
| * @see #draw(int, int, Rectangle, GC) |
| */ |
| public static final int PART_CLOSE_BUTTON = -8; |
| |
| public static final int MINIMUM_SIZE = 1 << 24; //TODO: Should this be a state? |
| |
| |
| /** |
| * Constructs a new instance of this class given its parent. |
| * |
| * @param parent CTabFolder |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_ARGUMENT - if the parent is disposed</li> |
| * </ul> |
| * |
| * @see Widget#getStyle |
| */ |
| protected CTabFolderRenderer(CTabFolder parent) { |
| if (parent == null) return; |
| if (parent.isDisposed ()) SWT.error (SWT.ERROR_INVALID_ARGUMENT); |
| this.parent = parent; |
| } |
| |
| 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); |
| } |
| } |
| |
| /** |
| * Returns the preferred size of a part. |
| * <p> |
| * The <em>preferred size</em> of a part is the size that it would |
| * best be displayed at. The width hint and height hint arguments |
| * allow the caller to ask a control questions such as "Given a particular |
| * width, how high does the part need to be to show all of the contents?" |
| * To indicate that the caller does not wish to constrain a particular |
| * dimension, the constant <code>SWT.DEFAULT</code> is passed for the hint. |
| * </p><p> |
| * The <code>part</code> value indicated what component the preferred size is |
| * to be calculated for. Valid values are any of the part constants: |
| * <ul> |
| * <li>PART_BODY</li> |
| * <li>PART_HEADER</li> |
| * <li>PART_BORDER</li> |
| * <li>PART_BACKGROUND</li> |
| * <li>PART_MAX_BUTTON</li> |
| * <li>PART_MIN_BUTTON</li> |
| * <li>PART_CHEVRON_BUTTON</li> |
| * <li>PART_CLOSE_BUTTON</li> |
| * <li>A positive integer which is the index of an item in the CTabFolder.</li> |
| * </ul> |
| * </p> |
| * <p> |
| * The <code>state</code> parameter may be one of the following: |
| * <ul> |
| * <li>SWT.NONE</li> |
| * <li>SWT.SELECTED - whether the part is selected</li> |
| * </ul> |
| * </p> |
| * @param part a part constant |
| * @param state current state |
| * @param gc the gc to use for measuring |
| * @param wHint the width hint (can be <code>SWT.DEFAULT</code>) |
| * @param hHint the height hint (can be <code>SWT.DEFAULT</code>) |
| * @return the preferred size of the part |
| * |
| * @since 3.6 |
| */ |
| protected Point computeSize (int part, int state, GC gc, int wHint, int hHint) { |
| 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, wHint, hHint).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 && !image.isDisposed()) { |
| 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, SWT.DEFAULT, SWT.DEFAULT).x; |
| } |
| } |
| } |
| break; |
| } |
| Rectangle trim = computeTrim(part, state, 0, 0, width, height); |
| width = trim.width; |
| height = trim.height; |
| return new Point(width, height); |
| } |
| |
| /** |
| * Given a desired <em>client area</em> for the part |
| * (as described by the arguments), returns the bounding |
| * rectangle which would be required to produce that client |
| * area. |
| * <p> |
| * In other words, it returns a rectangle such that, if the |
| * part's bounds were set to that rectangle, the area |
| * of the part which is capable of displaying data |
| * (that is, not covered by the "trimmings") would be the |
| * rectangle described by the arguments (relative to the |
| * receiver's parent). |
| * </p> |
| * |
| * @param part one of the part constants |
| * @param state the state of the part |
| * @param x the desired x coordinate of the client area |
| * @param y the desired y coordinate of the client area |
| * @param width the desired width of the client area |
| * @param height the desired height of the client area |
| * @return the required bounds to produce the given client area |
| * |
| * @see CTabFolderRenderer#computeSize(int, int, GC, int, int) valid part and state values |
| * |
| * @since 3.6 |
| */ |
| 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 style = parent.getStyle(); |
| int highlight_header = (style & SWT.FLAT) != 0 ? 1 : 3; |
| int highlight_margin = (style & SWT.FLAT) != 0 ? 0 : 2; |
| 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: |
| //no trim |
| break; |
| case PART_MAX_BUTTON: |
| case PART_MIN_BUTTON: |
| case PART_CLOSE_BUTTON: |
| case PART_CHEVRON_BUTTON: |
| x -= BUTTON_TRIM; |
| y -= BUTTON_TRIM; |
| width += BUTTON_TRIM*2; |
| height += BUTTON_TRIM*2; |
| 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); |
| } |
| } |
| |
| /** |
| * Dispose of any operating system resources associated with |
| * the renderer. Called by the CTabFolder parent upon receiving |
| * the dispose event or when changing the renderer. |
| * |
| * @since 3.6 |
| */ |
| 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; |
| } |
| |
| /** |
| * Draw a specified <code>part</code> of the CTabFolder using the provided <code>bounds</code> and <code>GC</code>. |
| * <p>The valid CTabFolder <code>part</code> constants are: |
| * <ul> |
| * <li>PART_BODY - the entire body of the CTabFolder</li> |
| * <li>PART_HEADER - the upper tab area of the CTabFolder</li> |
| * <li>PART_BORDER - the border of the CTabFolder</li> |
| * <li>PART_BACKGROUND - the background of the CTabFolder</li> |
| * <li>PART_MAX_BUTTON</li> |
| * <li>PART_MIN_BUTTON</li> |
| * <li>PART_CHEVRON_BUTTON</li> |
| * <li>PART_CLOSE_BUTTON</li> |
| * <li>A positive integer which is the index of an item in the CTabFolder.</li> |
| * </ul> |
| * </p> |
| * <p> |
| * The <code>state</code> parameter may be a combination of: |
| * <ul> |
| * <li>SWT.BACKGROUND - whether the background should be drawn</li> |
| * <li>SWT.FOREGROUND - whether the foreground should be drawn</li> |
| * <li>SWT.SELECTED - whether the part is selected</li> |
| * <li>SWT.HOT - whether the part is hot (i.e. mouse is over the part)</li> |
| * </ul> |
| * </p> |
| * |
| * @param part part to draw |
| * @param state state of the part |
| * @param bounds the bounds of the part |
| * @param gc the gc to draw the part on |
| * |
| * @since 3.6 |
| */ |
| protected void draw (int part, int state, Rectangle bounds, GC gc) { |
| switch (part) { |
| case PART_BACKGROUND: |
| this.drawBackground(gc, bounds, state); |
| break; |
| 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, Rectangle bounds, int state) { |
| boolean selected = (state & SWT.SELECTED) != 0; |
| 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; |
| |
| drawBackground(gc, null, bounds.x, bounds.y, bounds.width, bounds.height, defaultBackground, image, colors, percents, vertical); |
| } |
| |
| 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 + ((parent.getStyle() & SWT.FLAT) != 0 ? 1 : 3); |
| 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 = null, region = null; |
| if (shape != null) { |
| clipping = new Region(); |
| gc.getClipping(clipping); |
| 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); |
| } |
| } |
| if (shape != null) { |
| 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; |
| |
| int style = parent.getStyle(); |
| int highlight_header = (style & SWT.FLAT) != 0 ? 1 : 3; |
| int highlight_margin = (style & SWT.FLAT) != 0 ? 0 : 2; |
| |
| // 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}; |
| drawBackground(gc, shape, false); |
| 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 highlight_header = (parent.getStyle() & SWT.FLAT) != 0 ? 1 : 3; |
| 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 && !image.isDisposed()) { |
| 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; |
| int highlight_header = (style & SWT.FLAT) != 0 ? 1 : 3; |
| 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 && !image.isDisposed() && 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 () { |
| //Temp fix for Bug 384743 |
| if (this.getClass().getName().equals("org.eclipse.e4.ui.workbench.renderers.swt.CTabRendering")) return; |
| 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; |
| } |
| } |