]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/custom/CTabFolderRenderer.java
Remove invalid SHA-256-Digests
[simantics/platform.git] / bundles / org.eclipse.swt.win32.win32.x86_64 / src / org / eclipse / swt / custom / CTabFolderRenderer.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2016 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Lars Vogel <Lars.Vogel@vogella.com> - Bug 455263
14  *******************************************************************************/
15 package org.eclipse.swt.custom;
16
17 import org.eclipse.swt.*;
18 import org.eclipse.swt.graphics.*;
19 import org.eclipse.swt.widgets.*;
20
21 /**
22  * Instances of this class provide all of the measuring and drawing functionality
23  * required by <code>CTabFolder</code>. This class can be subclassed in order to
24  * customize the look of a CTabFolder.
25  *
26  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
27  * @since 3.6
28  */
29 public class CTabFolderRenderer {
30
31         protected CTabFolder parent;
32
33         int[] curve;
34         int[] topCurveHighlightStart;
35         int[] topCurveHighlightEnd;
36         int curveWidth = 0;
37         int curveIndent = 0;
38         int lastTabHeight = -1;
39
40         Color fillColor;
41         /* Selected item appearance */
42         Color selectionHighlightGradientBegin = null;  //null == no highlight
43         //Although we are given new colours all the time to show different states (active, etc),
44         //some of which may have a highlight and some not, we'd like to retain the highlight colours
45         //as a cache so that we can reuse them if we're again told to show the highlight.
46         //We are relying on the fact that only one tab state usually gets a highlight, so only
47         //a single cache is required. If that happens to not be true, cache simply becomes less effective,
48         //but we don't leak colours.
49         Color[] selectionHighlightGradientColorsCache = null;  //null is a legal value, check on access
50         /* Colors for anti-aliasing */
51         Color selectedOuterColor = null;
52         Color selectedInnerColor = null;
53         Color tabAreaColor = null;
54         Color minMaxBorderColor = null;
55         /*
56          * Border color that was used in computing the cached anti-alias Colors.
57          * We have to recompute the colors if the border color changes
58          */
59         Color lastBorderColor = null;
60
61         Font chevronFont = null;
62
63         //TOP_LEFT_CORNER_HILITE is laid out in reverse (ie. top to bottom)
64         //so can fade in same direction as right swoop curve
65         static final int[] TOP_LEFT_CORNER_HILITE = new int[] {5,2, 4,2, 3,3, 2,4, 2,5, 1,6};
66
67         static final int[] TOP_LEFT_CORNER = new int[] {0,6, 1,5, 1,4, 4,1, 5,1, 6,0};
68         static final int[] TOP_RIGHT_CORNER = new int[] {-6,0, -5,1, -4,1, -1,4, -1,5, 0,6};
69         static final int[] BOTTOM_LEFT_CORNER = new int[] {0,-6, 1,-5, 1,-4, 4,-1, 5,-1, 6,0};
70         static final int[] BOTTOM_RIGHT_CORNER = new int[] {-6,0, -5,-1, -4,-1, -1,-4, -1,-5, 0,-6};
71
72         static final int[] SIMPLE_TOP_LEFT_CORNER = new int[] {0,2, 1,1, 2,0};
73         static final int[] SIMPLE_TOP_RIGHT_CORNER = new int[] {-2,0, -1,1, 0,2};
74         static final int[] SIMPLE_BOTTOM_LEFT_CORNER = new int[] {0,-2, 1,-1, 2,0};
75         static final int[] SIMPLE_BOTTOM_RIGHT_CORNER = new int[] {-2,0, -1,-1, 0,-2};
76         static final int[] SIMPLE_UNSELECTED_INNER_CORNER = new int[] {0,0};
77
78         static final int[] TOP_LEFT_CORNER_BORDERLESS = new int[] {0,6, 1,5, 1,4, 4,1, 5,1, 6,0};
79         static final int[] TOP_RIGHT_CORNER_BORDERLESS = new int[] {-7,0, -6,1, -5,1, -2,4, -2,5, -1,6};
80         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};
81         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};
82
83         static final int[] SIMPLE_TOP_LEFT_CORNER_BORDERLESS = new int[] {0,2, 1,1, 2,0};
84         static final int[] SIMPLE_TOP_RIGHT_CORNER_BORDERLESS= new int[] {-3,0, -2,1, -1,2};
85         static final int[] SIMPLE_BOTTOM_LEFT_CORNER_BORDERLESS = new int[] {0,-3, 1,-2, 2,-1, 3,0};
86         static final int[] SIMPLE_BOTTOM_RIGHT_CORNER_BORDERLESS = new int[] {-4,0, -3,-1, -2,-2, -1,-3};
87
88         static final RGB CLOSE_FILL = new RGB(252, 160, 160);
89
90         static final int BUTTON_SIZE = 16;
91         static final int BUTTON_TRIM = 1;
92
93         static final int BUTTON_BORDER = SWT.COLOR_WIDGET_DARK_SHADOW;
94         static final int BUTTON_FILL = SWT.COLOR_LIST_BACKGROUND;
95         static final int BORDER1_COLOR = SWT.COLOR_WIDGET_NORMAL_SHADOW;
96
97         static final int ITEM_TOP_MARGIN = 2;
98         static final int ITEM_BOTTOM_MARGIN = 2;
99         static final int ITEM_LEFT_MARGIN = 4;
100         static final int ITEM_RIGHT_MARGIN = 4;
101         static final int INTERNAL_SPACING = 4;
102         static final int FLAGS = SWT.DRAW_TRANSPARENT | SWT.DRAW_MNEMONIC | SWT.DRAW_DELIMITER;
103         static final String ELLIPSIS = "..."; //$NON-NLS-1$
104         private static final String CHEVRON_ELLIPSIS = "99+"; //$NON-NLS-1$
105         private static final int CHEVRON_FONT_HEIGHT = 10;
106
107         //Part constants
108         /**
109          * Part constant indicating the body of the tab folder. The body is the
110          * underlying container for all of the tab folder and all other parts are
111          * drawn on top of it. (value is -1).
112          *
113          * @see #computeSize(int, int, GC, int, int)
114          * @see #computeTrim(int, int, int, int, int, int)
115          * @see #draw(int, int, Rectangle, GC)
116          */
117         public static final int PART_BODY = -1;
118         /**
119          * Part constant indicating the tab header of the folder (value is -2). The
120          * header is drawn on top of the body and provides an area for the tabs and
121          * other tab folder buttons to be rendered.
122          *
123          * @see #computeSize(int, int, GC, int, int)
124          * @see #computeTrim(int, int, int, int, int, int)
125          * @see #draw(int, int, Rectangle, GC)
126          */
127         public static final int PART_HEADER = -2;
128         /**
129          * Part constant indicating the border of the tab folder. (value is -3). The
130          * border is drawn around the body and is part of the body trim.
131          *
132          * @see #computeSize(int, int, GC, int, int)
133          * @see #computeTrim(int, int, int, int, int, int)
134          * @see #draw(int, int, Rectangle, GC)
135          */
136         public static final int PART_BORDER = -3;
137         /**
138          * Part constant indicating the background of the tab folder. (value is -4).
139          *
140          * @see #computeSize(int, int, GC, int, int)
141          * @see #computeTrim(int, int, int, int, int, int)
142          * @see #draw(int, int, Rectangle, GC)
143          */
144         public static final int PART_BACKGROUND = -4;
145         /**
146          * Part constant indicating the maximize button of the tab folder. (value is
147          * -5).
148          *
149          * @see #computeSize(int, int, GC, int, int)
150          * @see #computeTrim(int, int, int, int, int, int)
151          * @see #draw(int, int, Rectangle, GC)
152          */
153         public static final int PART_MAX_BUTTON = -5;
154         /**
155          * Part constant indicating the minimize button of the tab folder. (value is
156          * -6).
157          *
158          * @see #computeSize(int, int, GC, int, int)
159          * @see #computeTrim(int, int, int, int, int, int)
160          * @see #draw(int, int, Rectangle, GC)
161          */
162         public static final int PART_MIN_BUTTON = -6;
163         /**
164          * Part constant indicating the chevron button of the tab folder. (value is
165          * -7).
166          *
167          * @see #computeSize(int, int, GC, int, int)
168          * @see #computeTrim(int, int, int, int, int, int)
169          * @see #draw(int, int, Rectangle, GC)
170          */
171         public static final int PART_CHEVRON_BUTTON = -7;
172         /**
173          * Part constant indicating the close button of a tab item. (value is -8).
174          *
175          * @see #computeSize(int, int, GC, int, int)
176          * @see #computeTrim(int, int, int, int, int, int)
177          * @see #draw(int, int, Rectangle, GC)
178          */
179         public static final int PART_CLOSE_BUTTON = -8;
180
181         public static final int MINIMUM_SIZE = 1 << 24; //TODO: Should this be a state?
182
183
184         /**
185          * Constructs a new instance of this class given its parent.
186          *
187          * @param parent CTabFolder
188          *
189          * @exception IllegalArgumentException <ul>
190          *    <li>ERROR_INVALID_ARGUMENT - if the parent is disposed</li>
191          * </ul>
192          *
193          * @see Widget#getStyle
194          */
195         protected CTabFolderRenderer(CTabFolder parent) {
196                 if (parent == null) return;
197                 if (parent.isDisposed ()) SWT.error (SWT.ERROR_INVALID_ARGUMENT);
198                 this.parent = parent;
199         }
200
201         void antialias (int[] shape, Color innerColor, Color outerColor, GC gc){
202                 // Don't perform anti-aliasing on Mac because the platform
203                 // already does it.  The simple style also does not require anti-aliasing.
204                 if (parent.simple) return;
205                 String platform = SWT.getPlatform();
206                 if ("cocoa".equals(platform)) return; //$NON-NLS-1$
207                 // Don't perform anti-aliasing on low resolution displays
208                 if (parent.getDisplay().getDepth() < 15) return;
209                 if (outerColor != null) {
210                         int index = 0;
211                         boolean left = true;
212                         int oldY = parent.onBottom ? 0 : parent.getSize().y;
213                         int[] outer = new int[shape.length];
214                         for (int i = 0; i < shape.length/2; i++) {
215                                 if (left && (index + 3 < shape.length)) {
216                                         left = parent.onBottom ? oldY <= shape[index+3] : oldY >= shape[index+3];
217                                         oldY = shape[index+1];
218                                 }
219                                 outer[index] = shape[index++] + (left ? -1 : +1);
220                                 outer[index] = shape[index++];
221                         }
222                         gc.setForeground(outerColor);
223                         gc.drawPolyline(outer);
224                 }
225                 if (innerColor != null) {
226                         int[] inner = new int[shape.length];
227                         int index = 0;
228                         boolean left = true;
229                         int oldY = parent.onBottom ? 0 : parent.getSize().y;
230                         for (int i = 0; i < shape.length/2; i++) {
231                                 if (left && (index + 3 < shape.length)) {
232                                         left = parent.onBottom ? oldY <= shape[index+3] : oldY >= shape[index+3];
233                                         oldY = shape[index+1];
234                                 }
235                                 inner[index] = shape[index++] + (left ? +1 : -1);
236                                 inner[index] = shape[index++];
237                         }
238                         gc.setForeground(innerColor);
239                         gc.drawPolyline(inner);
240                 }
241         }
242
243         /**
244          * Returns the preferred size of a part.
245          * <p>
246          * The <em>preferred size</em> of a part is the size that it would
247          * best be displayed at. The width hint and height hint arguments
248          * allow the caller to ask a control questions such as "Given a particular
249          * width, how high does the part need to be to show all of the contents?"
250          * To indicate that the caller does not wish to constrain a particular
251          * dimension, the constant <code>SWT.DEFAULT</code> is passed for the hint.
252          * </p><p>
253          * The <code>part</code> value indicated what component the preferred size is
254          * to be calculated for. Valid values are any of the part constants:
255          * </p>
256          * <ul>
257          * <li>PART_BODY</li>
258          * <li>PART_HEADER</li>
259          * <li>PART_BORDER</li>
260          * <li>PART_BACKGROUND</li>
261          * <li>PART_MAX_BUTTON</li>
262          * <li>PART_MIN_BUTTON</li>
263          * <li>PART_CHEVRON_BUTTON</li>
264          * <li>PART_CLOSE_BUTTON</li>
265          * <li>A positive integer which is the index of an item in the CTabFolder.</li>
266          * </ul>
267          * <p>
268          * The <code>state</code> parameter may be one of the following:
269          * </p>
270          * <ul>
271          * <li>SWT.NONE</li>
272          * <li>SWT.SELECTED - whether the part is selected</li>
273          * </ul>
274          * @param part a part constant
275          * @param state current state
276          * @param gc the gc to use for measuring
277          * @param wHint the width hint (can be <code>SWT.DEFAULT</code>)
278          * @param hHint the height hint (can be <code>SWT.DEFAULT</code>)
279          * @return the preferred size of the part
280          *
281          * @since 3.6
282          */
283         protected Point computeSize (int part, int state, GC gc, int wHint, int hHint) {
284                 int width = 0, height = 0;
285                 switch (part) {
286                         case PART_HEADER:
287                                 if (parent.fixedTabHeight != SWT.DEFAULT) {
288                                         height = parent.fixedTabHeight == 0 ? 0 : parent.fixedTabHeight + 1; // +1 for line drawn across top of tab
289                                 } else {
290                                         CTabItem[] items = parent.items;
291                                         if (items.length == 0) {
292                                                 height = gc.textExtent("Default", FLAGS).y + ITEM_TOP_MARGIN + ITEM_BOTTOM_MARGIN; //$NON-NLS-1$
293                                         } else {
294                                                 for (int i=0; i < items.length; i++) {
295                                                         height = Math.max(height, computeSize(i, SWT.NONE, gc, wHint, hHint).y);
296                                                 }
297                                         }
298                                         gc.dispose();
299                                 }
300                                 break;
301                         case PART_MAX_BUTTON:
302                         case PART_MIN_BUTTON:
303                         case PART_CLOSE_BUTTON:
304                                 width = height = BUTTON_SIZE;
305                                 break;
306                         case PART_CHEVRON_BUTTON:
307                                 Font prevFont = gc.getFont();
308                                 gc.setFont(getChevronFont(parent.getDisplay()));
309                                 int widthOfDoubleArrows = 8; // see drawChevron method
310                                 width = gc.textExtent(CHEVRON_ELLIPSIS).x + widthOfDoubleArrows;
311                                 height = BUTTON_SIZE;
312                                 gc.setFont(prevFont);
313                                 break;
314                         default:
315                                 if (0 <= part && part < parent.getItemCount()) {
316                                         updateCurves();
317                                         CTabItem item = parent.items[part];
318                                         if (item.isDisposed()) return new Point(0,0);
319                                         Image image = item.getImage();
320                                         if (image != null && !image.isDisposed()) {
321                                                 Rectangle bounds = image.getBounds();
322                                                 if ((state & SWT.SELECTED) != 0 || parent.showUnselectedImage) {
323                                                         width += bounds.width;
324                                                 }
325                                                 height =  bounds.height;
326                                         }
327                                         String text = null;
328                                         if ((state & MINIMUM_SIZE) != 0) {
329                                                 int minChars = parent.minChars;
330                                                 text = minChars == 0 ? null : item.getText();
331                                                 if (text != null && text.length() > minChars) {
332                                                         if (useEllipses()) {
333                                                                 int end = minChars < ELLIPSIS.length() + 1 ? minChars : minChars - ELLIPSIS.length();
334                                                                 text = text.substring(0, end);
335                                                                 if (minChars > ELLIPSIS.length() + 1) text += ELLIPSIS;
336                                                         } else {
337                                                                 int end = minChars;
338                                                                 text = text.substring(0, end);
339                                                         }
340                                                 }
341                                         } else {
342                                                 text = item.getText();
343                                         }
344                                         if (text != null) {
345                                                 if (width > 0) width += INTERNAL_SPACING;
346                                                 if (item.font == null) {
347                                                         Point size = gc.textExtent(text, FLAGS);
348                                                         width += size.x;
349                                                         height = Math.max(height, size.y);
350                                                 } else {
351                                                         Font gcFont = gc.getFont();
352                                                         gc.setFont(item.font);
353                                                         Point size = gc.textExtent(text, FLAGS);
354                                                         width += size.x;
355                                                         height = Math.max(height, size.y);
356                                                         gc.setFont(gcFont);
357                                                 }
358                                         }
359                                         if (parent.showClose || item.showClose) {
360                                                 if ((state & SWT.SELECTED) != 0 || parent.showUnselectedClose) {
361                                                         if (width > 0) width += INTERNAL_SPACING;
362                                                         width += computeSize(PART_CLOSE_BUTTON, SWT.NONE, gc, SWT.DEFAULT, SWT.DEFAULT).x;
363                                                 }
364                                         }
365                                 }
366                                 break;
367                 }
368                 Rectangle trim = computeTrim(part, state, 0, 0, width, height);
369                 width = trim.width;
370                 height = trim.height;
371                 return new Point(width, height);
372         }
373
374         /**
375          * Given a desired <em>client area</em> for the part
376          * (as described by the arguments), returns the bounding
377          * rectangle which would be required to produce that client
378          * area.
379          * <p>
380          * In other words, it returns a rectangle such that, if the
381          * part's bounds were set to that rectangle, the area
382          * of the part which is capable of displaying data
383          * (that is, not covered by the "trimmings") would be the
384          * rectangle described by the arguments (relative to the
385          * receiver's parent).
386          * </p>
387          *
388          * @param part one of the part constants
389          * @param state the state of the part
390          * @param x the desired x coordinate of the client area
391          * @param y the desired y coordinate of the client area
392          * @param width the desired width of the client area
393          * @param height the desired height of the client area
394          * @return the required bounds to produce the given client area
395          *
396          * @see CTabFolderRenderer#computeSize(int, int, GC, int, int) valid part and state values
397          *
398          * @since 3.6
399          */
400         protected Rectangle computeTrim (int part, int state, int x, int y, int width, int height) {
401                 int borderLeft = parent.borderVisible ? 1 : 0;
402                 int borderRight = borderLeft;
403                 int borderTop = parent.onBottom ? borderLeft : 0;
404                 int borderBottom = parent.onBottom ? 0 : borderLeft;
405                 int tabHeight = parent.tabHeight;
406                 switch (part) {
407                         case PART_BODY:
408                                 int style = parent.getStyle();
409                                 int highlight_header = (style & SWT.FLAT) != 0 ? 1 : 3;
410                                 int highlight_margin = (style & SWT.FLAT) != 0 ? 0 : 2;
411                                 if (parent.fixedTabHeight == 0 && (style & SWT.FLAT) != 0 && (style & SWT.BORDER) == 0) {
412                                         highlight_header = 0;
413                                 }
414                                 int marginWidth = parent.marginWidth;
415                                 int marginHeight = parent.marginHeight;
416                                 x = x - marginWidth - highlight_margin - borderLeft;
417                                 width = width + borderLeft + borderRight + 2*marginWidth + 2*highlight_margin;
418                                 if (parent.minimized) {
419                                         y = parent.onBottom ? y - borderTop : y - highlight_header - tabHeight - borderTop;
420                                         height = borderTop + borderBottom + tabHeight + highlight_header;
421                                 } else {
422                                         y = parent.onBottom ? y - marginHeight - highlight_margin - borderTop : y - marginHeight - highlight_header - tabHeight - borderTop;
423                                         height = height + borderTop + borderBottom + 2*marginHeight + tabHeight + highlight_header + highlight_margin;
424                                 }
425                                 break;
426                         case PART_HEADER:
427                                 //no trim
428                                 break;
429                         case PART_MAX_BUTTON:
430                         case PART_MIN_BUTTON:
431                         case PART_CLOSE_BUTTON:
432                         case PART_CHEVRON_BUTTON:
433                                 x -= BUTTON_TRIM;
434                                 y -= BUTTON_TRIM;
435                                 width += BUTTON_TRIM*2;
436                                 height += BUTTON_TRIM*2;
437                                 break;
438                         case PART_BORDER:
439                                 x = x - borderLeft;
440                                 width = width + borderLeft + borderRight;
441                                 if (!parent.simple) width += 2; // TOP_RIGHT_CORNER needs more space
442                                 y = y - borderTop;
443                                 height = height + borderTop + borderBottom;
444                                 break;
445                         default:
446                                 if (0 <= part && part < parent.getItemCount()) {
447                                         updateCurves();
448                                         x = x - ITEM_LEFT_MARGIN;
449                                         width = width + ITEM_LEFT_MARGIN + ITEM_RIGHT_MARGIN;
450                                         if (!parent.simple && !parent.single && (state & SWT.SELECTED) != 0) {
451                                                 width += curveWidth - curveIndent;
452                                         }
453                                         y = y - ITEM_TOP_MARGIN;
454                                         height = height + ITEM_TOP_MARGIN + ITEM_BOTTOM_MARGIN;
455                                 }
456                                 break;
457                 }
458                 return new Rectangle(x, y, width, height);
459         }
460
461         void createAntialiasColors() {
462                 disposeAntialiasColors();
463                 lastBorderColor = parent.getDisplay().getSystemColor(BORDER1_COLOR);
464                 RGB lineRGB = lastBorderColor.getRGB();
465                 /* compute the selected color */
466                 RGB innerRGB = parent.selectionBackground.getRGB();
467                 if (parent.selectionBgImage != null ||
468                         (parent.selectionGradientColors != null && parent.selectionGradientColors.length > 1)) {
469                         innerRGB = null;
470                 }
471                 RGB outerRGB = parent.getBackground().getRGB();
472                 if (parent.gradientColors != null && parent.gradientColors.length > 1) {
473                         outerRGB = null;
474                 }
475                 if (outerRGB != null) {
476                         RGB from = lineRGB;
477                         RGB to = outerRGB;
478                         int red = from.red + 2*(to.red - from.red)/3;
479                         int green = from.green + 2*(to.green - from.green)/3;
480                         int blue = from.blue + 2*(to.blue - from.blue)/3;
481                         selectedOuterColor = new Color(parent.getDisplay(), red, green, blue);
482                 }
483                 if (innerRGB != null) {
484                         RGB from = lineRGB;
485                         RGB to = innerRGB;
486                         int red = from.red + 2*(to.red - from.red)/3;
487                         int green = from.green + 2*(to.green - from.green)/3;
488                         int blue = from.blue + 2*(to.blue - from.blue)/3;
489                         selectedInnerColor = new Color(parent.getDisplay(), red, green, blue);
490                 }
491                 /* compute the tabArea color */
492                 outerRGB = parent.getParent().getBackground().getRGB();
493                 if (outerRGB != null) {
494                         RGB from = lineRGB;
495                         RGB to = outerRGB;
496                         int red = from.red + 2*(to.red - from.red)/3;
497                         int green = from.green + 2*(to.green - from.green)/3;
498                         int blue = from.blue + 2*(to.blue - from.blue)/3;
499                         tabAreaColor = new Color(parent.getDisplay(), red, green, blue);
500                 }
501         }
502
503         /*
504          * Allocate colors for the highlight line.
505          * Colours will be a gradual blend ranging from to.
506          * Blend length will be tab height.
507          * Recompute this if tab height changes.
508          * Could remain null if there'd be no gradient (start=end or low colour display)
509          */
510         void createSelectionHighlightGradientColors(Color start) {
511                 disposeSelectionHighlightGradientColors(); //dispose if existing
512
513                 if(start == null)  //shouldn't happen but just to be safe
514                         return;
515
516                 //alloc colours for entire height to ensure it matches wherever we stop drawing
517                 int fadeGradientSize = parent.tabHeight;
518
519                 RGB from = start.getRGB();
520                 RGB to = parent.selectionBackground.getRGB();
521
522                 selectionHighlightGradientColorsCache = new Color[fadeGradientSize];
523                 int denom = fadeGradientSize - 1;
524
525                 for (int i = 0; i < fadeGradientSize; i++) {
526                         int propFrom = denom - i;
527                         int propTo = i;
528                         int red = (to.red * propTo + from.red * propFrom) / denom;
529                         int green = (to.green * propTo  + from.green * propFrom) / denom;
530                         int blue = (to.blue * propTo  + from.blue * propFrom) / denom;
531                         selectionHighlightGradientColorsCache[i] = new Color(parent.getDisplay(), red, green, blue);
532                 }
533         }
534
535         /**
536          * Dispose of any operating system resources associated with
537          * the renderer. Called by the CTabFolder parent upon receiving
538          * the dispose event or when changing the renderer.
539          *
540          * @since 3.6
541          */
542         protected void dispose() {
543                 disposeAntialiasColors();
544                 disposeSelectionHighlightGradientColors();
545                 if (fillColor != null) {
546                         fillColor.dispose();
547                         fillColor = null;
548                 }
549                 if (chevronFont != null) {
550                         chevronFont.dispose();
551                         chevronFont = null;
552                 }
553                 if (minMaxBorderColor != null) {
554                         minMaxBorderColor.dispose();
555                         minMaxBorderColor = null;
556                 }
557         }
558
559         void disposeAntialiasColors() {
560                 if (tabAreaColor != null) tabAreaColor.dispose();
561                 if (selectedInnerColor != null) selectedInnerColor.dispose();
562                 if (selectedOuterColor != null) selectedOuterColor.dispose();
563                 tabAreaColor = selectedInnerColor = selectedOuterColor = null;
564         }
565
566         void disposeSelectionHighlightGradientColors() {
567                 if(selectionHighlightGradientColorsCache == null)
568                         return;
569                 for (Color element : selectionHighlightGradientColorsCache) {
570                         element.dispose();
571                 }
572                 selectionHighlightGradientColorsCache = null;
573         }
574
575         /**
576          * Draw a specified <code>part</code> of the CTabFolder using the provided <code>bounds</code> and <code>GC</code>.
577          * <p>The valid CTabFolder <code>part</code> constants are:
578          * </p>
579          * <ul>
580          * <li>PART_BODY - the entire body of the CTabFolder</li>
581          * <li>PART_HEADER - the upper tab area of the CTabFolder</li>
582          * <li>PART_BORDER - the border of the CTabFolder</li>
583          * <li>PART_BACKGROUND - the background of the CTabFolder</li>
584          * <li>PART_MAX_BUTTON</li>
585          * <li>PART_MIN_BUTTON</li>
586          * <li>PART_CHEVRON_BUTTON</li>
587          * <li>PART_CLOSE_BUTTON</li>
588          * <li>A positive integer which is the index of an item in the CTabFolder.</li>
589          * </ul>
590          * <p>
591          * The <code>state</code> parameter may be a combination of:
592          * </p>
593          * <ul>
594          * <li>SWT.BACKGROUND - whether the background should be drawn</li>
595          * <li>SWT.FOREGROUND - whether the foreground should be drawn</li>
596          * <li>SWT.SELECTED - whether the part is selected</li>
597          * <li>SWT.HOT - whether the part is hot (i.e. mouse is over the part)</li>
598          * </ul>
599          *
600          * @param part part to draw
601          * @param state state of the part
602          * @param bounds the bounds of the part
603          * @param gc the gc to draw the part on
604          *
605          * @since 3.6
606          */
607         protected void draw (int part, int state, Rectangle bounds, GC gc) {
608                 if (minMaxBorderColor == null) {
609                         // this color has to be identical to the colors used in the PNG files of
610                         // the view drop down menu located in
611                         // org.eclipse.e4.ui.workbench.renderers.swt/icons/full/elcl16/view_menu.png
612                         minMaxBorderColor = new Color (gc.getDevice(), 105, 105, 105);
613                 }
614                 switch (part) {
615                         case PART_BACKGROUND:
616                                 this.drawBackground(gc, bounds, state);
617                                 break;
618                         case PART_BODY:
619                                 drawBody(gc, bounds, state);
620                                 break;
621                         case PART_HEADER:
622                                 drawTabArea(gc, bounds, state);
623                                 break;
624                         case PART_MAX_BUTTON:
625                                 drawMaximize(gc, bounds, state);
626                                 break;
627                         case PART_MIN_BUTTON:
628                                 drawMinimize(gc, bounds, state);
629                                 break;
630                         case PART_CHEVRON_BUTTON:
631                                 drawChevron(gc, bounds, state);
632                                 break;
633                         default:
634                                 if (0 <= part && part < parent.getItemCount()) {
635                                         if (bounds.width == 0 || bounds.height == 0) return;
636                                         if ((state & SWT.SELECTED) != 0 ) {
637                                                 drawSelected(part, gc, bounds, state);
638                                         } else {
639                                                 drawUnselected(part, gc, bounds, state);
640                                         }
641                                 }
642                                 break;
643                 }
644         }
645
646         void drawBackground(GC gc, Rectangle bounds, int state) {
647                 boolean selected = (state & SWT.SELECTED) != 0;
648                 Color defaultBackground = selected && parent.shouldHighlight() ? parent.selectionBackground : parent.getBackground();
649                 Image image = selected ? parent.selectionBgImage : null;
650                 Color[] colors = selected & parent.shouldHighlight() ? parent.selectionGradientColors : parent.gradientColors;
651                 int[] percents = selected ? parent.selectionGradientPercents : parent.gradientPercents;
652                 boolean vertical = selected ? parent.selectionGradientVertical : parent.gradientVertical;
653
654                 drawBackground(gc, null, bounds.x, bounds.y, bounds.width, bounds.height, defaultBackground, image, colors, percents, vertical);
655         }
656
657         void drawBackground(GC gc, int[] shape, boolean selected) {
658                 Color defaultBackground = selected && parent.shouldHighlight() ? parent.selectionBackground : parent.getBackground();
659                 Image image = selected ? parent.selectionBgImage : null;
660                 Color[] colors = selected && parent.shouldHighlight() ? parent.selectionGradientColors : parent.gradientColors;
661                 int[] percents = selected ? parent.selectionGradientPercents : parent.gradientPercents;
662                 boolean vertical = selected ? parent.selectionGradientVertical : parent.gradientVertical;
663                 Point size = parent.getSize();
664                 int width = size.x;
665                 int height = parent.tabHeight + ((parent.getStyle() & SWT.FLAT) != 0 ? 1 : 3);
666                 int x = 0;
667
668                 int borderLeft = parent.borderVisible ? 1 : 0;
669                 int borderTop = parent.onBottom ? borderLeft : 0;
670                 int borderBottom = parent.onBottom ? 0 : borderLeft;
671
672                 if (borderLeft > 0) {
673                         x += 1; width -= 2;
674                 }
675                 int y = parent.onBottom ? size.y - borderBottom - height : borderTop;
676                 drawBackground(gc, shape, x, y, width, height, defaultBackground, image, colors, percents, vertical);
677         }
678
679         void drawBackground(GC gc, int[] shape, int x, int y, int width, int height, Color defaultBackground, Image image, Color[] colors, int[] percents, boolean vertical) {
680                 Region clipping = null, region = null;
681                 if (shape != null) {
682                         clipping = new Region();
683                         gc.getClipping(clipping);
684                         region = new Region();
685                         region.add(shape);
686                         region.intersect(clipping);
687                         gc.setClipping(region);
688                 }
689                 if (image != null) {
690                         // draw the background image in shape
691                         gc.setBackground(defaultBackground);
692                         gc.fillRectangle(x, y, width, height);
693                         Rectangle imageRect = image.getBounds();
694                         gc.drawImage(image, imageRect.x, imageRect.y, imageRect.width, imageRect.height, x, y, width, height);
695                 } else if (colors != null) {
696                         // draw gradient
697                         if (colors.length == 1) {
698                                 Color background = colors[0] != null ? colors[0] : defaultBackground;
699                                 gc.setBackground(background);
700                                 gc.fillRectangle(x, y, width, height);
701                         } else {
702                                 if (vertical) {
703                                         if (parent.onBottom) {
704                                                 int pos = 0;
705                                                 if (percents[percents.length - 1] < 100) {
706                                                         pos = (100 - percents[percents.length - 1]) * height / 100;
707                                                         gc.setBackground(defaultBackground);
708                                                         gc.fillRectangle(x, y, width, pos);
709                                                 }
710                                                 Color lastColor = colors[colors.length-1];
711                                                 if (lastColor == null) lastColor = defaultBackground;
712                                                 for (int i = percents.length-1; i >= 0; i--) {
713                                                         gc.setForeground(lastColor);
714                                                         lastColor = colors[i];
715                                                         if (lastColor == null) lastColor = defaultBackground;
716                                                         gc.setBackground(lastColor);
717                                                         int percentage = i > 0 ? percents[i] - percents[i-1] : percents[i];
718                                                         int gradientHeight = percentage * height / 100;
719                                                         gc.fillGradientRectangle(x, y+pos, width, gradientHeight, true);
720                                                         pos += gradientHeight;
721                                                 }
722                                         } else {
723                                                 Color lastColor = colors[0];
724                                                 if (lastColor == null) lastColor = defaultBackground;
725                                                 int pos = 0;
726                                                 for (int i = 0; i < percents.length; i++) {
727                                                         gc.setForeground(lastColor);
728                                                         lastColor = colors[i + 1];
729                                                         if (lastColor == null) lastColor = defaultBackground;
730                                                         gc.setBackground(lastColor);
731                                                         int percentage = i > 0 ? percents[i] - percents[i-1] : percents[i];
732                                                         int gradientHeight = percentage * height / 100;
733                                                         gc.fillGradientRectangle(x, y+pos, width, gradientHeight, true);
734                                                         pos += gradientHeight;
735                                                 }
736                                                 if (pos < height) {
737                                                         gc.setBackground(defaultBackground);
738                                                         gc.fillRectangle(x, pos, width, height-pos+1);
739                                                 }
740                                         }
741                                 } else { //horizontal gradient
742                                         y = 0;
743                                         height = parent.getSize().y;
744                                         Color lastColor = colors[0];
745                                         if (lastColor == null) lastColor = defaultBackground;
746                                         int pos = 0;
747                                         for (int i = 0; i < percents.length; ++i) {
748                                                 gc.setForeground(lastColor);
749                                                 lastColor = colors[i + 1];
750                                                 if (lastColor == null) lastColor = defaultBackground;
751                                                 gc.setBackground(lastColor);
752                                                 int gradientWidth = (percents[i] * width / 100) - pos;
753                                                 gc.fillGradientRectangle(x+pos, y, gradientWidth, height, false);
754                                                 pos += gradientWidth;
755                                         }
756                                         if (pos < width) {
757                                                 gc.setBackground(defaultBackground);
758                                                 gc.fillRectangle(x+pos, y, width-pos, height);
759                                         }
760                                 }
761                         }
762                 } else {
763                         // draw a solid background using default background in shape
764                         if ((parent.getStyle() & SWT.NO_BACKGROUND) != 0 || !defaultBackground.equals(parent.getBackground())) {
765                                 gc.setBackground(defaultBackground);
766                                 gc.fillRectangle(x, y, width, height);
767                         }
768                 }
769                 if (shape != null) {
770                         gc.setClipping(clipping);
771                         clipping.dispose();
772                         region.dispose();
773                 }
774         }
775
776         /*
777          * Draw the border of the tab
778          *
779          * @param gc
780          * @param shape
781          */
782         void drawBorder(GC gc, int[] shape) {
783
784                 gc.setForeground(parent.getDisplay().getSystemColor(BORDER1_COLOR));
785                 gc.drawPolyline(shape);
786         }
787
788         void drawBody(GC gc, Rectangle bounds, int state) {
789                 Point size = new Point(bounds.width, bounds.height);
790                 int selectedIndex = parent.selectedIndex;
791                 int tabHeight = parent.tabHeight;
792
793                 int borderLeft = parent.borderVisible ? 1 : 0;
794                 int borderRight = borderLeft;
795                 int borderTop = parent.onBottom ? borderLeft : 0;
796                 int borderBottom = parent.onBottom ? 0 : borderLeft;
797
798                 int style = parent.getStyle();
799                 int highlight_header = (style & SWT.FLAT) != 0 ? 1 : 3;
800                 int highlight_margin = (style & SWT.FLAT) != 0 ? 0 : 2;
801
802                 // fill in body
803                 if (!parent.minimized){
804                         int width = size.x  - borderLeft - borderRight - 2*highlight_margin;
805                         int height = size.y - borderTop - borderBottom - tabHeight - highlight_header - highlight_margin;
806                         // Draw highlight margin
807                         if (highlight_margin > 0) {
808                                 int[] shape = null;
809                                 if (parent.onBottom) {
810                                         int x1 = borderLeft;
811                                         int y1 = borderTop;
812                                         int x2 = size.x - borderRight;
813                                         int y2 = size.y - borderBottom - tabHeight - highlight_header;
814                                         shape = new int[] {x1,y1, x2,y1, x2,y2, x2-highlight_margin,y2,
815                                                                            x2-highlight_margin, y1+highlight_margin, x1+highlight_margin,y1+highlight_margin,
816                                                                            x1+highlight_margin,y2, x1,y2};
817                                 } else {
818                                         int x1 = borderLeft;
819                                         int y1 = borderTop + tabHeight + highlight_header;
820                                         int x2 = size.x - borderRight;
821                                         int y2 = size.y - borderBottom;
822                                         shape = new int[] {x1,y1, x1+highlight_margin,y1, x1+highlight_margin,y2-highlight_margin,
823                                                                            x2-highlight_margin,y2-highlight_margin, x2-highlight_margin,y1,
824                                                                            x2,y1, x2,y2, x1,y2};
825                                 }
826                                 // If horizontal gradient, show gradient across the whole area
827                                 if (selectedIndex != -1 && parent.selectionGradientColors != null && parent.selectionGradientColors.length > 1 && !parent.selectionGradientVertical) {
828                                         drawBackground(gc, shape, true);
829                                 } else if (selectedIndex == -1 && parent.gradientColors != null && parent.gradientColors.length > 1 && !parent.gradientVertical) {
830                                         drawBackground(gc, shape, false);
831                                 } else {
832                                         gc.setBackground(selectedIndex == -1 ? parent.getBackground() : parent.selectionBackground);
833                                         gc.fillPolygon(shape);
834                                 }
835                         }
836                         //Draw client area
837                         if ((parent.getStyle() & SWT.NO_BACKGROUND) != 0) {
838                                 gc.setBackground(parent.getBackground());
839                                 int marginWidth = parent.marginWidth;
840                                 int marginHeight = parent.marginHeight;
841                                 int xClient = borderLeft + marginWidth + highlight_margin, yClient;
842                                 if (parent.onBottom) {
843                                         yClient = borderTop + highlight_margin + marginHeight;
844                                 } else {
845                                         yClient = borderTop + tabHeight + highlight_header + marginHeight;
846                                 }
847                                 gc.fillRectangle(xClient - marginWidth, yClient - marginHeight, width, height);
848                         }
849                 } else {
850                         if ((parent.getStyle() & SWT.NO_BACKGROUND) != 0) {
851                                 int height = borderTop + tabHeight + highlight_header + borderBottom;
852                                 if (size.y > height) {
853                                         gc.setBackground(parent.getParent().getBackground());
854                                         gc.fillRectangle(0, height, size.x, size.y - height);
855                                 }
856                         }
857                 }
858
859                 //draw 1 pixel border around outside
860                 if (borderLeft > 0) {
861                         gc.setForeground(parent.getDisplay().getSystemColor(BORDER1_COLOR));
862                         int x1 = borderLeft - 1;
863                         int x2 = size.x - borderRight;
864                         int y1 = parent.onBottom ? borderTop - 1 : borderTop + tabHeight;
865                         int y2 = parent.onBottom ? size.y - tabHeight - borderBottom - 1 : size.y - borderBottom;
866                         gc.drawLine(x1, y1, x1, y2); // left
867                         gc.drawLine(x2, y1, x2, y2); // right
868                         if (parent.onBottom) {
869                                 gc.drawLine(x1, y1, x2, y1); // top
870                         } else {
871                                 gc.drawLine(x1, y2, x2, y2); // bottom
872                         }
873                 }
874         }
875
876         void drawClose(GC gc, Rectangle closeRect, int closeImageState) {
877                 if (closeRect.width == 0 || closeRect.height == 0) return;
878                 Display display = parent.getDisplay();
879
880                 // draw X 9x9
881                 int x = closeRect.x + Math.max(1, (closeRect.width-9)/2);
882                 int y = closeRect.y + Math.max(1, (closeRect.height-9)/2);
883                 y += parent.onBottom ? -1 : 1;
884
885                 Color closeBorder = display.getSystemColor(BUTTON_BORDER);
886                 switch (closeImageState & (SWT.HOT | SWT.SELECTED | SWT.BACKGROUND)) {
887                         case SWT.NONE: {
888                                 int[] shape = new int[] {x,y, x+2,y, x+4,y+2, x+5,y+2, x+7,y, x+9,y,
889                                                                                  x+9,y+2, x+7,y+4, x+7,y+5, x+9,y+7, x+9,y+9,
890                                                                                  x+7,y+9, x+5,y+7, x+4,y+7, x+2,y+9, x,y+9,
891                                                                                  x,y+7, x+2,y+5, x+2,y+4, x,y+2};
892                                 gc.setBackground(display.getSystemColor(BUTTON_FILL));
893                                 gc.fillPolygon(shape);
894                                 gc.setForeground(closeBorder);
895                                 gc.drawPolygon(shape);
896                                 break;
897                         }
898                         case SWT.HOT: {
899                                 int[] shape = new int[] {x,y, x+2,y, x+4,y+2, x+5,y+2, x+7,y, x+9,y,
900                                                                                  x+9,y+2, x+7,y+4, x+7,y+5, x+9,y+7, x+9,y+9,
901                                                                                  x+7,y+9, x+5,y+7, x+4,y+7, x+2,y+9, x,y+9,
902                                                                                  x,y+7, x+2,y+5, x+2,y+4, x,y+2};
903                                 gc.setBackground(getFillColor());
904                                 gc.fillPolygon(shape);
905                                 gc.setForeground(closeBorder);
906                                 gc.drawPolygon(shape);
907                                 break;
908                         }
909                         case SWT.SELECTED: {
910                                 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,
911                                                                                  x+10,y+3, x+8,y+5, x+8,y+6, x+10,y+8, x+10,y+10,
912                                                                                  x+8,y+10, x+6,y+8, x+5,y+8, x+3,y+10, x+1,y+10,
913                                                                                  x+1,y+8, x+3,y+6, x+3,y+5, x+1,y+3};
914                                 gc.setBackground(getFillColor());
915                                 gc.fillPolygon(shape);
916                                 gc.setForeground(closeBorder);
917                                 gc.drawPolygon(shape);
918                                 break;
919                         }
920                         case SWT.BACKGROUND: {
921                                 int[] shape = new int[] {x,y, x+10,y, x+10,y+10, x,y+10};
922                                 drawBackground(gc, shape, false);
923                                 break;
924                         }
925                 }
926         }
927
928         void drawChevron(GC gc, Rectangle chevronRect, int chevronImageState) {
929                 if (chevronRect.width == 0 || chevronRect.height == 0) return;
930                 int selectedIndex = parent.selectedIndex;
931                 // draw chevron (10x7)
932                 Display display = parent.getDisplay();
933                 Font font = getChevronFont(display);
934                 int fontHeight = font.getFontData()[0].getHeight();
935                 int indent = Math.max(2, (chevronRect.height - fontHeight - 4) /2);
936                 int x = chevronRect.x + 2;
937                 int y = chevronRect.y + indent;
938                 int count;
939                 int itemCount = parent.getItemCount();
940                 if (parent.single) {
941                         count = selectedIndex == -1 ? itemCount : itemCount - 1;
942                 } else {
943                         int showCount = 0;
944                         while (showCount < parent.priority.length && parent.items[parent.priority[showCount]].showing) {
945                                 showCount++;
946                         }
947                         count = itemCount - showCount;
948                 }
949                 String chevronString = count > 99 ? CHEVRON_ELLIPSIS : String.valueOf(count);
950                 switch (chevronImageState & (SWT.HOT | SWT.SELECTED)) {
951                         case SWT.NONE: {
952                                 Color chevronBorder = parent.single ? parent.getSelectionForeground() : parent.getForeground();
953                                 gc.setForeground(chevronBorder);
954                                 gc.setFont(font);
955                                 drawChevronContent(gc, x, y, chevronString);
956                                 break;
957                         }
958                         case SWT.HOT: {
959                                 gc.setForeground(display.getSystemColor(BUTTON_BORDER));
960                                 gc.setBackground(display.getSystemColor(BUTTON_FILL));
961                                 gc.setFont(font);
962                                 drawRoundRectangle(gc, chevronRect);
963                                 drawChevronContent(gc, x, y, chevronString);
964                                 break;
965                         }
966                         case SWT.SELECTED: {
967                                 gc.setForeground(display.getSystemColor(BUTTON_BORDER));
968                                 gc.setBackground(display.getSystemColor(BUTTON_FILL));
969                                 gc.setFont(font);
970                                 drawRoundRectangle(gc, chevronRect);
971                                 drawChevronContent(gc, x+1, y+1, chevronString);
972                                 break;
973                         }
974                 }
975         }
976
977         private void drawRoundRectangle(GC gc, Rectangle chevronRect) {
978                 gc.fillRoundRectangle(chevronRect.x, chevronRect.y, chevronRect.width,     chevronRect.height,     6, 6);
979                 gc.drawRoundRectangle(chevronRect.x, chevronRect.y, chevronRect.width - 1, chevronRect.height - 1, 6, 6);
980         }
981
982         private void drawChevronContent(GC gc, int x, int y, String chevronString) {
983                 gc.drawLine(x,y,     x+2,y+2);
984                 gc.drawLine(x+2,y+2, x,y+4);
985                 gc.drawLine(x+1,y,   x+3,y+2);
986                 gc.drawLine(x+3,y+2, x+1,y+4);
987                 gc.drawLine(x+4,y,   x+6,y+2);
988                 gc.drawLine(x+6,y+2, x+4,y+4);
989                 gc.drawLine(x+5,y,   x+7,y+2);
990                 gc.drawLine(x+7,y+2, x+5,y+4);
991                 gc.drawString(chevronString, x+7, y+3, true);
992         }
993
994         /*
995          * Draw a highlight effect along the left, top, and right edges of the tab.
996          * Only for curved tabs, on top.
997          * Do not draw if insufficient colors.
998          */
999         void drawHighlight(GC gc, Rectangle bounds, int state, int rightEdge) {
1000                 //only draw for curvy tabs and only draw for top tabs
1001                 if(parent.simple || parent.onBottom)
1002                         return;
1003
1004                 if(selectionHighlightGradientBegin == null)
1005                         return;
1006
1007                 Color[] gradients = selectionHighlightGradientColorsCache;
1008                 if(gradients == null)
1009                         return;
1010                 int gradientsSize = gradients.length;
1011                 if(gradientsSize == 0)
1012                         return;         //shouldn't happen but just to be tidy
1013
1014                 int x = bounds.x;
1015                 int y = bounds.y;
1016
1017                 gc.setForeground(gradients[0]);
1018
1019                 //draw top horizontal line
1020                 gc.drawLine(
1021                                 TOP_LEFT_CORNER_HILITE[0] + x + 1, //rely on fact that first pair is top/right of curve
1022                                 1 + y,
1023                                 rightEdge - curveIndent,
1024                                 1 + y);
1025
1026                 int[] leftHighlightCurve = TOP_LEFT_CORNER_HILITE;
1027
1028                 int d = parent.tabHeight - topCurveHighlightEnd.length /2;
1029
1030                 int lastX = 0;
1031                 int lastY = 0;
1032                 int lastColorIndex = 0;
1033
1034                 //draw upper left curve highlight
1035                 for (int i = 0; i < leftHighlightCurve.length /2; i++) {
1036                         int rawX = leftHighlightCurve[i * 2];
1037                         int rawY = leftHighlightCurve[i * 2 + 1];
1038                         lastX = rawX + x;
1039                         lastY = rawY + y;
1040                         lastColorIndex = rawY - 1;
1041                         gc.setForeground(gradients[lastColorIndex]);
1042                         gc.drawPoint(lastX, lastY);
1043                 }
1044                 //draw left vertical line highlight
1045                 for(int i = lastColorIndex; i < gradientsSize; i++) {
1046                         gc.setForeground(gradients[i]);
1047                         gc.drawPoint(lastX, 1 + lastY++);
1048                 }
1049
1050                 int rightEdgeOffset = rightEdge - curveIndent;
1051
1052                 //draw right swoop highlight up to diagonal portion
1053                 for (int i = 0; i < topCurveHighlightStart.length /2; i++) {
1054                         int rawX = topCurveHighlightStart[i * 2];
1055                         int rawY = topCurveHighlightStart[i * 2 + 1];
1056                         lastX = rawX + rightEdgeOffset;
1057                         lastY = rawY + y;
1058                         lastColorIndex = rawY - 1;
1059                         if(lastColorIndex >= gradientsSize)
1060                                 break;  //can happen if tabs are unusually short and cut off the curve
1061                         gc.setForeground(gradients[lastColorIndex]);
1062                         gc.drawPoint(lastX, lastY);
1063                 }
1064                 //draw right diagonal line highlight
1065                 for(int i = lastColorIndex; i < lastColorIndex + d; i++) {
1066                         if(i >= gradientsSize)
1067                                 break;  //can happen if tabs are unusually short and cut off the curve
1068                         gc.setForeground(gradients[i]);
1069                         gc.drawPoint(1 + lastX++, 1 + lastY++);
1070                 }
1071
1072                 //draw right swoop highlight from diagonal portion to end
1073                 for (int i = 0; i < topCurveHighlightEnd.length /2; i++) {
1074                         int rawX = topCurveHighlightEnd[i * 2]; //d is already encoded in this value
1075                         int rawY = topCurveHighlightEnd[i * 2 + 1]; //d already encoded
1076                         lastX = rawX + rightEdgeOffset;
1077                         lastY = rawY + y;
1078                         lastColorIndex = rawY - 1;
1079                         if(lastColorIndex >= gradientsSize)
1080                                 break;  //can happen if tabs are unusually short and cut off the curve
1081                         gc.setForeground(gradients[lastColorIndex]);
1082                         gc.drawPoint(lastX, lastY);
1083                 }
1084         }
1085
1086         /*
1087          * Draw the unselected border for the receiver on the left.
1088          *
1089          * @param gc
1090          */
1091         void drawLeftUnselectedBorder(GC gc, Rectangle bounds, int state) {
1092                 int x = bounds.x;
1093                 int y = bounds.y;
1094                 int height = bounds.height;
1095
1096                 int[] shape = null;
1097                 if (parent.onBottom) {
1098                         int[] left = parent.simple
1099                                 ? SIMPLE_UNSELECTED_INNER_CORNER
1100                                 : BOTTOM_LEFT_CORNER;
1101
1102                         shape = new int[left.length + 2];
1103                         int index = 0;
1104                         shape[index++] = x;
1105                         shape[index++] = y - 1;
1106                         for (int i = 0; i < left.length / 2; i++) {
1107                                 shape[index++] = x + left[2 * i];
1108                                 shape[index++] = y + height + left[2 * i + 1] - 1;
1109                         }
1110                 } else {
1111                         int[] left = parent.simple
1112                                 ? SIMPLE_UNSELECTED_INNER_CORNER
1113                                 : TOP_LEFT_CORNER;
1114
1115                         shape = new int[left.length + 2];
1116                         int index = 0;
1117                         shape[index++] = x;
1118                         shape[index++] = y + height;
1119                         for (int i = 0; i < left.length / 2; i++) {
1120                                 shape[index++] = x + left[2 * i];
1121                                 shape[index++] = y + left[2 * i + 1];
1122                         }
1123
1124                 }
1125
1126                 drawBorder(gc, shape);
1127         }
1128
1129         void drawMaximize(GC gc, Rectangle maxRect, int maxImageState) {
1130                 if (maxRect.width == 0 || maxRect.height == 0) return;
1131                 Display display = parent.getDisplay();
1132                 // 5x4 or 7x9
1133                 int x = maxRect.x + (maxRect.width - 10)/2;
1134                 int y = maxRect.y + 3;
1135
1136                 gc.setForeground(minMaxBorderColor);
1137                 gc.setBackground(display.getSystemColor(BUTTON_FILL));
1138
1139                 switch (maxImageState & (SWT.HOT | SWT.SELECTED)) {
1140                         case SWT.NONE: {
1141                                 if (!parent.getMaximized()) {
1142                                         gc.fillRectangle(x, y, 9, 9);
1143                                         gc.drawRectangle(x, y, 9, 9);
1144                                         gc.drawLine(x, y+2, x+9, y+2);
1145                                 } else {
1146                                         gc.fillRectangle(x, y+3, 5, 4);
1147                                         gc.fillRectangle(x+2, y, 5, 4);
1148                                         gc.drawRectangle(x, y+3, 5, 4);
1149                                         gc.drawRectangle(x+2, y, 5, 4);
1150                                         gc.drawLine(x+2, y+1, x+7, y+1);
1151                                         gc.drawLine(x, y+4, x+5, y+4);
1152                                 }
1153                                 break;
1154                         }
1155                         case SWT.HOT: {
1156                                 drawRoundRectangle(gc, maxRect);
1157                                 if (!parent.getMaximized()) {
1158                                         gc.fillRectangle(x, y, 9, 9);
1159                                         gc.drawRectangle(x, y, 9, 9);
1160                                         gc.drawLine(x, y+2, x+9, y+2);
1161                                 } else {
1162                                         gc.fillRectangle(x, y+3, 5, 4);
1163                                         gc.fillRectangle(x+2, y, 5, 4);
1164                                         gc.drawRectangle(x, y+3, 5, 4);
1165                                         gc.drawRectangle(x+2, y, 5, 4);
1166                                         gc.drawLine(x+2, y+1, x+7, y+1);
1167                                         gc.drawLine(x, y+4, x+5, y+4);
1168                                 }
1169                                 break;
1170                         }
1171                         case SWT.SELECTED: {
1172                                 drawRoundRectangle(gc, maxRect);
1173                                 if (!parent.getMaximized()) {
1174                                         gc.fillRectangle(x+1, y+1, 9, 9);
1175                                         gc.drawRectangle(x+1, y+1, 9, 9);
1176                                         gc.drawLine(x+1, y+3, x+10, y+3);
1177                                 } else {
1178                                         gc.fillRectangle(x+1, y+4, 5, 4);
1179                                         gc.fillRectangle(x+3, y+1, 5, 4);
1180                                         gc.drawRectangle(x+1, y+4, 5, 4);
1181                                         gc.drawRectangle(x+3, y+1, 5, 4);
1182                                         gc.drawLine(x+3, y+2, x+8, y+2);
1183                                         gc.drawLine(x+1, y+5, x+6, y+5);
1184                                 }
1185                                 break;
1186                         }
1187                 }
1188         }
1189         void drawMinimize(GC gc, Rectangle minRect, int minImageState) {
1190                 if (minRect.width == 0 || minRect.height == 0) return;
1191                 Display display = parent.getDisplay();
1192                 // 5x4 or 9x3
1193                 int x = minRect.x + (minRect.width - 10)/2;
1194                 int y = minRect.y + 3;
1195
1196                 gc.setForeground(minMaxBorderColor);
1197                 gc.setBackground(display.getSystemColor(BUTTON_FILL));
1198
1199                 switch (minImageState & (SWT.HOT | SWT.SELECTED)) {
1200                         case SWT.NONE: {
1201                                 if (!parent.getMinimized()) {
1202                                         gc.fillRectangle(x, y, 9, 3);
1203                                         gc.drawRectangle(x, y, 9, 3);
1204                                 } else {
1205                                         gc.fillRectangle(x, y+3, 5, 4);
1206                                         gc.fillRectangle(x+2, y, 5, 4);
1207                                         gc.drawRectangle(x, y+3, 5, 4);
1208                                         gc.drawRectangle(x+2, y, 5, 4);
1209                                         gc.drawLine(x+3, y+1, x+6, y+1);
1210                                         gc.drawLine(x+1, y+4, x+4, y+4);
1211                                 }
1212                                 break;
1213                         }
1214                         case SWT.HOT: {
1215                                 drawRoundRectangle(gc, minRect);
1216                                 if (!parent.getMinimized()) {
1217                                         gc.fillRectangle(x, y, 9, 3);
1218                                         gc.drawRectangle(x, y, 9, 3);
1219                                 } else {
1220                                         gc.fillRectangle(x, y+3, 5, 4);
1221                                         gc.fillRectangle(x+2, y, 5, 4);
1222                                         gc.drawRectangle(x, y+3, 5, 4);
1223                                         gc.drawRectangle(x+2, y, 5, 4);
1224                                         gc.drawLine(x+3, y+1, x+6, y+1);
1225                                         gc.drawLine(x+1, y+4, x+4, y+4);
1226                                 }
1227                                 break;
1228                         }
1229                         case SWT.SELECTED: {
1230                                 drawRoundRectangle(gc, minRect);
1231                                 if (!parent.getMinimized()) {
1232                                         gc.fillRectangle(x+1, y+1, 9, 3);
1233                                         gc.drawRectangle(x+1, y+1, 9, 3);
1234                                 } else {
1235                                         gc.fillRectangle(x+1, y+4, 5, 4);
1236                                         gc.fillRectangle(x+3, y+1, 5, 4);
1237                                         gc.drawRectangle(x+1, y+4, 5, 4);
1238                                         gc.drawRectangle(x+3, y+1, 5, 4);
1239                                         gc.drawLine(x+4, y+2, x+7, y+2);
1240                                         gc.drawLine(x+2, y+5, x+5, y+5);
1241                                 }
1242                                 break;
1243                         }
1244                 }
1245         }
1246
1247         /*
1248          * Draw the unselected border for the receiver on the right.
1249          *
1250          * @param gc
1251          */
1252         void drawRightUnselectedBorder(GC gc, Rectangle bounds, int state) {
1253                 int x = bounds.x;
1254                 int y = bounds.y;
1255                 int width = bounds.width;
1256                 int height = bounds.height;
1257
1258                 int[] shape = null;
1259                 int startX = x + width - 1;
1260
1261                 if (parent.onBottom) {
1262                         int[] right = parent.simple
1263                                 ? SIMPLE_UNSELECTED_INNER_CORNER
1264                                 : BOTTOM_RIGHT_CORNER;
1265
1266                         shape = new int[right.length + 2];
1267                         int index = 0;
1268
1269                         for (int i = 0; i < right.length / 2; i++) {
1270                                 shape[index++] = startX + right[2 * i];
1271                                 shape[index++] = y + height + right[2 * i + 1] - 1;
1272                         }
1273                         shape[index++] = startX;
1274                         shape[index++] = y - 1;
1275                 } else {
1276                         int[] right = parent.simple
1277                                 ? SIMPLE_UNSELECTED_INNER_CORNER
1278                                 : TOP_RIGHT_CORNER;
1279
1280                         shape = new int[right.length + 2];
1281                         int index = 0;
1282
1283                         for (int i = 0; i < right.length / 2; i++) {
1284                                 shape[index++] = startX + right[2 * i];
1285                                 shape[index++] = y + right[2 * i + 1];
1286                         }
1287
1288                         shape[index++] = startX;
1289                         shape[index++] = y + height;
1290
1291                 }
1292
1293                 drawBorder(gc, shape);
1294
1295         }
1296
1297         void drawSelected(int itemIndex, GC gc, Rectangle bounds, int state ) {
1298                 CTabItem item = parent.items[itemIndex];
1299                 int x = bounds.x;
1300                 int y = bounds.y;
1301                 int height = bounds.height;
1302                 int width = bounds.width;
1303                 if (!parent.simple && !parent.single) width -= (curveWidth - curveIndent);
1304                 int borderLeft = parent.borderVisible ? 1 : 0;
1305                 int borderRight = borderLeft;
1306                 int borderTop = parent.onBottom ? borderLeft : 0;
1307                 int borderBottom = parent.onBottom ? 0 : borderLeft;
1308
1309                 Point size = parent.getSize();
1310
1311                 int rightEdge = Math.min (x + width, parent.getRightItemEdge(gc));
1312                 //       Draw selection border across all tabs
1313
1314                 if ((state & SWT.BACKGROUND) != 0) {
1315                         int highlight_header = (parent.getStyle() & SWT.FLAT) != 0 ? 1 : 3;
1316                         int xx = borderLeft;
1317                         int yy = parent.onBottom ? size.y - borderBottom - parent.tabHeight - highlight_header : borderTop + parent.tabHeight + 1;
1318                         int ww = size.x - borderLeft - borderRight;
1319                         int hh = highlight_header - 1;
1320                         int[] shape = new int[] {xx,yy, xx+ww,yy, xx+ww,yy+hh, xx,yy+hh};
1321                         if (parent.selectionGradientColors != null && !parent.selectionGradientVertical) {
1322                                 drawBackground(gc, shape, parent.shouldHighlight());
1323                         } else {
1324                                 gc.setBackground(parent.shouldHighlight() ? parent.selectionBackground : parent.getBackground());
1325                                 gc.fillRectangle(xx, yy, ww, hh);
1326                         }
1327
1328                         if (parent.single) {
1329                                 if (!item.showing) return;
1330                         } else {
1331                                 // if selected tab scrolled out of view or partially out of view
1332                                 // just draw bottom line
1333                                 if (!item.showing){
1334                                         int x1 = Math.max(0, borderLeft - 1);
1335                                         int y1 = (parent.onBottom) ? y - 1 : y + height;
1336                                         int x2 = size.x - borderRight;
1337                                         gc.setForeground(parent.getDisplay().getSystemColor(BORDER1_COLOR));
1338                                         gc.drawLine(x1, y1, x2, y1);
1339                                         return;
1340                                 }
1341
1342                                 // draw selected tab background and outline
1343                                 shape = null;
1344                                 if (parent.onBottom) {
1345                                         int[] left = parent.simple ? SIMPLE_BOTTOM_LEFT_CORNER : BOTTOM_LEFT_CORNER;
1346                                         int[] right = parent.simple ? SIMPLE_BOTTOM_RIGHT_CORNER : curve;
1347                                         if (borderLeft == 0 && itemIndex == parent.firstIndex) {
1348                                                 left = new int[]{x, y+height};
1349                                         }
1350                                         shape = new int[left.length+right.length+8];
1351                                         int index = 0;
1352                                         shape[index++] = x; // first point repeated here because below we reuse shape to draw outline
1353                                         shape[index++] = y - 1;
1354                                         shape[index++] = x;
1355                                         shape[index++] = y - 1;
1356                                         for (int i = 0; i < left.length/2; i++) {
1357                                                 shape[index++] = x + left[2*i];
1358                                                 shape[index++] = y + height + left[2*i+1] - 1;
1359                                         }
1360                                         for (int i = 0; i < right.length/2; i++) {
1361                                                 shape[index++] = parent.simple ? rightEdge - 1 + right[2*i] : rightEdge - curveIndent + right[2*i];
1362                                                 shape[index++] = parent.simple ? y + height + right[2*i+1] - 1 : y + right[2*i+1] - 2;
1363                                         }
1364                                         shape[index++] = parent.simple ? rightEdge - 1 : rightEdge + curveWidth - curveIndent;
1365                                         shape[index++] = y - 1;
1366                                         shape[index++] = parent.simple ? rightEdge - 1 : rightEdge + curveWidth - curveIndent;
1367                                         shape[index++] = y - 1;
1368                                 } else {
1369                                         int[] left = parent.simple ? SIMPLE_TOP_LEFT_CORNER : TOP_LEFT_CORNER;
1370                                         int[] right = parent.simple ? SIMPLE_TOP_RIGHT_CORNER : curve;
1371                                         if (borderLeft == 0 && itemIndex == parent.firstIndex) {
1372                                                 left = new int[]{x, y};
1373                                         }
1374                                         shape = new int[left.length+right.length+8];
1375                                         int index = 0;
1376                                         shape[index++] = x; // first point repeated here because below we reuse shape to draw outline
1377                                         shape[index++] = y + height + 1;
1378                                         shape[index++] = x;
1379                                         shape[index++] = y + height + 1;
1380                                         for (int i = 0; i < left.length/2; i++) {
1381                                                 shape[index++] = x + left[2*i];
1382                                                 shape[index++] = y + left[2*i+1];
1383                                         }
1384                                         for (int i = 0; i < right.length/2; i++) {
1385                                                 shape[index++] = parent.simple ? rightEdge - 1 + right[2*i] : rightEdge - curveIndent + right[2*i];
1386                                                 shape[index++] = y + right[2*i+1];
1387                                         }
1388                                         shape[index++] = parent.simple ? rightEdge - 1 : rightEdge + curveWidth - curveIndent;
1389                                         shape[index++] = y + height + 1;
1390                                         shape[index++] = parent.simple ? rightEdge - 1 : rightEdge + curveWidth - curveIndent;
1391                                         shape[index++] = y + height + 1;
1392                                 }
1393
1394                                 Rectangle clipping = gc.getClipping();
1395                                 Rectangle clipBounds = item.getBounds();
1396                                 clipBounds.height += 1;
1397                                 if (parent.onBottom) clipBounds.y -= 1;
1398                                 boolean tabInPaint = clipping.intersects(clipBounds);
1399
1400                                 if (tabInPaint) {
1401                                         // fill in tab background
1402                                         if (parent.selectionGradientColors != null && !parent.selectionGradientVertical) {
1403                                                 drawBackground(gc, shape, true);
1404                                         } else {
1405                                                 Color defaultBackground = parent.shouldHighlight() ? parent.selectionBackground : parent.getBackground();
1406                                                 Image image = parent.selectionBgImage;
1407                                                 Color[] colors = parent.selectionGradientColors;
1408                                                 int[] percents = parent.selectionGradientPercents;
1409                                                 boolean vertical = parent.selectionGradientVertical;
1410                                                 xx = x;
1411                                                 yy = parent.onBottom ? y -1 : y + 1;
1412                                                 ww = width;
1413                                                 hh = height;
1414                                                 if (!parent.single && !parent.simple) ww += curveWidth - curveIndent;
1415                                                 drawBackground(gc, shape, xx, yy, ww, hh, defaultBackground, image, colors, percents, vertical);
1416                                         }
1417                                 }
1418
1419                                 //Highlight MUST be drawn before the outline so that outline can cover it in the right spots (start of swoop)
1420                                 //otherwise the curve looks jagged
1421                                 drawHighlight(gc, bounds, state, rightEdge);
1422
1423                                 // draw outline
1424                                 shape[0] = Math.max(0, borderLeft - 1);
1425                                 if (borderLeft == 0 && itemIndex == parent.firstIndex) {
1426                                         shape[1] = parent.onBottom ? y + height - 1 : y;
1427                                         shape[5] = shape[3] = shape[1];
1428                                 }
1429                                 shape[shape.length - 2] = size.x - borderRight + 1;
1430                                 for (int i = 0; i < shape.length/2; i++) {
1431                                         if (shape[2*i + 1] == y + height + 1) shape[2*i + 1] -= 1;
1432                                 }
1433                                 Color borderColor = parent.getDisplay().getSystemColor(BORDER1_COLOR);
1434                                 if (! borderColor.equals(lastBorderColor)) createAntialiasColors();
1435                                 antialias(shape, selectedInnerColor, selectedOuterColor, gc);
1436                                 gc.setForeground(borderColor);
1437                                 gc.drawPolyline(shape);
1438
1439                                 if (!tabInPaint) return;
1440                         }
1441                 }
1442
1443                 if ((state & SWT.FOREGROUND) != 0) {
1444                         // draw Image
1445                         Rectangle trim = computeTrim(itemIndex, SWT.NONE, 0, 0, 0, 0);
1446                         int xDraw = x - trim.x;
1447                         if (parent.single && (parent.showClose || item.showClose)) xDraw += item.closeRect.width;
1448                         Image image = item.getImage();
1449                         if (image != null && !image.isDisposed()) {
1450                                 Rectangle imageBounds = image.getBounds();
1451                                 // only draw image if it won't overlap with close button
1452                                 int maxImageWidth = rightEdge - xDraw - (trim.width + trim.x);
1453                                 if (!parent.single && item.closeRect.width > 0) maxImageWidth -= item.closeRect.width + INTERNAL_SPACING;
1454                                 if (imageBounds.width < maxImageWidth) {
1455                                         int imageX = xDraw;
1456                                         int imageY = y + (height - imageBounds.height) / 2;
1457                                         imageY += parent.onBottom ? -1 : 1;
1458                                         gc.drawImage(image, imageX, imageY);
1459                                         xDraw += imageBounds.width + INTERNAL_SPACING;
1460                                 }
1461                         }
1462
1463                         // draw Text
1464                         int textWidth = rightEdge - xDraw - (trim.width + trim.x);
1465                         if (!parent.single && item.closeRect.width > 0) textWidth -= item.closeRect.width + INTERNAL_SPACING;
1466                         if (textWidth > 0) {
1467                                 Font gcFont = gc.getFont();
1468                                 gc.setFont(item.font == null ? parent.getFont() : item.font);
1469
1470                                 if (item.shortenedText == null || item.shortenedTextWidth != textWidth) {
1471                                         item.shortenedText = shortenText(gc, item.getText(), textWidth);
1472                                         item.shortenedTextWidth = textWidth;
1473                                 }
1474                                 Point extent = gc.textExtent(item.shortenedText, FLAGS);
1475                                 int textY = y + (height - extent.y) / 2;
1476                                 textY += parent.onBottom ? -1 : 1;
1477
1478                                 gc.setForeground(parent.selectionForeground);
1479                                 gc.drawText(item.shortenedText, xDraw, textY, FLAGS);
1480                                 gc.setFont(gcFont);
1481
1482                                 // draw a Focus rectangle
1483                                 if (parent.isFocusControl()) {
1484                                         Display display = parent.getDisplay();
1485                                         if (parent.simple || parent.single) {
1486                                                 gc.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
1487                                                 gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
1488                                                 gc.drawFocus(xDraw-1, textY-1, extent.x+2, extent.y+2);
1489                                         } else {
1490                                                 gc.setForeground(display.getSystemColor(BUTTON_BORDER));
1491                                                 gc.drawLine(xDraw, textY+extent.y+1, xDraw+extent.x+1, textY+extent.y+1);
1492                                         }
1493                                 }
1494                         }
1495                         if (parent.showClose || item.showClose) drawClose(gc, item.closeRect, item.closeImageState);
1496                 }
1497         }
1498
1499         void drawTabArea(GC gc, Rectangle bounds, int state) {
1500                 Point size = parent.getSize();
1501                 int[] shape = null;
1502                 Color borderColor = parent.getDisplay().getSystemColor(BORDER1_COLOR);
1503                 int tabHeight = parent.tabHeight;
1504                 int style = parent.getStyle();
1505
1506                 int borderLeft = parent.borderVisible ? 1 : 0;
1507                 int borderRight = borderLeft;
1508                 int borderTop = parent.onBottom ? borderLeft : 0;
1509                 int borderBottom = parent.onBottom ? 0 : borderLeft;
1510
1511                 int selectedIndex = parent.selectedIndex;
1512                 int highlight_header = (style & SWT.FLAT) != 0 ? 1 : 3;
1513                 if (tabHeight == 0) {
1514                         if ((style & SWT.FLAT) != 0 && (style & SWT.BORDER) == 0) return;
1515                         int x1 = borderLeft - 1;
1516                         int x2 = size.x - borderRight;
1517                         int y1 = parent.onBottom ? size.y - borderBottom - highlight_header - 1 : borderTop + highlight_header;
1518                         int y2 = parent.onBottom ? size.y - borderBottom : borderTop;
1519                         if (borderLeft > 0 && parent.onBottom) y2 -= 1;
1520
1521                         shape = new int[] {x1, y1, x1,y2, x2,y2, x2,y1};
1522
1523                         // If horizontal gradient, show gradient across the whole area
1524                         if (selectedIndex != -1 && parent.selectionGradientColors != null && parent.selectionGradientColors.length > 1 && !parent.selectionGradientVertical) {
1525                                 drawBackground(gc, shape, true);
1526                         } else if (selectedIndex == -1 && parent.gradientColors != null && parent.gradientColors.length > 1 && !parent.gradientVertical) {
1527                                 drawBackground(gc, shape, false);
1528                         } else {
1529                                 gc.setBackground(selectedIndex == -1 ? parent.getBackground() : parent.selectionBackground);
1530                                 gc.fillPolygon(shape);
1531                         }
1532
1533                         //draw 1 pixel border
1534                         if (borderLeft > 0) {
1535                                 gc.setForeground(borderColor);
1536                                 gc.drawPolyline(shape);
1537                         }
1538                         return;
1539                 }
1540
1541                 int x = Math.max(0, borderLeft - 1);
1542                 int y = parent.onBottom ? size.y - borderBottom - tabHeight : borderTop;
1543                 int width = size.x - borderLeft - borderRight + 1;
1544                 int height = tabHeight - 1;
1545                 boolean simple = parent.simple;
1546                 // Draw Tab Header
1547                 if (parent.onBottom) {
1548                         int[] left, right;
1549                         if ((style & SWT.BORDER) != 0) {
1550                                 left = simple ? SIMPLE_BOTTOM_LEFT_CORNER : BOTTOM_LEFT_CORNER;
1551                                 right = simple ? SIMPLE_BOTTOM_RIGHT_CORNER : BOTTOM_RIGHT_CORNER;
1552                         } else {
1553                                 left = simple ? SIMPLE_BOTTOM_LEFT_CORNER_BORDERLESS : BOTTOM_LEFT_CORNER_BORDERLESS;
1554                                 right = simple ? SIMPLE_BOTTOM_RIGHT_CORNER_BORDERLESS : BOTTOM_RIGHT_CORNER_BORDERLESS;
1555                         }
1556                         shape = new int[left.length + right.length + 4];
1557                         int index = 0;
1558                         shape[index++] = x;
1559                         shape[index++] = y-highlight_header;
1560                         for (int i = 0; i < left.length/2; i++) {
1561                                 shape[index++] = x+left[2*i];
1562                                 shape[index++] = y+height+left[2*i+1];
1563                                 if (borderLeft == 0) shape[index-1] += 1;
1564                         }
1565                         for (int i = 0; i < right.length/2; i++) {
1566                                 shape[index++] = x+width+right[2*i];
1567                                 shape[index++] = y+height+right[2*i+1];
1568                                 if (borderLeft == 0) shape[index-1] += 1;
1569                         }
1570                         shape[index++] = x+width;
1571                         shape[index++] = y-highlight_header;
1572                 } else {
1573                         int[] left, right;
1574                         if ((style & SWT.BORDER) != 0) {
1575                                 left = simple ? SIMPLE_TOP_LEFT_CORNER : TOP_LEFT_CORNER;
1576                                 right = simple ? SIMPLE_TOP_RIGHT_CORNER : TOP_RIGHT_CORNER;
1577                         } else {
1578                                 left = simple ? SIMPLE_TOP_LEFT_CORNER_BORDERLESS : TOP_LEFT_CORNER_BORDERLESS;
1579                                 right = simple ? SIMPLE_TOP_RIGHT_CORNER_BORDERLESS : TOP_RIGHT_CORNER_BORDERLESS;
1580                         }
1581                         shape = new int[left.length + right.length + 4];
1582                         int index = 0;
1583                         shape[index++] = x;
1584                         shape[index++] = y+height+highlight_header + 1;
1585                         for (int i = 0; i < left.length/2; i++) {
1586                                 shape[index++] = x+left[2*i];
1587                                 shape[index++] = y+left[2*i+1];
1588                         }
1589                         for (int i = 0; i < right.length/2; i++) {
1590                                 shape[index++] = x+width+right[2*i];
1591                                 shape[index++] = y+right[2*i+1];
1592                         }
1593                         shape[index++] = x+width;
1594                         shape[index++] = y+height+highlight_header + 1;
1595                 }
1596                 // Fill in background
1597                 boolean single = parent.single;
1598                 boolean bkSelected = single && selectedIndex != -1;
1599                 drawBackground(gc, shape, bkSelected);
1600                 // Fill in parent background for non-rectangular shape
1601                 Region r = new Region();
1602                 r.add(new Rectangle(x, y, width + 1, height + 1));
1603                 r.subtract(shape);
1604                 gc.setBackground(parent.getParent().getBackground());
1605                 fillRegion(gc, r);
1606                 r.dispose();
1607
1608                 // Draw selected tab
1609                 if (selectedIndex == -1) {
1610                         // if no selected tab - draw line across bottom of all tabs
1611                         int x1 = borderLeft;
1612                         int y1 = (parent.onBottom) ? size.y - borderBottom - tabHeight - 1 : borderTop + tabHeight;
1613                         int x2 = size.x - borderRight;
1614                         gc.setForeground(borderColor);
1615                         gc.drawLine(x1, y1, x2, y1);
1616                 }
1617
1618                 // Draw border line
1619                 if (borderLeft > 0) {
1620                         if (! borderColor.equals(lastBorderColor)) createAntialiasColors();
1621                         antialias(shape, null, tabAreaColor, gc);
1622                         gc.setForeground(borderColor);
1623                         gc.drawPolyline(shape);
1624                 }
1625         }
1626
1627         void drawUnselected(int index, GC gc, Rectangle bounds, int state) {
1628                 CTabItem item = parent.items[index];
1629                 int x = bounds.x;
1630                 int y = bounds.y;
1631                 int height = bounds.height;
1632                 int width = bounds.width;
1633
1634                 // Do not draw partial items
1635                 if (!item.showing) return;
1636
1637                 Rectangle clipping = gc.getClipping();
1638                 if (!clipping.intersects(bounds)) return;
1639
1640                 if ((state & SWT.BACKGROUND) != 0) {
1641                         if (index > 0 && index < parent.selectedIndex)
1642                                 drawLeftUnselectedBorder(gc, bounds, state);
1643                         // If it is the last one then draw a line
1644                         if (index > parent.selectedIndex)
1645                                 drawRightUnselectedBorder(gc, bounds, state);
1646                 }
1647
1648                 if ((state & SWT.FOREGROUND) != 0) {
1649                         // draw Image
1650                         Rectangle trim = computeTrim(index, SWT.NONE, 0, 0, 0, 0);
1651                         int xDraw = x - trim.x;
1652                         Image image = item.getImage();
1653                         if (image != null && !image.isDisposed() && parent.showUnselectedImage) {
1654                                 Rectangle imageBounds = image.getBounds();
1655                                 // only draw image if it won't overlap with close button
1656                                 int maxImageWidth = x + width - xDraw - (trim.width + trim.x);
1657                                 if (parent.showUnselectedClose && (parent.showClose || item.showClose)) {
1658                                         maxImageWidth -= item.closeRect.width + INTERNAL_SPACING;
1659                                 }
1660                                 if (imageBounds.width < maxImageWidth) {
1661                                         int imageX = xDraw;
1662                                         int imageHeight = imageBounds.height;
1663                                         int imageY = y + (height - imageHeight) / 2;
1664                                         imageY += parent.onBottom ? -1 : 1;
1665                                         int imageWidth = imageBounds.width * imageHeight / imageBounds.height;
1666                                         gc.drawImage(image,
1667                                                                  imageBounds.x, imageBounds.y, imageBounds.width, imageBounds.height,
1668                                                                  imageX, imageY, imageWidth, imageHeight);
1669                                         xDraw += imageWidth + INTERNAL_SPACING;
1670                                 }
1671                         }
1672                         // draw Text
1673                         int textWidth = x + width - xDraw - (trim.width + trim.x);
1674                         if (parent.showUnselectedClose && (parent.showClose || item.showClose)) {
1675                                 textWidth -= item.closeRect.width + INTERNAL_SPACING;
1676                         }
1677                         if (textWidth > 0) {
1678                                 Font gcFont = gc.getFont();
1679                                 gc.setFont(item.font == null ? parent.getFont() : item.font);
1680                                 if (item.shortenedText == null || item.shortenedTextWidth != textWidth) {
1681                                         item.shortenedText = shortenText(gc, item.getText(), textWidth);
1682                                         item.shortenedTextWidth = textWidth;
1683                                 }
1684                                 Point extent = gc.textExtent(item.shortenedText, FLAGS);
1685                                 int textY = y + (height - extent.y) / 2;
1686                                 textY += parent.onBottom ? -1 : 1;
1687                                 gc.setForeground(parent.getForeground());
1688                                 gc.drawText(item.shortenedText, xDraw, textY, FLAGS);
1689                                 gc.setFont(gcFont);
1690                         }
1691                         // draw close
1692                         if (parent.showUnselectedClose && (parent.showClose || item.showClose)) drawClose(gc, item.closeRect, item.closeImageState);
1693                 }
1694         }
1695
1696         void fillRegion(GC gc, Region region) {
1697                 // NOTE: region passed in to this function will be modified
1698                 Region clipping = new Region();
1699                 gc.getClipping(clipping);
1700                 region.intersect(clipping);
1701                 gc.setClipping(region);
1702                 gc.fillRectangle(region.getBounds());
1703                 gc.setClipping(clipping);
1704                 clipping.dispose();
1705         }
1706
1707         Color getFillColor() {
1708                 if (fillColor == null) {
1709                         fillColor = new Color(parent.getDisplay(), CLOSE_FILL);
1710                 }
1711                 return fillColor;
1712         }
1713
1714         private Font getChevronFont(Display display) {
1715                 if (chevronFont == null) {
1716                         Point dpi = display.getDPI();
1717                         int fontHeight = 72 * CHEVRON_FONT_HEIGHT / dpi.y;
1718                         FontData fd = parent.getFont().getFontData()[0];
1719                         fd.setHeight(fontHeight);
1720                         chevronFont = new Font(display, fd);
1721                 }
1722                 return chevronFont;
1723         }
1724
1725         /*
1726          * Return true if given start color, the cache of highlight colors we have
1727          * would match the highlight colors we'd compute.
1728          */
1729         boolean isSelectionHighlightColorsCacheHit(Color start) {
1730
1731                 if(selectionHighlightGradientColorsCache == null)
1732                         return false;
1733
1734                 //this case should never happen but check to be safe before accessing array indexes
1735                 if(selectionHighlightGradientColorsCache.length < 2)
1736                         return false;
1737
1738                 Color highlightBegin = selectionHighlightGradientColorsCache[0];
1739                 Color highlightEnd = selectionHighlightGradientColorsCache[selectionHighlightGradientColorsCache.length - 1];
1740
1741                 if(! highlightBegin.equals(start))
1742                         return false;
1743
1744                 //Compare number of colours we have vs. we'd compute
1745                 if(selectionHighlightGradientColorsCache.length != parent.tabHeight)
1746                         return false;
1747
1748                 //Compare existing highlight end to what it would be (selectionBackground)
1749                 if(! highlightEnd.equals(parent.selectionBackground))
1750                         return false;
1751
1752                 return true;
1753         }
1754
1755         void setSelectionHighlightGradientColor(Color start) {
1756                 //
1757                 //Set to null to match all the early return cases.
1758                 //For early returns, don't realloc the cache, we may get a cache hit next time we're given the highlight
1759                 selectionHighlightGradientBegin = null;
1760
1761                 if(start == null)
1762                         return;
1763
1764                 //don't bother on low colour
1765                 if (parent.getDisplay().getDepth() < 15)
1766                         return;
1767
1768                 //don't bother if we don't have a background gradient
1769                 if(parent.selectionGradientColors.length < 2)
1770                         return;
1771
1772                 //OK we know its a valid gradient now
1773                 selectionHighlightGradientBegin = start;
1774
1775                 if(! isSelectionHighlightColorsCacheHit(start))
1776                         createSelectionHighlightGradientColors(start);  //if no cache hit then compute new ones
1777         }
1778
1779         String shortenText(GC gc, String text, int width) {
1780                 return useEllipses()
1781                         ? shortenText(gc, text, width, ELLIPSIS)
1782                         : shortenText(gc, text, width, ""); //$NON-NLS-1$
1783         }
1784
1785         String shortenText(GC gc, String text, int width, String ellipses) {
1786                 if (gc.textExtent(text, FLAGS).x <= width) return text;
1787                 int ellipseWidth = gc.textExtent(ellipses, FLAGS).x;
1788                 int length = text.length();
1789                 TextLayout layout = new TextLayout(parent.getDisplay());
1790                 layout.setText(text);
1791                 int end = layout.getPreviousOffset(length, SWT.MOVEMENT_CLUSTER);
1792                 while (end > 0) {
1793                         text = text.substring(0, end);
1794                         int l = gc.textExtent(text, FLAGS).x;
1795                         if (l + ellipseWidth <= width) {
1796                                 break;
1797                         }
1798                         end = layout.getPreviousOffset(end, SWT.MOVEMENT_CLUSTER);
1799                 }
1800                 layout.dispose();
1801                 return end == 0 ? text.substring(0, 1) : text + ellipses;
1802         }
1803
1804         void updateCurves () {
1805                 //Temp fix for Bug 384743
1806                 if (this.getClass().getName().equals("org.eclipse.e4.ui.workbench.renderers.swt.CTabRendering")) return;
1807                 int tabHeight = parent.tabHeight;
1808                 if (tabHeight == lastTabHeight) return;
1809                 if (parent.onBottom) {
1810                         int d = tabHeight - 12;
1811                         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,
1812                                                                 11,7+d, 11+d,7,
1813                                                                 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};
1814                         curveWidth = 26+d;
1815                         curveIndent = curveWidth/3;
1816                 } else {
1817                         int d = tabHeight - 12;
1818                         curve = new int[]{0,0, 0,1, 2,1, 3,2, 5,2, 6,3, 7,3, 9,5, 10,5,
1819                                                                 11,6, 11+d,6+d,
1820                                                                 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};
1821                         curveWidth = 26+d;
1822                         curveIndent = curveWidth/3;
1823
1824                         //this could be static but since values depend on curve, better to keep in one place
1825                         topCurveHighlightStart = new int[] {
1826                                         0, 2,  1, 2,  2, 2,
1827                                         3, 3,  4, 3,  5, 3,
1828                                         6, 4,  7, 4,
1829                                         8, 5,
1830                                         9, 6, 10, 6};
1831
1832                         //also, by adding in 'd' here we save some math cost when drawing the curve
1833                         topCurveHighlightEnd = new int[] {
1834                                         10+d, 6+d,
1835                                         11+d, 7+d,
1836                                         12+d, 8+d,  13+d, 8+d,
1837                                         14+d, 9+d,
1838                                         15+d, 10+d,  16+d, 10+d,
1839                                         17+d, 11+d,  18+d, 11+d,  19+d, 11+d,
1840                                         20+d, 12+d,  21+d, 12+d,  22+d,  12+d };
1841                 }
1842         }
1843
1844         /*
1845          * Return whether to use ellipses or just truncate labels
1846          */
1847         boolean useEllipses() {
1848                 return parent.simple;
1849         }
1850 }