KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > swt > custom > CTabFolder


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.swt.custom;
12
13 import org.eclipse.swt.*;
14 import org.eclipse.swt.accessibility.*;
15 import org.eclipse.swt.events.*;
16 import org.eclipse.swt.graphics.*;
17 import org.eclipse.swt.widgets.*;
18
19 /**
20  *
21  * Instances of this class implement the notebook user interface
22  * metaphor. It allows the user to select a notebook page from
23  * set of pages.
24  * <p>
25  * The item children that may be added to instances of this class
26  * must be of type <code>CTabItem</code>.
27  * <code>Control</code> children are created and then set into a
28  * tab item using <code>CTabItem#setControl</code>.
29  * </p><p>
30  * Note that although this class is a subclass of <code>Composite</code>,
31  * it does not make sense to set a layout on it.
32  * </p><p>
33  * <dl>
34  * <dt><b>Styles:</b></dt>
35  * <dd>CLOSE, TOP, BOTTOM, FLAT, BORDER, SINGLE, MULTI</dd>
36  * <dt><b>Events:</b></dt>
37  * <dd>Selection</dd>
38  * <dd>"CTabFolder2"</dd>
39  * </dl>
40  * <p>
41  * Note: Only one of the styles TOP and BOTTOM
42  * may be specified.
43  * </p><p>
44  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
45  * </p>
46  */

47  
48 public class CTabFolder extends Composite {
49     
50     /**
51      * marginWidth specifies the number of pixels of horizontal margin
52      * that will be placed along the left and right edges of the form.
53      *
54      * The default value is 0.
55      */

56     public int marginWidth = 0;
57     /**
58      * marginHeight specifies the number of pixels of vertical margin
59      * that will be placed along the top and bottom edges of the form.
60      *
61      * The default value is 0.
62      */

63     public int marginHeight = 0;
64     
65     /**
66      * A multiple of the tab height that specifies the minimum width to which a tab
67      * will be compressed before scrolling arrows are used to navigate the tabs.
68      *
69      * NOTE This field is badly named and can not be fixed for backwards compatibility.
70      * It should not be capitalized.
71      *
72      * @deprecated This field is no longer used. See setMinimumCharacters(int)
73      */

74     public int MIN_TAB_WIDTH = 4;
75     
76     /**
77      * Color of innermost line of drop shadow border.
78      *
79      * NOTE This field is badly named and can not be fixed for backwards compatibility.
80      * It should be capitalized.
81      *
82      * @deprecated drop shadow border is no longer drawn in 3.0
83      */

84     public static RGB borderInsideRGB = new RGB (132, 130, 132);
85     /**
86      * Color of middle line of drop shadow border.
87      *
88      * NOTE This field is badly named and can not be fixed for backwards compatibility.
89      * It should be capitalized.
90      *
91      * @deprecated drop shadow border is no longer drawn in 3.0
92      */

93     public static RGB borderMiddleRGB = new RGB (143, 141, 138);
94     /**
95      * Color of outermost line of drop shadow border.
96      *
97      * NOTE This field is badly named and can not be fixed for backwards compatibility.
98      * It should be capitalized.
99      *
100      * @deprecated drop shadow border is no longer drawn in 3.0
101      */

102     public static RGB borderOutsideRGB = new RGB (171, 168, 165);
103
104     /* sizing, positioning */
105     int xClient, yClient;
106     boolean onBottom = false;
107     boolean single = false;
108     boolean simple = true;
109     int fixedTabHeight = SWT.DEFAULT;
110     int tabHeight;
111     int minChars = 20;
112     
113     /* item management */
114     CTabItem items[] = new CTabItem[0];
115     int firstIndex = -1; // index of the left most visible tab.
116
int selectedIndex = -1;
117     int[] priority = new int[0];
118     boolean mru = false;
119     Listener listener;
120     
121     /* External Listener management */
122     CTabFolder2Listener[] folderListeners = new CTabFolder2Listener[0];
123     // support for deprecated listener mechanism
124
CTabFolderListener[] tabListeners = new CTabFolderListener[0];
125     
126     /* Selected item appearance */
127     Image selectionBgImage;
128     Color[] selectionGradientColors;
129     int[] selectionGradientPercents;
130     boolean selectionGradientVertical;
131     Color selectionForeground;
132     Color selectionBackground; //selection fade end
133
Color selectionFadeStart;
134     
135     Color selectionHighlightGradientBegin = null; //null == no highlight
136
//Although we are given new colours all the time to show different states (active, etc),
137
//some of which may have a highlight and some not, we'd like to retain the highlight colours
138
//as a cache so that we can reuse them if we're again told to show the highlight.
139
//We are relying on the fact that only one tab state usually gets a highlight, so only
140
//a single cache is required. If that happens to not be true, cache simply becomes less effective,
141
//but we don't leak colours.
142
Color[] selectionHighlightGradientColorsCache = null; //null is a legal value, check on access
143

144     /* Unselected item appearance */
145     Image bgImage;
146     Color[] gradientColors;
147     int[] gradientPercents;
148     boolean gradientVertical;
149     boolean showUnselectedImage = true;
150     
151     static Color borderColor;
152     
153     // close, min/max and chevron buttons
154
boolean showClose = false;
155     boolean showUnselectedClose = true;
156     
157     Rectangle chevronRect = new Rectangle(0, 0, 0, 0);
158     int chevronImageState = NORMAL;
159     boolean showChevron = false;
160     Menu showMenu;
161     
162     boolean showMin = false;
163     Rectangle minRect = new Rectangle(0, 0, 0, 0);
164     boolean minimized = false;
165     int minImageState = NORMAL;
166     
167     boolean showMax = false;
168     Rectangle maxRect = new Rectangle(0, 0, 0, 0);
169     boolean maximized = false;
170     int maxImageState = NORMAL;
171     
172     Control topRight;
173     Rectangle topRightRect = new Rectangle(0, 0, 0, 0);
174     int topRightAlignment = SWT.RIGHT;
175     
176     // borders and shapes
177
int borderLeft = 0;
178     int borderRight = 0;
179     int borderTop = 0;
180     int borderBottom = 0;
181     
182     int highlight_margin = 0;
183     int highlight_header = 0;
184     
185     int[] curve;
186     int[] topCurveHighlightStart;
187     int[] topCurveHighlightEnd;
188     int curveWidth = 0;
189     int curveIndent = 0;
190     
191     // when disposing CTabFolder, don't try to layout the items or
192
// change the selection as each child is destroyed.
193
boolean inDispose = false;
194
195     // keep track of size changes in order to redraw only affected area
196
// on Resize
197
Point oldSize;
198     Font oldFont;
199     
200     // internal constants
201
static final int DEFAULT_WIDTH = 64;
202     static final int DEFAULT_HEIGHT = 64;
203     static final int BUTTON_SIZE = 18;
204
205     static final int[] TOP_LEFT_CORNER = new int[] {0,6, 1,5, 1,4, 4,1, 5,1, 6,0};
206
207     //TOP_LEFT_CORNER_HILITE is laid out in reverse (ie. top to bottom)
208
//so can fade in same direction as right swoop curve
209
static final int[] TOP_LEFT_CORNER_HILITE = new int[] {5,2, 4,2, 3,3, 2,4, 2,5, 1,6};
210
211     static final int[] TOP_RIGHT_CORNER = new int[] {-6,0, -5,1, -4,1, -1,4, -1,5, 0,6};
212     static final int[] BOTTOM_LEFT_CORNER = new int[] {0,-6, 1,-5, 1,-4, 4,-1, 5,-1, 6,0};
213     static final int[] BOTTOM_RIGHT_CORNER = new int[] {-6,0, -5,-1, -4,-1, -1,-4, -1,-5, 0,-6};
214
215     static final int[] SIMPLE_TOP_LEFT_CORNER = new int[] {0,2, 1,1, 2,0};
216     static final int[] SIMPLE_TOP_RIGHT_CORNER = new int[] {-2,0, -1,1, 0,2};
217     static final int[] SIMPLE_BOTTOM_LEFT_CORNER = new int[] {0,-2, 1,-1, 2,0};
218     static final int[] SIMPLE_BOTTOM_RIGHT_CORNER = new int[] {-2,0, -1,-1, 0,-2};
219     static final int[] SIMPLE_UNSELECTED_INNER_CORNER = new int[] {0,0};
220
221     static final int[] TOP_LEFT_CORNER_BORDERLESS = new int[] {0,6, 1,5, 1,4, 4,1, 5,1, 6,0};
222     static final int[] TOP_RIGHT_CORNER_BORDERLESS = new int[] {-7,0, -6,1, -5,1, -2,4, -2,5, -1,6};
223     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};
224     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};
225
226     static final int[] SIMPLE_TOP_LEFT_CORNER_BORDERLESS = new int[] {0,2, 1,1, 2,0};
227     static final int[] SIMPLE_TOP_RIGHT_CORNER_BORDERLESS= new int[] {-3,0, -2,1, -1,2};
228     static final int[] SIMPLE_BOTTOM_LEFT_CORNER_BORDERLESS = new int[] {0,-3, 1,-2, 2,-1, 3,0};
229     static final int[] SIMPLE_BOTTOM_RIGHT_CORNER_BORDERLESS = new int[] {-4,0, -3,-1, -2,-2, -1,-3};
230
231     static final int SELECTION_FOREGROUND = SWT.COLOR_LIST_FOREGROUND;
232     static final int SELECTION_BACKGROUND = SWT.COLOR_LIST_BACKGROUND;
233     static final int BORDER1_COLOR = SWT.COLOR_WIDGET_NORMAL_SHADOW;
234     static final int FOREGROUND = SWT.COLOR_WIDGET_FOREGROUND;
235     static final int BACKGROUND = SWT.COLOR_WIDGET_BACKGROUND;
236     static final int BUTTON_BORDER = SWT.COLOR_WIDGET_DARK_SHADOW;
237     static final int BUTTON_FILL = SWT.COLOR_LIST_BACKGROUND;
238     
239     static final int NONE = 0;
240     static final int NORMAL = 1;
241     static final int HOT = 2;
242     static final int SELECTED = 3;
243     static final RGB CLOSE_FILL = new RGB(252, 160, 160);
244     
245     static final int CHEVRON_CHILD_ID = 0;
246     static final int MINIMIZE_CHILD_ID = 1;
247     static final int MAXIMIZE_CHILD_ID = 2;
248     static final int EXTRA_CHILD_ID_COUNT = 3;
249     
250
251 /**
252  * Constructs a new instance of this class given its parent
253  * and a style value describing its behavior and appearance.
254  * <p>
255  * The style value is either one of the style constants defined in
256  * class <code>SWT</code> which is applicable to instances of this
257  * class, or must be built by <em>bitwise OR</em>'ing together
258  * (that is, using the <code>int</code> "|" operator) two or more
259  * of those <code>SWT</code> style constants. The class description
260  * lists the style constants that are applicable to the class.
261  * Style bits are also inherited from superclasses.
262  * </p>
263  *
264  * @param parent a widget which will be the parent of the new instance (cannot be null)
265  * @param style the style of widget to construct
266  *
267  * @exception IllegalArgumentException <ul>
268  * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
269  * </ul>
270  * @exception SWTException <ul>
271  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
272  * </ul>
273  *
274  * @see SWT#TOP
275  * @see SWT#BOTTOM
276  * @see SWT#FLAT
277  * @see SWT#BORDER
278  * @see SWT#SINGLE
279  * @see SWT#MULTI
280  * @see #getStyle()
281  */

282 public CTabFolder(Composite parent, int style) {
283     super(parent, checkStyle (parent, style));
284     super.setLayout(new CTabFolderLayout());
285     int style2 = super.getStyle();
286     oldFont = getFont();
287     onBottom = (style2 & SWT.BOTTOM) != 0;
288     showClose = (style2 & SWT.CLOSE) != 0;
289 // showMin = (style2 & SWT.MIN) != 0; - conflicts with SWT.TOP
290
// showMax = (style2 & SWT.MAX) != 0; - conflicts with SWT.BOTTOM
291
single = (style2 & SWT.SINGLE) != 0;
292     borderLeft = borderRight = (style & SWT.BORDER) != 0 ? 1 : 0;
293     borderTop = onBottom ? borderLeft : 0;
294     borderBottom = onBottom ? 0 : borderLeft;
295     highlight_header = (style & SWT.FLAT) != 0 ? 1 : 3;
296     highlight_margin = (style & SWT.FLAT) != 0 ? 0 : 2;
297     //set up default colors
298
Display display = getDisplay();
299     selectionForeground = display.getSystemColor(SELECTION_FOREGROUND);
300     selectionBackground = display.getSystemColor(SELECTION_BACKGROUND);
301     borderColor = display.getSystemColor(BORDER1_COLOR);
302     updateTabHeight(false);
303     
304     initAccessible();
305     
306     // Add all listeners
307
listener = new Listener() {
308         public void handleEvent(Event event) {
309             switch (event.type) {
310                 case SWT.Dispose: onDispose(event); break;
311                 case SWT.DragDetect: onDragDetect(event); break;
312                 case SWT.FocusIn: onFocus(event); break;
313                 case SWT.FocusOut: onFocus(event); break;
314                 case SWT.KeyDown: onKeyDown(event); break;
315                 case SWT.MouseDoubleClick: onMouseDoubleClick(event); break;
316                 case SWT.MouseDown: onMouse(event); break;
317                 case SWT.MouseEnter: onMouse(event); break;
318                 case SWT.MouseExit: onMouse(event); break;
319                 case SWT.MouseMove: onMouse(event); break;
320                 case SWT.MouseUp: onMouse(event); break;
321                 case SWT.Paint: onPaint(event); break;
322                 case SWT.Resize: onResize(); break;
323                 case SWT.Traverse: onTraverse(event); break;
324             }
325         }
326     };
327
328     int[] folderEvents = new int[]{
329         SWT.Dispose,
330         SWT.DragDetect,
331         SWT.FocusIn,
332         SWT.FocusOut,
333         SWT.KeyDown,
334         SWT.MouseDoubleClick,
335         SWT.MouseDown,
336         SWT.MouseEnter,
337         SWT.MouseExit,
338         SWT.MouseMove,
339         SWT.MouseUp,
340         SWT.Paint,
341         SWT.Resize,
342         SWT.Traverse,
343     };
344     for (int i = 0; i < folderEvents.length; i++) {
345         addListener(folderEvents[i], listener);
346     }
347 }
348 static int checkStyle (Composite parent, int style) {
349     int mask = SWT.CLOSE | SWT.TOP | SWT.BOTTOM | SWT.FLAT | SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT | SWT.SINGLE | SWT.MULTI;
350     style = style & mask;
351     // TOP and BOTTOM are mutually exclusive.
352
// TOP is the default
353
if ((style & SWT.TOP) != 0) style = style & ~SWT.BOTTOM;
354     // SINGLE and MULTI are mutually exclusive.
355
// MULTI is the default
356
if ((style & SWT.MULTI) != 0) style = style & ~SWT.SINGLE;
357     // reduce the flash by not redrawing the entire area on a Resize event
358
style |= SWT.NO_REDRAW_RESIZE;
359     //TEMPORARY CODE
360
/*
361      * The default background on carbon and some GTK themes is not a solid color
362      * but a texture. To show the correct default background, we must allow
363      * the operating system to draw it and therefore, we can not use the
364      * NO_BACKGROUND style. The NO_BACKGROUND style is not required on platforms
365      * that use double buffering which is true in both of these cases.
366      */

367     String JavaDoc platform = SWT.getPlatform();
368     if ("carbon".equals(platform) || "gtk".equals(platform)) return style; //$NON-NLS-1$ //$NON-NLS-2$
369

370     //TEMPORARY CODE
371
/*
372      * In Right To Left orientation on Windows, all GC calls that use a brush are drawing
373      * offset by one pixel. This results in some parts of the CTabFolder not drawing correctly.
374      * To alleviate some of the appearance problems, allow the OS to draw the background.
375      * This does not draw correctly but the result is less obviously wrong.
376      */

377     if ((style & SWT.RIGHT_TO_LEFT) != 0) return style;
378     if ((parent.getStyle() & SWT.MIRRORED) != 0 && (style & SWT.LEFT_TO_RIGHT) == 0) return style;
379     
380     return style | SWT.NO_BACKGROUND;
381 }
382 static void fillRegion(GC gc, Region region) {
383     // NOTE: region passed in to this function will be modified
384
Region clipping = new Region();
385     gc.getClipping(clipping);
386     region.intersect(clipping);
387     gc.setClipping(region);
388     gc.fillRectangle(region.getBounds());
389     gc.setClipping(clipping);
390     clipping.dispose();
391 }
392 /**
393  *
394  * Adds the listener to the collection of listeners who will
395  * be notified when a tab item is closed, minimized, maximized,
396  * restored, or to show the list of items that are not
397  * currently visible.
398  *
399  * @param listener the listener which should be notified
400  *
401  * @exception IllegalArgumentException <ul>
402  * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
403  * </ul>
404  *
405  * @exception SWTException <ul>
406  * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
407  * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
408  * </ul>
409  *
410  * @see CTabFolder2Listener
411  * @see #removeCTabFolder2Listener(CTabFolder2Listener)
412  *
413  * @since 3.0
414  */

415 public void addCTabFolder2Listener(CTabFolder2Listener listener) {
416     checkWidget();
417     if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
418     // add to array
419
CTabFolder2Listener[] newListeners = new CTabFolder2Listener[folderListeners.length + 1];
420     System.arraycopy(folderListeners, 0, newListeners, 0, folderListeners.length);
421     folderListeners = newListeners;
422     folderListeners[folderListeners.length - 1] = listener;
423 }
424 /**
425  * Adds the listener to the collection of listeners who will
426  * be notified when a tab item is closed.
427  *
428  * @param listener the listener which should be notified
429  *
430  * @exception IllegalArgumentException <ul>
431  * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
432  * </ul>
433  * @exception SWTException <ul>
434  * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
435  * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
436  * </ul>
437  *
438  * @see CTabFolderListener
439  * @see #removeCTabFolderListener(CTabFolderListener)
440  *
441  * @deprecated use addCTabFolder2Listener(CTabFolder2Listener)
442  */

443 public void addCTabFolderListener(CTabFolderListener listener) {
444     checkWidget();
445     if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
446     // add to array
447
CTabFolderListener[] newTabListeners = new CTabFolderListener[tabListeners.length + 1];
448     System.arraycopy(tabListeners, 0, newTabListeners, 0, tabListeners.length);
449     tabListeners = newTabListeners;
450     tabListeners[tabListeners.length - 1] = listener;
451     // display close button to be backwards compatible
452
if (!showClose) {
453         showClose = true;
454         updateItems();
455         redraw();
456     }
457 }
458 /**
459  * Adds the listener to the collection of listeners who will
460  * be notified when the user changes the receiver's selection, by sending
461  * it one of the messages defined in the <code>SelectionListener</code>
462  * interface.
463  * <p>
464  * <code>widgetSelected</code> is called when the user changes the selected tab.
465  * <code>widgetDefaultSelected</code> is not called.
466  * </p>
467  *
468  * @param listener the listener which should be notified when the user changes the receiver's selection
469  *
470  * @exception IllegalArgumentException <ul>
471  * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
472  * </ul>
473  * @exception SWTException <ul>
474  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
475  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
476  * </ul>
477  *
478  * @see SelectionListener
479  * @see #removeSelectionListener
480  * @see SelectionEvent
481  */

482 public void addSelectionListener(SelectionListener listener) {
483     checkWidget();
484     if (listener == null) {
485         SWT.error(SWT.ERROR_NULL_ARGUMENT);
486     }
487     TypedListener typedListener = new TypedListener(listener);
488     addListener(SWT.Selection, typedListener);
489     addListener(SWT.DefaultSelection, typedListener);
490 }
491 void antialias (int[] shape, RGB lineRGB, RGB innerRGB, RGB outerRGB, GC gc){
492     // Don't perform anti-aliasing on Mac and WPF because the platform
493
// already does it. The simple style also does not require anti-aliasing.
494
if (simple || "carbon".equals(SWT.getPlatform()) || "wpf".equals(SWT.getPlatform())) return; //$NON-NLS-1$
495
// Don't perform anti-aliasing on low resolution displays
496
if (getDisplay().getDepth() < 15) return;
497     if (outerRGB != null) {
498         int index = 0;
499         boolean left = true;
500         int oldY = onBottom ? 0 : getSize().y;
501         int[] outer = new int[shape.length];
502         for (int i = 0; i < shape.length/2; i++) {
503             if (left && (index + 3 < shape.length)) {
504                 left = onBottom ? oldY <= shape[index+3] : oldY >= shape[index+3];
505                 oldY = shape[index+1];
506             }
507             outer[index] = shape[index++] + (left ? -1 : +1);
508             outer[index] = shape[index++];
509         }
510         RGB from = lineRGB;
511         RGB to = outerRGB;
512         int red = from.red + 2*(to.red - from.red)/3;
513         int green = from.green + 2*(to.green - from.green)/3;
514         int blue = from.blue + 2*(to.blue - from.blue)/3;
515         Color color = new Color(getDisplay(), red, green, blue);
516         gc.setForeground(color);
517         gc.drawPolyline(outer);
518         color.dispose();
519     }
520     if (innerRGB != null) {
521         int[] inner = new int[shape.length];
522         int index = 0;
523         boolean left = true;
524         int oldY = onBottom ? 0 : getSize().y;
525         for (int i = 0; i < shape.length/2; i++) {
526             if (left && (index + 3 < shape.length)) {
527                 left = onBottom ? oldY <= shape[index+3] : oldY >= shape[index+3];
528                 oldY = shape[index+1];
529             }
530             inner[index] = shape[index++] + (left ? +1 : -1);
531             inner[index] = shape[index++];
532         }
533         RGB from = lineRGB;
534         RGB to = innerRGB;
535         int red = from.red + 2*(to.red - from.red)/3;
536         int green = from.green + 2*(to.green - from.green)/3;
537         int blue = from.blue + 2*(to.blue - from.blue)/3;
538         Color color = new Color(getDisplay(), red, green, blue);
539         gc.setForeground(color);
540         gc.drawPolyline(inner);
541         color.dispose();
542     }
543 }
544 public Rectangle computeTrim (int x, int y, int width, int height) {
545     checkWidget();
546     int trimX = x - marginWidth - highlight_margin - borderLeft;
547     int trimWidth = width + borderLeft + borderRight + 2*marginWidth + 2*highlight_margin;
548     if (minimized) {
549         int trimY = onBottom ? y - borderTop : y - highlight_header - tabHeight - borderTop;
550         int trimHeight = borderTop + borderBottom + tabHeight + highlight_header;
551         return new Rectangle (trimX, trimY, trimWidth, trimHeight);
552     } else {
553         int trimY = onBottom ? y - marginHeight - highlight_margin - borderTop: y - marginHeight - highlight_header - tabHeight - borderTop;
554         int trimHeight = height + borderTop + borderBottom + 2*marginHeight + tabHeight + highlight_header + highlight_margin;
555         return new Rectangle (trimX, trimY, trimWidth, trimHeight);
556     }
557 }
558 void createItem (CTabItem item, int index) {
559     if (0 > index || index > getItemCount ())SWT.error (SWT.ERROR_INVALID_RANGE);
560     item.parent = this;
561     CTabItem[] newItems = new CTabItem [items.length + 1];
562     System.arraycopy(items, 0, newItems, 0, index);
563     newItems[index] = item;
564     System.arraycopy(items, index, newItems, index + 1, items.length - index);
565     items = newItems;
566     if (selectedIndex >= index) selectedIndex ++;
567     int[] newPriority = new int[priority.length + 1];
568     int next = 0, priorityIndex = priority.length;
569     for (int i = 0; i < priority.length; i++) {
570         if (!mru && priority[i] == index) {
571             priorityIndex = next++;
572         }
573         newPriority[next++] = priority[i] >= index ? priority[i] + 1 : priority[i];
574     }
575     newPriority[priorityIndex] = index;
576     priority = newPriority;
577     
578     if (items.length == 1) {
579         if (!updateTabHeight(false)) updateItems();
580         redraw();
581     } else {
582         updateItems();
583         redrawTabs();
584     }
585 }
586 void destroyItem (CTabItem item) {
587     if (inDispose) return;
588     int index = indexOf(item);
589     if (index == -1) return;
590     
591     if (items.length == 1) {
592         items = new CTabItem[0];
593         priority = new int[0];
594         firstIndex = -1;
595         selectedIndex = -1;
596         
597         Control control = item.getControl();
598         if (control != null && !control.isDisposed()) {
599             control.setVisible(false);
600         }
601         setToolTipText(null);
602         setButtonBounds();
603         redraw();
604         return;
605     }
606         
607     CTabItem[] newItems = new CTabItem [items.length - 1];
608     System.arraycopy(items, 0, newItems, 0, index);
609     System.arraycopy(items, index + 1, newItems, index, items.length - index - 1);
610     items = newItems;
611     
612     int[] newPriority = new int[priority.length - 1];
613     int next = 0;
614     for (int i = 0; i < priority.length; i++) {
615         if (priority [i] == index) continue;
616         newPriority[next++] = priority[i] > index ? priority[i] - 1 : priority [i];
617     }
618     priority = newPriority;
619     
620     // move the selection if this item is selected
621
if (selectedIndex == index) {
622         Control control = item.getControl();
623         selectedIndex = -1;
624         int nextSelection = mru ? priority[0] : Math.max(0, index - 1);
625         setSelection(nextSelection, true);
626         if (control != null && !control.isDisposed()) {
627             control.setVisible(false);
628         }
629     } else if (selectedIndex > index) {
630         selectedIndex --;
631     }
632     
633     updateItems();
634     redrawTabs();
635 }
636 void drawBackground(GC gc, int[] shape, boolean selected) {
637     Color defaultBackground = selected ? selectionBackground : getBackground();
638     Image image = selected ? selectionBgImage : bgImage;
639     Color[] colors = selected ? selectionGradientColors : gradientColors;
640     int[] percents = selected ? selectionGradientPercents : gradientPercents;
641     boolean vertical = selected ? selectionGradientVertical : gradientVertical;
642     Point size = getSize();
643     int width = size.x;
644     int height = tabHeight + highlight_header;
645     int x = 0;
646     if (borderLeft > 0) {
647         x += 1; width -= 2;
648     }
649     int y = onBottom ? size.y - borderBottom - height : borderTop;
650     drawBackground(gc, shape, x, y, width, height, defaultBackground, image, colors, percents, vertical);
651 }
652 void drawBackground(GC gc, int[] shape, int x, int y, int width, int height, Color defaultBackground, Image image, Color[] colors, int[] percents, boolean vertical) {
653     Region clipping = new Region();
654     gc.getClipping(clipping);
655     Region region = new Region();
656     region.add(shape);
657     region.intersect(clipping);
658     gc.setClipping(region);
659     
660     if (image != null) {
661         // draw the background image in shape
662
gc.setBackground(defaultBackground);
663         gc.fillRectangle(x, y, width, height);
664         Rectangle imageRect = image.getBounds();
665         gc.drawImage(image, imageRect.x, imageRect.y, imageRect.width, imageRect.height, x, y, width, height);
666     } else if (colors != null) {
667         // draw gradient
668
if (colors.length == 1) {
669             Color background = colors[0] != null ? colors[0] : defaultBackground;
670             gc.setBackground(background);
671             gc.fillRectangle(x, y, width, height);
672         } else {
673             if (vertical) {
674                 if (onBottom) {
675                     int pos = 0;
676                     if (percents[percents.length - 1] < 100) {
677                         pos = percents[percents.length - 1] * height / 100;
678                         gc.setBackground(defaultBackground);
679                         gc.fillRectangle(x, y, width, pos);
680                     }
681                     Color lastColor = colors[colors.length-1];
682                     if (lastColor == null) lastColor = defaultBackground;
683                     for (int i = percents.length-1; i >= 0; i--) {
684                         gc.setForeground(lastColor);
685                         lastColor = colors[i];
686                         if (lastColor == null) lastColor = defaultBackground;
687                         gc.setBackground(lastColor);
688                         int gradientHeight = percents[i] * height / 100;
689                         gc.fillGradientRectangle(x, y+pos, width, gradientHeight, true);
690                         pos += gradientHeight;
691                     }
692                 } else {
693                     Color lastColor = colors[0];
694                     if (lastColor == null) lastColor = defaultBackground;
695                     int pos = 0;
696                     for (int i = 0; i < percents.length; i++) {
697                         gc.setForeground(lastColor);
698                         lastColor = colors[i + 1];
699                         if (lastColor == null) lastColor = defaultBackground;
700                         gc.setBackground(lastColor);
701                         int gradientHeight = percents[i] * height / 100;
702                         gc.fillGradientRectangle(x, y+pos, width, gradientHeight, true);
703                         pos += gradientHeight;
704                     }
705                     if (pos < height) {
706                         gc.setBackground(defaultBackground);
707                         gc.fillRectangle(x, pos, width, height-pos+1);
708                     }
709                 }
710             } else { //horizontal gradient
711
y = 0;
712                 height = getSize().y;
713                 Color lastColor = colors[0];
714                 if (lastColor == null) lastColor = defaultBackground;
715                 int pos = 0;
716                 for (int i = 0; i < percents.length; ++i) {
717                     gc.setForeground(lastColor);
718                     lastColor = colors[i + 1];
719                     if (lastColor == null) lastColor = defaultBackground;
720                     gc.setBackground(lastColor);
721                     int gradientWidth = (percents[i] * width / 100) - pos;
722                     gc.fillGradientRectangle(x+pos, y, gradientWidth, height, false);
723                     pos += gradientWidth;
724                 }
725                 if (pos < width) {
726                     gc.setBackground(defaultBackground);
727                     gc.fillRectangle(x+pos, y, width-pos, height);
728                 }
729             }
730         }
731     } else {
732         // draw a solid background using default background in shape
733
if ((getStyle() & SWT.NO_BACKGROUND) != 0 || !defaultBackground.equals(getBackground())) {
734             gc.setBackground(defaultBackground);
735             gc.fillRectangle(x, y, width, height);
736         }
737     }
738     gc.setClipping(clipping);
739     clipping.dispose();
740     region.dispose();
741 }
742 void drawBody(Event event) {
743     GC gc = event.gc;
744     Point size = getSize();
745     
746     // fill in body
747
if (!minimized){
748         int width = size.x - borderLeft - borderRight - 2*highlight_margin;
749         int height = size.y - borderTop - borderBottom - tabHeight - highlight_header - highlight_margin;
750         // Draw highlight margin
751
if (highlight_margin > 0) {
752             int[] shape = null;
753             if (onBottom) {
754                 int x1 = borderLeft;
755                 int y1 = borderTop;
756                 int x2 = size.x - borderRight;
757                 int y2 = size.y - borderBottom - tabHeight - highlight_header;
758                 shape = new int[] {x1,y1, x2,y1, x2,y2, x2-highlight_margin,y2,
759                                    x2-highlight_margin, y1+highlight_margin, x1+highlight_margin,y1+highlight_margin,
760                                    x1+highlight_margin,y2, x1,y2};
761             } else {
762                 int x1 = borderLeft;
763                 int y1 = borderTop + tabHeight + highlight_header;
764                 int x2 = size.x - borderRight;
765                 int y2 = size.y - borderBottom;
766                 shape = new int[] {x1,y1, x1+highlight_margin,y1, x1+highlight_margin,y2-highlight_margin,
767                                    x2-highlight_margin,y2-highlight_margin, x2-highlight_margin,y1,
768                                    x2,y1, x2,y2, x1,y2};
769             }
770             // If horizontal gradient, show gradient across the whole area
771
if (selectedIndex != -1 && selectionGradientColors != null && selectionGradientColors.length > 1 && !selectionGradientVertical) {
772                 drawBackground(gc, shape, true);
773             } else if (selectedIndex == -1 && gradientColors != null && gradientColors.length > 1 && !gradientVertical) {
774                 drawBackground(gc, shape, false);
775             } else {
776                 gc.setBackground(selectedIndex == -1 ? getBackground() : selectionBackground);
777                 gc.fillPolygon(shape);
778             }
779         }
780         //Draw client area
781
if ((getStyle() & SWT.NO_BACKGROUND) != 0) {
782             gc.setBackground(getBackground());
783             gc.fillRectangle(xClient - marginWidth, yClient - marginHeight, width, height);
784         }
785     } else {
786         if ((getStyle() & SWT.NO_BACKGROUND) != 0) {
787             int height = borderTop + tabHeight + highlight_header + borderBottom;
788             if (size.y > height) {
789                 gc.setBackground(getParent().getBackground());
790                 gc.fillRectangle(0, height, size.x, size.y - height);
791             }
792         }
793     }
794     
795     //draw 1 pixel border around outside
796
if (borderLeft > 0) {
797         gc.setForeground(borderColor);
798         int x1 = borderLeft - 1;
799         int x2 = size.x - borderRight;
800         int y1 = onBottom ? borderTop - 1 : borderTop + tabHeight;
801         int y2 = onBottom ? size.y - tabHeight - borderBottom - 1 : size.y - borderBottom;
802         gc.drawLine(x1, y1, x1, y2); // left
803
gc.drawLine(x2, y1, x2, y2); // right
804
if (onBottom) {
805             gc.drawLine(x1, y1, x2, y1); // top
806
} else {
807             gc.drawLine(x1, y2, x2, y2); // bottom
808
}
809     }
810 }
811
812 void drawChevron(GC gc) {
813     if (chevronRect.width == 0 || chevronRect.height == 0) return;
814     // draw chevron (10x7)
815
Display display = getDisplay();
816     Point dpi = display.getDPI();
817     int fontHeight = 72 * 10 / dpi.y;
818     FontData fd = getFont().getFontData()[0];
819     fd.setHeight(fontHeight);
820     Font f = new Font(display, fd);
821     int fHeight = f.getFontData()[0].getHeight() * dpi.y / 72;
822     int indent = Math.max(2, (chevronRect.height - fHeight - 4) /2);
823     int x = chevronRect.x + 2;
824     int y = chevronRect.y + indent;
825     int count;
826     if (single) {
827         count = selectedIndex == -1 ? items.length : items.length - 1;
828     } else {
829         int showCount = 0;
830         while (showCount < priority.length && items[priority[showCount]].showing) {
831             showCount++;
832         }
833         count = items.length - showCount;
834     }
835     String JavaDoc chevronString = count > 99 ? "99+" : String.valueOf(count); //$NON-NLS-1$
836
switch (chevronImageState) {
837         case NORMAL: {
838             Color chevronBorder = single ? getSelectionForeground() : getForeground();
839             gc.setForeground(chevronBorder);
840             gc.setFont(f);
841             gc.drawLine(x,y, x+2,y+2);
842             gc.drawLine(x+2,y+2, x,y+4);
843             gc.drawLine(x+1,y, x+3,y+2);
844             gc.drawLine(x+3,y+2, x+1,y+4);
845             gc.drawLine(x+4,y, x+6,y+2);
846             gc.drawLine(x+6,y+2, x+5,y+4);
847             gc.drawLine(x+5,y, x+7,y+2);
848             gc.drawLine(x+7,y+2, x+4,y+4);
849             gc.drawString(chevronString, x+7, y+3, true);
850             break;
851         }
852         case HOT: {
853             gc.setForeground(display.getSystemColor(BUTTON_BORDER));
854             gc.setBackground(display.getSystemColor(BUTTON_FILL));
855             gc.setFont(f);
856             gc.fillRoundRectangle(chevronRect.x, chevronRect.y, chevronRect.width, chevronRect.height, 6, 6);
857             gc.drawRoundRectangle(chevronRect.x, chevronRect.y, chevronRect.width - 1, chevronRect.height - 1, 6, 6);
858             gc.drawLine(x,y, x+2,y+2);
859             gc.drawLine(x+2,y+2, x,y+4);
860             gc.drawLine(x+1,y, x+3,y+2);
861             gc.drawLine(x+3,y+2, x+1,y+4);
862             gc.drawLine(x+4,y, x+6,y+2);
863             gc.drawLine(x+6,y+2, x+5,y+4);
864             gc.drawLine(x+5,y, x+7,y+2);
865             gc.drawLine(x+7,y+2, x+4,y+4);
866             gc.drawString(chevronString, x+7, y+3, true);
867             break;
868         }
869         case SELECTED: {
870             gc.setForeground(display.getSystemColor(BUTTON_BORDER));
871             gc.setBackground(display.getSystemColor(BUTTON_FILL));
872             gc.setFont(f);
873             gc.fillRoundRectangle(chevronRect.x, chevronRect.y, chevronRect.width, chevronRect.height, 6, 6);
874             gc.drawRoundRectangle(chevronRect.x, chevronRect.y, chevronRect.width - 1, chevronRect.height - 1, 6, 6);
875             gc.drawLine(x+1,y+1, x+3,y+3);
876             gc.drawLine(x+3,y+3, x+1,y+5);
877             gc.drawLine(x+2,y+1, x+4,y+3);
878             gc.drawLine(x+4,y+3, x+2,y+5);
879             gc.drawLine(x+5,y+1, x+7,y+3);
880             gc.drawLine(x+7,y+3, x+6,y+5);
881             gc.drawLine(x+6,y+1, x+8,y+3);
882             gc.drawLine(x+8,y+3, x+5,y+5);
883             gc.drawString(chevronString, x+8, y+4, true);
884             break;
885         }
886     }
887     f.dispose();
888 }
889 void drawMaximize(GC gc) {
890     if (maxRect.width == 0 || maxRect.height == 0) return;
891     Display display = getDisplay();
892     // 5x4 or 7x9
893
int x = maxRect.x + (CTabFolder.BUTTON_SIZE - 10)/2;
894     int y = maxRect.y + 3;
895     
896     gc.setForeground(display.getSystemColor(BUTTON_BORDER));
897     gc.setBackground(display.getSystemColor(BUTTON_FILL));
898     
899     switch (maxImageState) {
900         case NORMAL: {
901             if (!maximized) {
902                 gc.fillRectangle(x, y, 9, 9);
903                 gc.drawRectangle(x, y, 9, 9);
904                 gc.drawLine(x+1, y+2, x+8, y+2);
905             } else {
906                 gc.fillRectangle(x, y+3, 5, 4);
907                 gc.fillRectangle(x+2, y, 5, 4);
908                 gc.drawRectangle(x, y+3, 5, 4);
909                 gc.drawRectangle(x+2, y, 5, 4);
910                 gc.drawLine(x+3, y+1, x+6, y+1);
911                 gc.drawLine(x+1, y+4, x+4, y+4);
912             }
913             break;
914         }
915         case HOT: {
916             gc.fillRoundRectangle(maxRect.x, maxRect.y, maxRect.width, maxRect.height, 6, 6);
917             gc.drawRoundRectangle(maxRect.x, maxRect.y, maxRect.width - 1, maxRect.height - 1, 6, 6);
918             if (!maximized) {
919                 gc.fillRectangle(x, y, 9, 9);
920                 gc.drawRectangle(x, y, 9, 9);
921                 gc.drawLine(x+1, y+2, x+8, y+2);
922             } else {
923                 gc.fillRectangle(x, y+3, 5, 4);
924                 gc.fillRectangle(x+2, y, 5, 4);
925                 gc.drawRectangle(x, y+3, 5, 4);
926                 gc.drawRectangle(x+2, y, 5, 4);
927                 gc.drawLine(x+3, y+1, x+6, y+1);
928                 gc.drawLine(x+1, y+4, x+4, y+4);
929             }
930             break;
931         }
932         case SELECTED: {
933             gc.fillRoundRectangle(maxRect.x, maxRect.y, maxRect.width, maxRect.height, 6, 6);
934             gc.drawRoundRectangle(maxRect.x, maxRect.y, maxRect.width - 1, maxRect.height - 1, 6, 6);
935             if (!maximized) {
936                 gc.fillRectangle(x+1, y+1, 9, 9);
937                 gc.drawRectangle(x+1, y+1, 9, 9);
938                 gc.drawLine(x+2, y+3, x+9, y+3);
939             } else {
940                 gc.fillRectangle(x+1, y+4, 5, 4);
941                 gc.fillRectangle(x+3, y+1, 5, 4);
942                 gc.drawRectangle(x+1, y+4, 5, 4);
943                 gc.drawRectangle(x+3, y+1, 5, 4);
944                 gc.drawLine(x+4, y+2, x+7, y+2);
945                 gc.drawLine(x+2, y+5, x+5, y+5);
946             }
947             break;
948         }
949     }
950 }
951 void drawMinimize(GC gc) {
952     if (minRect.width == 0 || minRect.height == 0) return;
953     Display display = getDisplay();
954     // 5x4 or 9x3
955
int x = minRect.x + (BUTTON_SIZE - 10)/2;
956     int y = minRect.y + 3;
957     
958     gc.setForeground(display.getSystemColor(BUTTON_BORDER));
959     gc.setBackground(display.getSystemColor(BUTTON_FILL));
960     
961     switch (minImageState) {
962         case NORMAL: {
963             if (!minimized) {
964                 gc.fillRectangle(x, y, 9, 3);
965                 gc.drawRectangle(x, y, 9, 3);
966             } else {
967                 gc.fillRectangle(x, y+3, 5, 4);
968                 gc.fillRectangle(x+2, y, 5, 4);
969                 gc.drawRectangle(x, y+3, 5, 4);
970                 gc.drawRectangle(x+2, y, 5, 4);
971                 gc.drawLine(x+3, y+1, x+6, y+1);
972                 gc.drawLine(x+1, y+4, x+4, y+4);
973             }
974             break;
975         }
976         case HOT: {
977             gc.fillRoundRectangle(minRect.x, minRect.y, minRect.width, minRect.height, 6, 6);
978             gc.drawRoundRectangle(minRect.x, minRect.y, minRect.width - 1, minRect.height - 1, 6, 6);
979             if (!minimized) {
980                 gc.fillRectangle(x, y, 9, 3);
981                 gc.drawRectangle(x, y, 9, 3);
982             } else {
983                 gc.fillRectangle(x, y+3, 5, 4);
984                 gc.fillRectangle(x+2, y, 5, 4);
985                 gc.drawRectangle(x, y+3, 5, 4);
986                 gc.drawRectangle(x+2, y, 5, 4);
987                 gc.drawLine(x+3, y+1, x+6, y+1);
988                 gc.drawLine(x+1, y+4, x+4, y+4);
989             }
990             break;
991         }
992         case SELECTED: {
993             gc.fillRoundRectangle(minRect.x, minRect.y, minRect.width, minRect.height, 6, 6);
994             gc.drawRoundRectangle(minRect.x, minRect.y, minRect.width - 1, minRect.height - 1, 6, 6);
995             if (!minimized) {
996                 gc.fillRectangle(x+1, y+1, 9, 3);
997                 gc.drawRectangle(x+1, y+1, 9, 3);
998             } else {
999                 gc.fillRectangle(x+1, y+4, 5, 4);
1000                gc.fillRectangle(x+3, y+1, 5, 4);
1001                gc.drawRectangle(x+1, y+4, 5, 4);
1002                gc.drawRectangle(x+3, y+1, 5, 4);
1003                gc.drawLine(x+4, y+2, x+7, y+2);
1004                gc.drawLine(x+2, y+5, x+5, y+5);
1005            }
1006            break;
1007        }
1008    }
1009}
1010void drawTabArea(Event event) {
1011    GC gc = event.gc;
1012    Point size = getSize();
1013    int[] shape = null;
1014    
1015    if (tabHeight == 0) {
1016        int style = getStyle();
1017        if ((style & SWT.FLAT) != 0 && (style & SWT.BORDER) == 0) return;
1018        int x1 = borderLeft - 1;
1019        int x2 = size.x - borderRight;
1020        int y1 = onBottom ? size.y - borderBottom - highlight_header - 1 : borderTop + highlight_header;
1021        int y2 = onBottom ? size.y - borderBottom : borderTop;
1022        if (borderLeft > 0 && onBottom) y2 -= 1;
1023        
1024        shape = new int[] {x1, y1, x1,y2, x2,y2, x2,y1};
1025
1026        // If horizontal gradient, show gradient across the whole area
1027
if (selectedIndex != -1 && selectionGradientColors != null && selectionGradientColors.length > 1 && !selectionGradientVertical) {
1028            drawBackground(gc, shape, true);
1029        } else if (selectedIndex == -1 && gradientColors != null && gradientColors.length > 1 && !gradientVertical) {
1030            drawBackground(gc, shape, false);
1031        } else {
1032            gc.setBackground(selectedIndex == -1 ? getBackground() : selectionBackground);
1033            gc.fillPolygon(shape);
1034        }
1035        
1036        //draw 1 pixel border
1037
if (borderLeft > 0) {
1038            gc.setForeground(borderColor);
1039            gc.drawPolyline(shape);
1040        }
1041        return;
1042    }
1043    
1044    int x = Math.max(0, borderLeft - 1);
1045    int y = onBottom ? size.y - borderBottom - tabHeight : borderTop;
1046    int width = size.x - borderLeft - borderRight + 1;
1047    int height = tabHeight - 1;
1048    
1049    // Draw Tab Header
1050
if (onBottom) {
1051        int[] left, right;
1052        if ((getStyle() & SWT.BORDER) != 0) {
1053            left = simple ? SIMPLE_BOTTOM_LEFT_CORNER : BOTTOM_LEFT_CORNER;
1054            right = simple ? SIMPLE_BOTTOM_RIGHT_CORNER : BOTTOM_RIGHT_CORNER;
1055        } else {
1056            left = simple ? SIMPLE_BOTTOM_LEFT_CORNER_BORDERLESS : BOTTOM_LEFT_CORNER_BORDERLESS;
1057            right = simple ? SIMPLE_BOTTOM_RIGHT_CORNER_BORDERLESS : BOTTOM_RIGHT_CORNER_BORDERLESS;
1058        }
1059        shape = new int[left.length + right.length + 4];
1060        int index = 0;
1061        shape[index++] = x;
1062        shape[index++] = y-highlight_header;
1063        for (int i = 0; i < left.length/2; i++) {
1064            shape[index++] = x+left[2*i];
1065            shape[index++] = y+height+left[2*i+1];
1066            if (borderLeft == 0) shape[index-1] += 1;
1067        }
1068        for (int i = 0; i < right.length/2; i++) {
1069            shape[index++] = x+width+right[2*i];
1070            shape[index++] = y+height+right[2*i+1];
1071            if (borderLeft == 0) shape[index-1] += 1;
1072        }
1073        shape[index++] = x+width;
1074        shape[index++] = y-highlight_header;
1075    } else {
1076        int[] left, right;
1077        if ((getStyle() & SWT.BORDER) != 0) {
1078            left = simple ? SIMPLE_TOP_LEFT_CORNER : TOP_LEFT_CORNER;
1079            right = simple ? SIMPLE_TOP_RIGHT_CORNER : TOP_RIGHT_CORNER;
1080        } else {
1081            left = simple ? SIMPLE_TOP_LEFT_CORNER_BORDERLESS : TOP_LEFT_CORNER_BORDERLESS;
1082            right = simple ? SIMPLE_TOP_RIGHT_CORNER_BORDERLESS : TOP_RIGHT_CORNER_BORDERLESS;
1083        }
1084        shape = new int[left.length + right.length + 4];
1085        int index = 0;
1086        shape[index++] = x;
1087        shape[index++] = y+height+highlight_header + 1;
1088        for (int i = 0; i < left.length/2; i++) {
1089            shape[index++] = x+left[2*i];
1090            shape[index++] = y+left[2*i+1];
1091        }
1092        for (int i = 0; i < right.length/2; i++) {
1093            shape[index++] = x+width+right[2*i];
1094            shape[index++] = y+right[2*i+1];
1095        }
1096        shape[index++] = x+width;
1097        shape[index++] = y+height+highlight_header + 1;
1098    }
1099    // Fill in background
1100
boolean bkSelected = single && selectedIndex != -1;
1101    drawBackground(gc, shape, bkSelected);
1102    // Fill in parent background for non-rectangular shape
1103
Region r = new Region();
1104    r.add(new Rectangle(x, y, width + 1, height + 1));
1105    r.subtract(shape);
1106    gc.setBackground(getParent().getBackground());
1107    fillRegion(gc, r);
1108    r.dispose();
1109    
1110    // Draw the unselected tabs.
1111
if (!single) {
1112        for (int i=0; i < items.length; i++) {
1113            if (i != selectedIndex && event.getBounds().intersects(items[i].getBounds())) {
1114                items[i].onPaint(gc, false);
1115            }
1116        }
1117    }
1118    
1119    // Draw selected tab
1120
if (selectedIndex != -1) {
1121        CTabItem item = items[selectedIndex];
1122        item.onPaint(gc, true);
1123    } else {
1124        // if no selected tab - draw line across bottom of all tabs
1125
int x1 = borderLeft;
1126        int y1 = (onBottom) ? size.y - borderBottom - tabHeight - 1 : borderTop + tabHeight;
1127        int x2 = size.x - borderRight;
1128        gc.setForeground(borderColor);
1129        gc.drawLine(x1, y1, x2, y1);
1130    }
1131    
1132    // Draw Buttons
1133
drawChevron(gc);
1134    drawMinimize(gc);
1135    drawMaximize(gc);
1136    
1137    // Draw border line
1138
if (borderLeft > 0) {
1139        RGB outside = getParent().getBackground().getRGB();
1140        antialias(shape, borderColor.getRGB(), null, outside, gc);
1141        gc.setForeground(borderColor);
1142        gc.drawPolyline(shape);
1143    }
1144}
1145/**
1146 * Returns <code>true</code> if the receiver's border is visible.
1147 *
1148 * @return the receiver's border visibility state
1149 *
1150 * @exception SWTException <ul>
1151 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1152 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1153 * </ul>
1154 *
1155 * @since 3.0
1156 */

1157public boolean getBorderVisible() {
1158    checkWidget();
1159    return borderLeft == 1;
1160}
1161public Rectangle getClientArea() {
1162    checkWidget();
1163    if (minimized) return new Rectangle(xClient, yClient, 0, 0);
1164    Point size = getSize();
1165    int width = size.x - borderLeft - borderRight - 2*marginWidth - 2*highlight_margin;
1166    int height = size.y - borderTop - borderBottom - 2*marginHeight - highlight_margin - highlight_header;
1167    height -= tabHeight;
1168    return new Rectangle(xClient, yClient, width, height);
1169}
1170/**
1171 * Return the tab that is located at the specified index.
1172 *
1173 * @param index the index of the tab item
1174 * @return the item at the specified index
1175 *
1176 * @exception IllegalArgumentException <ul>
1177 * <li>ERROR_INVALID_RANGE - if the index is out of range</li>
1178 * </ul>
1179 * @exception SWTException <ul>
1180 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1181 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1182 * </ul>
1183 */

1184public CTabItem getItem (int index) {
1185    //checkWidget();
1186
if (index < 0 || index >= items.length)
1187        SWT.error(SWT.ERROR_INVALID_RANGE);
1188    return items [index];
1189}
1190/**
1191 * Gets the item at a point in the widget.
1192 *
1193 * @param pt the point in coordinates relative to the CTabFolder
1194 * @return the item at a point or null
1195 *
1196 * @exception SWTException <ul>
1197 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1198 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1199 * </ul>
1200 */

1201public CTabItem getItem (Point pt) {
1202    //checkWidget();
1203
if (items.length == 0) return null;
1204    Point size = getSize();
1205    if (size.x <= borderLeft + borderRight) return null;
1206    if (showChevron && chevronRect.contains(pt)) return null;
1207    for (int i = 0; i < priority.length; i++) {
1208        CTabItem item = items[priority[i]];
1209        Rectangle rect = item.getBounds();
1210        if (rect.contains(pt)) return item;
1211    }
1212    return null;
1213}
1214/**
1215 * Return the number of tabs in the folder.
1216 *
1217 * @return the number of tabs in the folder
1218 *
1219 * @exception SWTException <ul>
1220 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1221 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1222 * </ul>
1223 */

1224public int getItemCount(){
1225    //checkWidget();
1226
return items.length;
1227}
1228/**
1229 * Return the tab items.
1230 *
1231 * @return the tab items
1232 *
1233 * @exception SWTException <ul>
1234 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1235 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1236 * </ul>
1237 */

1238public CTabItem [] getItems() {
1239    //checkWidget();
1240
CTabItem[] tabItems = new CTabItem [items.length];
1241    System.arraycopy(items, 0, tabItems, 0, items.length);
1242    return tabItems;
1243}
1244/*
1245 * Return the lowercase of the first non-'&' character following
1246 * an '&' character in the given string. If there are no '&'
1247 * characters in the given string, return '\0'.
1248 */

1249char _findMnemonic (String JavaDoc string) {
1250    if (string == null) return '\0';
1251    int index = 0;
1252    int length = string.length ();
1253    do {
1254        while (index < length && string.charAt (index) != '&') index++;
1255        if (++index >= length) return '\0';
1256        if (string.charAt (index) != '&') return Character.toLowerCase (string.charAt (index));
1257        index++;
1258    } while (index < length);
1259    return '\0';
1260}
1261String JavaDoc stripMnemonic (String JavaDoc string) {
1262    int index = 0;
1263    int length = string.length ();
1264    do {
1265        while ((index < length) && (string.charAt (index) != '&')) index++;
1266        if (++index >= length) return string;
1267        if (string.charAt (index) != '&') {
1268            return string.substring(0, index-1) + string.substring(index, length);
1269        }
1270        index++;
1271    } while (index < length);
1272    return string;
1273}
1274/**
1275 * Returns <code>true</code> if the receiver is minimized.
1276 *
1277 * @return the receiver's minimized state
1278 *
1279 * @exception SWTException <ul>
1280 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1281 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1282 * </ul>
1283 *
1284 * @since 3.0
1285 */

1286public boolean getMinimized() {
1287    checkWidget();
1288    return minimized;
1289}
1290/**
1291 * Returns <code>true</code> if the minimize button
1292 * is visible.
1293 *
1294 * @return the visibility of the minimized button
1295 *
1296 * @exception SWTException <ul>
1297 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1298 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1299 * </ul>
1300 *
1301 * @since 3.0
1302 */

1303public boolean getMinimizeVisible() {
1304    checkWidget();
1305    return showMin;
1306}
1307/**
1308 * Returns the number of characters that will
1309 * appear in a fully compressed tab.
1310 *
1311 * @return number of characters that will appear in a fully compressed tab
1312 *
1313 * @since 3.0
1314 */

1315public int getMinimumCharacters() {
1316    checkWidget();
1317    return minChars;
1318}
1319/**
1320 * Returns <code>true</code> if the receiver is maximized.
1321 * <p>
1322 *
1323 * @return the receiver's maximized state
1324 *
1325 * @exception SWTException <ul>
1326 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1327 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1328 * </ul>
1329 *
1330 * @since 3.0
1331 */

1332public boolean getMaximized() {
1333    checkWidget();
1334    return maximized;
1335}
1336/**
1337 * Returns <code>true</code> if the maximize button
1338 * is visible.
1339 *
1340 * @return the visibility of the maximized button
1341 *
1342 * @exception SWTException <ul>
1343 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1344 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1345 * </ul>
1346 *
1347 * @since 3.0
1348 */

1349public boolean getMaximizeVisible() {
1350    checkWidget();
1351    return showMax;
1352}
1353/**
1354 * Returns <code>true</code> if the receiver displays most
1355 * recently used tabs and <code>false</code> otherwise.
1356 * <p>
1357 * When there is not enough horizontal space to show all the tabs,
1358 * by default, tabs are shown sequentially from left to right in
1359 * order of their index. When the MRU visibility is turned on,
1360 * the tabs that are visible will be the tabs most recently selected.
1361 * Tabs will still maintain their left to right order based on index
1362 * but only the most recently selected tabs are visible.
1363 * <p>
1364 * For example, consider a CTabFolder that contains "Tab 1", "Tab 2",
1365 * "Tab 3" and "Tab 4" (in order by index). The user selects
1366 * "Tab 1" and then "Tab 3". If the CTabFolder is now
1367 * compressed so that only two tabs are visible, by default,
1368 * "Tab 2" and "Tab 3" will be shown ("Tab 3" since it is currently
1369 * selected and "Tab 2" because it is the previous item in index order).
1370 * If MRU visibility is enabled, the two visible tabs will be "Tab 1"
1371 * and "Tab 3" (in that order from left to right).</p>
1372 *
1373 * @return the receiver's header's visibility state
1374 *
1375 * @exception SWTException <ul>
1376 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1377 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1378 * </ul>
1379 *
1380 * @since 3.1
1381 */

1382public boolean getMRUVisible() {
1383    checkWidget();
1384    return mru;
1385}
1386int getRightItemEdge (){
1387    int x = getSize().x - borderRight - 3;
1388    if (showMin) x -= BUTTON_SIZE;
1389    if (showMax) x -= BUTTON_SIZE;
1390    if (showChevron) x -= 3*BUTTON_SIZE/2;
1391    if (topRight != null && topRightAlignment != SWT.FILL) {
1392        Point rightSize = topRight.computeSize(SWT.DEFAULT, SWT.DEFAULT);
1393        x -= rightSize.x + 3;
1394    }
1395    return Math.max(0, x);
1396}
1397/**
1398 * Return the selected tab item, or null if there is no selection.
1399 *
1400 * @return the selected tab item, or null if none has been selected
1401 *
1402 * @exception SWTException <ul>
1403 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1404 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1405 * </ul>
1406 */

1407public CTabItem getSelection() {
1408    //checkWidget();
1409
if (selectedIndex == -1) return null;
1410    return items[selectedIndex];
1411}
1412/**
1413 * Returns the receiver's selection background color.
1414 *
1415 * @return the selection background color of the receiver
1416 *
1417 * @exception SWTException <ul>
1418 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1419 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1420 * </ul>
1421 *
1422 * @since 3.0
1423 */

1424public Color getSelectionBackground() {
1425    checkWidget();
1426    return selectionBackground;
1427}
1428/**
1429 * Returns the receiver's selection foreground color.
1430 *
1431 * @return the selection foreground color of the receiver
1432 *
1433 * @exception SWTException <ul>
1434 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1435 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1436 * </ul>
1437 *
1438 * @since 3.0
1439 */

1440public Color getSelectionForeground() {
1441    checkWidget();
1442    return selectionForeground;
1443}
1444/**
1445 * Return the index of the selected tab item, or -1 if there
1446 * is no selection.
1447 *
1448 * @return the index of the selected tab item or -1
1449 *
1450 * @exception SWTException <ul>
1451 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1452 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1453 * </ul>
1454 */

1455public int getSelectionIndex() {
1456    //checkWidget();
1457
return selectedIndex;
1458}
1459/**
1460 * Returns <code>true</code> if the CTabFolder is rendered
1461 * with a simple, traditional shape.
1462 *
1463 * @return <code>true</code> if the CTabFolder is rendered with a simple shape
1464 *
1465 * @since 3.0
1466 */

1467public boolean getSimple() {
1468    checkWidget();
1469    return simple;
1470}
1471/**
1472 * Returns <code>true</code> if the CTabFolder only displays the selected tab
1473 * and <code>false</code> if the CTabFolder displays multiple tabs.
1474 *
1475 * @return <code>true</code> if the CTabFolder only displays the selected tab and <code>false</code> if the CTabFolder displays multiple tabs
1476 *
1477 * @since 3.0
1478 */

1479public boolean getSingle() {
1480    checkWidget();
1481    return single;
1482}
1483
1484public int getStyle() {
1485    int style = super.getStyle();
1486    style &= ~(SWT.TOP | SWT.BOTTOM);
1487    style |= onBottom ? SWT.BOTTOM : SWT.TOP;
1488    style &= ~(SWT.SINGLE | SWT.MULTI);
1489    style |= single ? SWT.SINGLE : SWT.MULTI;
1490    if (borderLeft != 0) style |= SWT.BORDER;
1491    return style;
1492}
1493/**
1494 * Returns the height of the tab
1495 *
1496 * @return the height of the tab
1497 *
1498 * @exception SWTException <ul>
1499 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1500 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1501 * </ul>
1502 */

1503public int getTabHeight(){
1504    checkWidget();
1505    if (fixedTabHeight != SWT.DEFAULT) return fixedTabHeight;
1506    return tabHeight - 1; // -1 for line drawn across top of tab
1507
}
1508/**
1509 * Returns the position of the tab. Possible values are SWT.TOP or SWT.BOTTOM.
1510 *
1511 * @return the position of the tab
1512 *
1513 * @exception SWTException <ul>
1514 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1515 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1516 * </ul>
1517 */

1518public int getTabPosition(){
1519    checkWidget();
1520    return onBottom ? SWT.BOTTOM : SWT.TOP;
1521}
1522/**
1523 * Returns the control in the top right corner of the tab folder.
1524 * Typically this is a close button or a composite with a menu and close button.
1525 *
1526 * @return the control in the top right corner of the tab folder or null
1527 *
1528 * @exception SWTException <ul>
1529 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1530 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1531 * </ul>
1532 *
1533 * @since 2.1
1534 */

1535public Control getTopRight() {
1536    checkWidget();
1537    return topRight;
1538}
1539/**
1540 * Returns <code>true</code> if the close button appears
1541 * when the user hovers over an unselected tabs.
1542 *
1543 * @return <code>true</code> if the close button appears on unselected tabs
1544 *
1545 * @since 3.0
1546 */

1547public boolean getUnselectedCloseVisible() {
1548    checkWidget();
1549    return showUnselectedClose;
1550}
1551/**
1552 * Returns <code>true</code> if an image appears
1553 * in unselected tabs.
1554 *
1555 * @return <code>true</code> if an image appears in unselected tabs
1556 *
1557 * @since 3.0
1558 */

1559public boolean getUnselectedImageVisible() {
1560    checkWidget();
1561    return showUnselectedImage;
1562}
1563/**
1564 * Return the index of the specified tab or -1 if the tab is not
1565 * in the receiver.
1566 *
1567 * @param item the tab item for which the index is required
1568 *
1569 * @return the index of the specified tab item or -1
1570 *
1571 * @exception IllegalArgumentException <ul>
1572 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
1573 * </ul>
1574 *
1575 * @exception SWTException <ul>
1576 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1577 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1578 * </ul>
1579 */

1580public int indexOf(CTabItem item) {
1581    checkWidget();
1582    if (item == null) {
1583        SWT.error(SWT.ERROR_NULL_ARGUMENT);
1584    }
1585    for (int i = 0; i < items.length; i++) {
1586        if (items[i] == item) return i;
1587    }
1588    return -1;
1589}
1590void initAccessible() {
1591    final Accessible accessible = getAccessible();
1592    accessible.addAccessibleListener(new AccessibleAdapter() {
1593        public void getName(AccessibleEvent e) {
1594            String JavaDoc name = null;
1595            int childID = e.childID;
1596            if (childID >= 0 && childID < items.length) {
1597                name = stripMnemonic(items[childID].getText());
1598            } else if (childID == items.length + CHEVRON_CHILD_ID) {
1599                name = SWT.getMessage("SWT_ShowList"); //$NON-NLS-1$
1600
} else if (childID == items.length + MINIMIZE_CHILD_ID) {
1601                name = minimized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Minimize"); //$NON-NLS-1$ //$NON-NLS-2$
1602
} else if (childID == items.length + MAXIMIZE_CHILD_ID) {
1603                name = maximized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Maximize"); //$NON-NLS-1$ //$NON-NLS-2$
1604
}
1605            e.result = name;
1606        }
1607
1608        public void getHelp(AccessibleEvent e) {
1609            String JavaDoc help = null;
1610            int childID = e.childID;
1611            if (childID == ACC.CHILDID_SELF) {
1612                help = getToolTipText();
1613            } else if (childID >= 0 && childID < items.length) {
1614                help = items[childID].getToolTipText();
1615            }
1616            e.result = help;
1617        }
1618        
1619        public void getKeyboardShortcut(AccessibleEvent e) {
1620            String JavaDoc shortcut = null;
1621            int childID = e.childID;
1622            if (childID >= 0 && childID < items.length) {
1623                String JavaDoc text = items[childID].getText();
1624                if (text != null) {
1625                    char mnemonic = _findMnemonic(text);
1626                    if (mnemonic != '\0') {
1627                        shortcut = "Alt+"+mnemonic; //$NON-NLS-1$
1628
}
1629                }
1630            }
1631            e.result = shortcut;
1632        }
1633    });
1634    
1635    accessible.addAccessibleControlListener(new AccessibleControlAdapter() {
1636        public void getChildAtPoint(AccessibleControlEvent e) {
1637            Point testPoint = toControl(e.x, e.y);
1638            int childID = ACC.CHILDID_NONE;
1639            for (int i = 0; i < items.length; i++) {
1640                if (items[i].getBounds().contains(testPoint)) {
1641                    childID = i;
1642                    break;
1643                }
1644            }
1645            if (childID == ACC.CHILDID_NONE) {
1646                if (showChevron && chevronRect.contains(testPoint)) {
1647                    childID = items.length + CHEVRON_CHILD_ID;
1648                } else if (showMin && minRect.contains(testPoint)) {
1649                    childID = items.length + MINIMIZE_CHILD_ID;
1650                } else if (showMax && maxRect.contains(testPoint)) {
1651                    childID = items.length + MAXIMIZE_CHILD_ID;
1652                } else {
1653                    Rectangle location = getBounds();
1654                    location.height = location.height - getClientArea().height;
1655                    if (location.contains(testPoint)) {
1656                        childID = ACC.CHILDID_SELF;
1657                    }
1658                }
1659            }
1660            e.childID = childID;
1661        }
1662
1663        public void getLocation(AccessibleControlEvent e) {
1664            Rectangle location = null;
1665            int childID = e.childID;
1666            if (childID == ACC.CHILDID_SELF) {
1667                location = getBounds();
1668            } else if (childID >= 0 && childID < items.length) {
1669                location = items[childID].getBounds();
1670            } else if (showChevron && childID == items.length + CHEVRON_CHILD_ID) {
1671                location = chevronRect;
1672            } else if (showMin && childID == items.length + MINIMIZE_CHILD_ID) {
1673                location = minRect;
1674            } else if (showMax && childID == items.length + MAXIMIZE_CHILD_ID) {
1675                location = maxRect;
1676            }
1677            if (location != null) {
1678                Point pt = toDisplay(location.x, location.y);
1679                e.x = pt.x;
1680                e.y = pt.y;
1681                e.width = location.width;
1682                e.height = location.height;
1683            }
1684        }
1685        
1686        public void getChildCount(AccessibleControlEvent e) {
1687            e.detail = items.length + EXTRA_CHILD_ID_COUNT;
1688        }
1689        
1690        public void getDefaultAction(AccessibleControlEvent e) {
1691            String JavaDoc action = null;
1692            int childID = e.childID;
1693            if (childID >= 0 && childID < items.length) {
1694                action = SWT.getMessage ("SWT_Switch"); //$NON-NLS-1$
1695
}
1696            if (childID >= items.length && childID < items.length + EXTRA_CHILD_ID_COUNT) {
1697                action = SWT.getMessage ("SWT_Press"); //$NON-NLS-1$
1698
}
1699            e.result = action;
1700        }
1701
1702        public void getFocus(AccessibleControlEvent e) {
1703            int childID = ACC.CHILDID_NONE;
1704            if (isFocusControl()) {
1705                if (selectedIndex == -1) {
1706                    childID = ACC.CHILDID_SELF;
1707                } else {
1708                    childID = selectedIndex;
1709                }
1710            }
1711            e.childID = childID;
1712        }
1713
1714        public void getRole(AccessibleControlEvent e) {
1715            int role = 0;
1716            int childID = e.childID;
1717            if (childID == ACC.CHILDID_SELF) {
1718                role = ACC.ROLE_TABFOLDER;
1719            } else if (childID >= 0 && childID < items.length) {
1720                role = ACC.ROLE_TABITEM;
1721            } else if (childID >= items.length && childID < items.length + EXTRA_CHILD_ID_COUNT) {
1722                role = ACC.ROLE_PUSHBUTTON;
1723            }
1724            e.detail = role;
1725        }
1726        
1727        public void getSelection(AccessibleControlEvent e) {
1728            e.childID = (selectedIndex == -1) ? ACC.CHILDID_NONE : selectedIndex;
1729        }
1730        
1731        public void getState(AccessibleControlEvent e) {
1732            int state = 0;
1733            int childID = e.childID;
1734            if (childID == ACC.CHILDID_SELF) {
1735                state = ACC.STATE_NORMAL;
1736            } else if (childID >= 0 && childID < items.length) {
1737                state = ACC.STATE_SELECTABLE;
1738                if (isFocusControl()) {
1739                    state |= ACC.STATE_FOCUSABLE;
1740                }
1741                if (selectedIndex == childID) {
1742                    state |= ACC.STATE_SELECTED;
1743                    if (isFocusControl()) {
1744                        state |= ACC.STATE_FOCUSED;
1745                    }
1746                }
1747            } else if (childID == items.length + CHEVRON_CHILD_ID) {
1748                state = showChevron ? ACC.STATE_NORMAL : ACC.STATE_INVISIBLE;
1749            } else if (childID == items.length + MINIMIZE_CHILD_ID) {
1750                state = showMin ? ACC.STATE_NORMAL : ACC.STATE_INVISIBLE;
1751            } else if (childID == items.length + MAXIMIZE_CHILD_ID) {
1752                state = showMax ? ACC.STATE_NORMAL : ACC.STATE_INVISIBLE;
1753            }
1754            e.detail = state;
1755        }
1756        
1757        public void getChildren(AccessibleControlEvent e) {
1758            int childIdCount = items.length + EXTRA_CHILD_ID_COUNT;
1759            Object JavaDoc[] children = new Object JavaDoc[childIdCount];
1760            for (int i = 0; i < childIdCount; i++) {
1761                children[i] = new Integer JavaDoc(i);
1762            }
1763            e.children = children;
1764        }
1765    });
1766    
1767    addListener(SWT.Selection, new Listener() {
1768        public void handleEvent(Event event) {
1769            if (isFocusControl()) {
1770                if (selectedIndex == -1) {
1771                    accessible.setFocus(ACC.CHILDID_SELF);
1772                } else {
1773                    accessible.setFocus(selectedIndex);
1774                }
1775            }
1776        }
1777    });
1778
1779    addListener(SWT.FocusIn, new Listener() {
1780        public void handleEvent(Event event) {
1781            if (selectedIndex == -1) {
1782                accessible.setFocus(ACC.CHILDID_SELF);
1783            } else {
1784                accessible.setFocus(selectedIndex);
1785            }
1786        }
1787    });
1788}
1789void onKeyDown (Event event) {
1790    switch (event.keyCode) {
1791        case SWT.ARROW_LEFT:
1792        case SWT.ARROW_RIGHT:
1793            int count = items.length;
1794            if (count == 0) return;
1795            if (selectedIndex == -1) return;
1796            int leadKey = (getStyle() & SWT.RIGHT_TO_LEFT) != 0 ? SWT.ARROW_RIGHT : SWT.ARROW_LEFT;
1797            int offset = event.keyCode == leadKey ? -1 : 1;
1798            int index;
1799            if (!mru) {
1800                index = selectedIndex + offset;
1801            } else {
1802                int[] visible = new int[items.length];
1803                int idx = 0;
1804                int current = -1;
1805                for (int i = 0; i < items.length; i++) {
1806                    if (items[i].showing) {
1807                        if (i == selectedIndex) current = idx;
1808                        visible [idx++] = i;
1809                    }
1810                }
1811                if (current + offset >= 0 && current + offset < idx){
1812                    index = visible [current + offset];
1813                } else {
1814                    if (showChevron) {
1815                        CTabFolderEvent e = new CTabFolderEvent(this);
1816                        e.widget = this;
1817                        e.time = event.time;
1818                        e.x = chevronRect.x;
1819                        e.y = chevronRect.y;
1820                        e.width = chevronRect.width;
1821                        e.height = chevronRect.height;
1822                        e.doit = true;
1823                        for (int i = 0; i < folderListeners.length; i++) {
1824                            folderListeners[i].showList(e);
1825                        }
1826                        if (e.doit && !isDisposed()) {
1827                            showList(chevronRect);
1828                        }
1829                    }
1830                    return;
1831                }
1832            }
1833            if (index < 0 || index >= count) return;
1834            setSelection (index, true);
1835            forceFocus();
1836    }
1837}
1838void onDispose(Event event) {
1839    removeListener(SWT.Dispose, listener);
1840    notifyListeners(SWT.Dispose, event);
1841    event.type = SWT.None;
1842    /*
1843     * Usually when an item is disposed, destroyItem will change the size of the items array,
1844     * reset the bounds of all the tabs and manage the widget associated with the tab.
1845     * Since the whole folder is being disposed, this is not necessary. For speed
1846     * the inDispose flag is used to skip over this part of the item dispose.
1847     */

1848    inDispose = true;
1849
1850    if (showMenu != null && !showMenu.isDisposed()) {
1851        showMenu.dispose();
1852        showMenu = null;
1853    }
1854    int length = items.length;
1855    for (int i = 0; i < length; i++) {
1856        if (items[i] != null) {
1857            items[i].dispose();
1858        }
1859    }
1860    
1861    selectionGradientColors = null;
1862    selectionGradientPercents = null;
1863    selectionBgImage = null;
1864
1865    selectionBackground = null;
1866    selectionForeground = null;
1867    disposeSelectionHighlightGradientColors();
1868}
1869void onDragDetect(Event event) {
1870    boolean consume = false;
1871    if (chevronRect.contains(event.x, event.y) ||
1872        minRect.contains(event.x, event.y) ||
1873        maxRect.contains(event.x, event.y)){
1874        consume = true;
1875    } else {
1876        for (int i = 0; i < items.length; i++) {
1877            if (items[i].closeRect.contains(event.x, event.y)) {
1878                    consume = true;
1879                    break;
1880            }
1881        }
1882    }
1883    if (consume) {
1884        event.type = SWT.None;
1885    }
1886}
1887void onFocus(Event event) {
1888    checkWidget();
1889    if (selectedIndex >= 0) {
1890        redraw();
1891    } else {
1892        setSelection(0, true);
1893    }
1894}
1895boolean onMnemonic (Event event) {
1896    char key = event.character;
1897    for (int i = 0; i < items.length; i++) {
1898        if (items[i] != null) {
1899            char mnemonic = _findMnemonic (items[i].getText ());
1900            if (mnemonic != '\0') {
1901                if (Character.toLowerCase (key) == mnemonic) {
1902                    setSelection(i, true);
1903                    return true;
1904                }
1905            }
1906        }
1907    }
1908    return false;
1909}
1910void onMouseDoubleClick(Event event) {
1911    if (event.button != 1 ||
1912        (event.stateMask & SWT.BUTTON2) != 0 ||
1913        (event.stateMask & SWT.BUTTON3) != 0) return;
1914    Event e = new Event();
1915    e.item = getItem(new Point(event.x, event.y));
1916    if (e.item != null) {
1917        notifyListeners(SWT.DefaultSelection, e);
1918    }
1919}
1920void onMouse(Event event) {
1921    int x = event.x, y = event.y;
1922    switch (event.type) {
1923        case SWT.MouseEnter: {
1924            setToolTipText(null);
1925            break;
1926        }
1927        case SWT.MouseExit: {
1928            if (minImageState != NORMAL) {
1929                minImageState = NORMAL;
1930                redraw(minRect.x, minRect.y, minRect.width, minRect.height, false);
1931            }
1932            if (maxImageState != NORMAL) {
1933                maxImageState = NORMAL;
1934                redraw(maxRect.x, maxRect.y, maxRect.width, maxRect.height, false);
1935            }
1936            if (chevronImageState != NORMAL) {
1937                chevronImageState = NORMAL;
1938                redraw(chevronRect.x, chevronRect.y, chevronRect.width, chevronRect.height, false);
1939            }
1940            for (int i=0; i<items.length; i++) {
1941                CTabItem item = items[i];
1942                if (i != selectedIndex && item.closeImageState != NONE) {
1943                    item.closeImageState = NONE;
1944                    redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1945                }
1946                if (i == selectedIndex && item.closeImageState != NORMAL) {
1947                    item.closeImageState = NORMAL;
1948                    redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1949                }
1950            }
1951            break;
1952        }
1953        case SWT.MouseDown: {
1954            if (minRect.contains(x, y)) {
1955                if (event.button != 1) return;
1956                minImageState = SELECTED;
1957                redraw(minRect.x, minRect.y, minRect.width, minRect.height, false);
1958                update();
1959                return;
1960            }
1961            if (maxRect.contains(x, y)) {
1962                if (event.button != 1) return;
1963                maxImageState = SELECTED;
1964                redraw(maxRect.x, maxRect.y, maxRect.width, maxRect.height, false);
1965                update();
1966                return;
1967            }
1968            if (chevronRect.contains(x, y)) {
1969                if (event.button != 1) return;
1970                if (chevronImageState != HOT) {
1971                    chevronImageState = HOT;
1972                } else {
1973                    chevronImageState = SELECTED;
1974                }
1975                redraw(chevronRect.x, chevronRect.y, chevronRect.width, chevronRect.height, false);
1976                update();
1977                return;
1978            }
1979            CTabItem item = null;
1980            if (single) {
1981                if (selectedIndex != -1) {
1982                    Rectangle bounds = items[selectedIndex].getBounds();
1983                    if (bounds.contains(x, y)){
1984                        item = items[selectedIndex];
1985                    }
1986                }
1987            } else {
1988                for (int i=0; i<items.length; i++) {
1989                    Rectangle bounds = items[i].getBounds();
1990                    if (bounds.contains(x, y)){
1991                        item = items[i];
1992                    }
1993                }
1994            }
1995            if (item != null) {
1996                if (item.closeRect.contains(x,y)){
1997                    if (event.button != 1) return;
1998                    item.closeImageState = SELECTED;
1999                    redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
2000                    update();
2001                    return;
2002                }
2003                int index = indexOf(item);
2004                if (item.showing){
2005                    setSelection(index, true);
2006                }
2007                return;
2008            }
2009            break;
2010        }
2011        case SWT.MouseMove: {
2012            _setToolTipText(event.x, event.y);
2013            boolean close = false, minimize = false, maximize = false, chevron = false;
2014            if (minRect.contains(x, y)) {
2015                minimize = true;
2016                if (minImageState != SELECTED && minImageState != HOT) {
2017                    minImageState = HOT;
2018                    redraw(minRect.x, minRect.y, minRect.width, minRect.height, false);
2019                }
2020            }
2021            if (maxRect.contains(x, y)) {
2022                maximize = true;
2023                if (maxImageState != SELECTED && maxImageState != HOT) {
2024                    maxImageState = HOT;
2025                    redraw(maxRect.x, maxRect.y, maxRect.width, maxRect.height, false);
2026                }
2027            }
2028            if (chevronRect.contains(x, y)) {
2029                chevron = true;
2030                if (chevronImageState != SELECTED && chevronImageState != HOT) {
2031                    chevronImageState = HOT;
2032                    redraw(chevronRect.x, chevronRect.y, chevronRect.width, chevronRect.height, false);
2033                }
2034            }
2035            if (minImageState != NORMAL && !minimize) {
2036                minImageState = NORMAL;
2037                redraw(minRect.x, minRect.y, minRect.width, minRect.height, false);
2038            }
2039            if (maxImageState != NORMAL && !maximize) {
2040                maxImageState = NORMAL;
2041                redraw(maxRect.x, maxRect.y, maxRect.width, maxRect.height, false);
2042            }
2043            if (chevronImageState != NORMAL && !chevron) {
2044                chevronImageState = NORMAL;
2045                redraw(chevronRect.x, chevronRect.y, chevronRect.width, chevronRect.height, false);
2046            }
2047            for (int i=0; i<items.length; i++) {
2048                CTabItem item = items[i];
2049                close = false;
2050                if (item.getBounds().contains(x, y)) {
2051                    close = true;
2052                    if (item.closeRect.contains(x, y)) {
2053                        if (item.closeImageState != SELECTED && item.closeImageState != HOT) {
2054                            item.closeImageState = HOT;
2055                            redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
2056                        }
2057                    } else {
2058                        if (item.closeImageState != NORMAL) {
2059                            item.closeImageState = NORMAL;
2060                            redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
2061                        }
2062                    }
2063                }
2064                if (i != selectedIndex && item.closeImageState != NONE && !close) {
2065                    item.closeImageState = NONE;
2066                    redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
2067                }
2068                if (i == selectedIndex && item.closeImageState != NORMAL && !close) {
2069                    item.closeImageState = NORMAL;
2070                    redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
2071                }
2072            }
2073            break;
2074        }
2075        case SWT.MouseUp: {
2076            if (event.button != 1) return;
2077            if (chevronRect.contains(x, y)) {
2078                boolean selected = chevronImageState == SELECTED;
2079                if (!selected) return;
2080                CTabFolderEvent e = new CTabFolderEvent(this);
2081                e.widget = this;
2082                e.time = event.time;
2083                e.x = chevronRect.x;
2084                e.y = chevronRect.y;
2085                e.width = chevronRect.width;
2086                e.height = chevronRect.height;
2087                e.doit = true;
2088                for (int i = 0; i < folderListeners.length; i++) {
2089                    folderListeners[i].showList(e);
2090                }
2091                if (e.doit && !isDisposed()) {
2092                    showList(chevronRect);
2093                }
2094                return;
2095            }
2096            if (minRect.contains(x, y)) {
2097                boolean selected = minImageState == SELECTED;
2098                minImageState = HOT;
2099                redraw(minRect.x, minRect.y, minRect.width, minRect.height, false);
2100                if (!selected) return;
2101                CTabFolderEvent e = new CTabFolderEvent(this);
2102                e.widget = this;
2103                e.time = event.time;
2104                for (int i = 0; i < folderListeners.length; i++) {
2105                    if (minimized) {
2106                        folderListeners[i].restore(e);
2107                    } else {
2108                        folderListeners[i].minimize(e);
2109                    }
2110                }
2111                return;
2112            }
2113            if (maxRect.contains(x, y)) {
2114                boolean selected = maxImageState == SELECTED;
2115                maxImageState = HOT;
2116                redraw(maxRect.x, maxRect.y, maxRect.width, maxRect.height, false);
2117                if (!selected) return;
2118                CTabFolderEvent e = new CTabFolderEvent(this);
2119                e.widget = this;
2120                e.time = event.time;
2121                for (int i = 0; i < folderListeners.length; i++) {
2122                    if (maximized) {
2123                        folderListeners[i].restore(e);
2124                    } else {
2125                        folderListeners[i].maximize(e);
2126                    }
2127                }
2128                return;
2129            }
2130            CTabItem item = null;
2131            if (single) {
2132                if (selectedIndex != -1) {
2133                    Rectangle bounds = items[selectedIndex].getBounds();
2134                    if (bounds.contains(x, y)){
2135                        item = items[selectedIndex];
2136                    }
2137                }
2138            } else {
2139                for (int i=0; i<items.length; i++) {
2140                    Rectangle bounds = items[i].getBounds();
2141                    if (bounds.contains(x, y)){
2142                        item = items[i];
2143                    }
2144                }
2145            }
2146            if (item != null) {
2147                if (item.closeRect.contains(x,y)) {
2148                    boolean selected = item.closeImageState == SELECTED;
2149                    item.closeImageState = HOT;
2150                    redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
2151                    if (!selected) return;
2152                    CTabFolderEvent e = new CTabFolderEvent(this);
2153                    e.widget = this;
2154                    e.time = event.time;
2155                    e.item = item;
2156                    e.doit = true;
2157                    for (int j = 0; j < folderListeners.length; j++) {
2158                        CTabFolder2Listener listener = folderListeners[j];
2159                        listener.close(e);
2160                    }
2161                    for (int j = 0; j < tabListeners.length; j++) {
2162                        CTabFolderListener listener = tabListeners[j];
2163                        listener.itemClosed(e);
2164                    }
2165                    if (e.doit) {
2166                        item.dispose();
2167                        Display display = getDisplay();
2168                        Point pt = display.getCursorLocation();
2169                        pt = display.map(null, this, pt.x, pt.y);
2170                        CTabItem nextItem = getItem(pt);
2171                        if (nextItem != null) {
2172                            if (nextItem.closeRect.contains(pt)) {
2173                                if (nextItem.closeImageState != SELECTED && nextItem.closeImageState != HOT) {
2174                                    nextItem.closeImageState = HOT;
2175                                    redraw(nextItem.closeRect.x, nextItem.closeRect.y, nextItem.closeRect.width, nextItem.closeRect.height, false);
2176                                }
2177                            } else {
2178                                if (nextItem.closeImageState != NORMAL) {
2179                                    nextItem.closeImageState = NORMAL;
2180                                    redraw(nextItem.closeRect.x, nextItem.closeRect.y, nextItem.closeRect.width, nextItem.closeRect.height, false);
2181                                }
2182                            }
2183                        }
2184                    }
2185                    return;
2186                }
2187            }
2188        }
2189    }
2190}
2191boolean onPageTraversal(Event event) {
2192    int count = items.length;
2193    if (count == 0) return false;
2194    int index = selectedIndex;
2195    if (index == -1) {
2196        index = 0;
2197    } else {
2198        int offset = (event.detail == SWT.TRAVERSE_PAGE_NEXT) ? 1 : -1;
2199        if (!mru) {
2200            index = (selectedIndex + offset + count) % count;
2201        } else {
2202            int[] visible = new int[items.length];
2203            int idx = 0;
2204            int current = -1;
2205            for (int i = 0; i < items.length; i++) {
2206                if (items[i].showing) {
2207                    if (i == selectedIndex) current = idx;
2208                    visible [idx++] = i;
2209                }
2210            }
2211            if (current + offset >= 0 && current + offset < idx){
2212                index = visible [current + offset];
2213            } else {
2214                if (showChevron) {
2215                    CTabFolderEvent e = new CTabFolderEvent(this);
2216                    e.widget = this;
2217                    e.time = event.time;
2218                    e.x = chevronRect.x;
2219                    e.y = chevronRect.y;
2220                    e.width = chevronRect.width;
2221                    e.height = chevronRect.height;
2222                    e.doit = true;
2223                    for (int i = 0; i < folderListeners.length; i++) {
2224                        folderListeners[i].showList(e);
2225                    }
2226                    if (e.doit && !isDisposed()) {
2227                        showList(chevronRect);
2228                    }
2229                }
2230                return true;
2231            }
2232        }
2233    }
2234    setSelection (index, true);
2235    return true;
2236}
2237void onPaint(Event event) {
2238    if (inDispose) return;
2239    Font font = getFont();
2240    if (oldFont == null || !oldFont.equals(font)) {
2241        // handle case where default font changes
2242
oldFont = font;
2243        if (!updateTabHeight(false)) {
2244            updateItems();
2245            redraw();
2246            return;
2247        }
2248    }
2249
2250    GC gc = event.gc;
2251    Font gcFont = gc.getFont();
2252    Color gcBackground = gc.getBackground();
2253    Color gcForeground = gc.getForeground();
2254    
2255// Useful for debugging paint problems
2256
//{
2257
//Point size = getSize();
2258
//gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_GREEN));
2259
//gc.fillRectangle(-10, -10, size.x + 20, size.y+20);
2260
//}
2261

2262    drawBody(event);
2263    
2264    gc.setFont(gcFont);
2265    gc.setForeground(gcForeground);
2266    gc.setBackground(gcBackground);
2267    
2268    drawTabArea(event);
2269    
2270    gc.setFont(gcFont);
2271    gc.setForeground(gcForeground);
2272    gc.setBackground(gcBackground);
2273}
2274
2275void onResize() {
2276    if (updateItems()) redrawTabs();
2277    
2278    Point size = getSize();
2279    if (oldSize == null) {
2280        redraw();
2281    } else {
2282        if (onBottom && size.y != oldSize.y) {
2283            redraw();
2284        } else {
2285            int x1 = Math.min(size.x, oldSize.x);
2286            if (size.x != oldSize.x) x1 -= borderRight + highlight_margin + 2;
2287            if (!simple) x1 -= 5; // rounded top right corner
2288
int y1 = Math.min(size.y, oldSize.y);
2289            if (size.y != oldSize.y) y1 -= borderBottom + highlight_margin;
2290            int x2 = Math.max(size.x, oldSize.x);
2291            int y2 = Math.max(size.y, oldSize.y);
2292            redraw(0, y1, x2, y2 - y1, false);
2293            redraw(x1, 0, x2 - x1, y2, false);
2294        }
2295    }
2296    oldSize = size;
2297}
2298void onTraverse (Event event) {
2299    switch (event.detail) {
2300        case SWT.TRAVERSE_ESCAPE:
2301        case SWT.TRAVERSE_RETURN:
2302        case SWT.TRAVERSE_TAB_NEXT:
2303        case SWT.TRAVERSE_TAB_PREVIOUS:
2304            Control focusControl = getDisplay().getFocusControl();
2305            if (focusControl == this) event.doit = true;
2306            break;
2307        case SWT.TRAVERSE_MNEMONIC:
2308            event.doit = onMnemonic(event);
2309            if (event.doit) event.detail = SWT.TRAVERSE_NONE;
2310            break;
2311        case SWT.TRAVERSE_PAGE_NEXT:
2312        case SWT.TRAVERSE_PAGE_PREVIOUS:
2313            event.doit = onPageTraversal(event);
2314            event.detail = SWT.TRAVERSE_NONE;
2315            break;
2316    }
2317}
2318void redrawTabs() {
2319    Point size = getSize();
2320    if (onBottom) {
2321        redraw(0, size.y - borderBottom - tabHeight - highlight_header - 1, size.x, borderBottom + tabHeight + highlight_header + 1, false);
2322    } else {
2323        redraw(0, 0, size.x, borderTop + tabHeight + highlight_header + 1, false);
2324    }
2325}
2326/**
2327 * Removes the listener.
2328 *
2329 * @param listener the listener
2330 *
2331 * @exception IllegalArgumentException <ul>
2332 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
2333 * </ul>
2334 *
2335 * @exception SWTException <ul>
2336 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
2337 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
2338 * </ul>
2339 *
2340 * @see #addCTabFolder2Listener(CTabFolder2Listener)
2341 *
2342 * @since 3.0
2343 */

2344public void removeCTabFolder2Listener(CTabFolder2Listener listener) {
2345    checkWidget();
2346    if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
2347    if (folderListeners.length == 0) return;
2348    int index = -1;
2349    for (int i = 0; i < folderListeners.length; i++) {
2350        if (listener == folderListeners[i]){
2351            index = i;
2352            break;
2353        }
2354    }
2355    if (index == -1) return;
2356    if (folderListeners.length == 1) {
2357        folderListeners = new CTabFolder2Listener[0];
2358        return;
2359    }
2360    CTabFolder2Listener[] newTabListeners = new CTabFolder2Listener[folderListeners.length - 1];
2361    System.arraycopy(folderListeners, 0, newTabListeners, 0, index);
2362    System.arraycopy(folderListeners, index + 1, newTabListeners, index, folderListeners.length - index - 1);
2363    folderListeners = newTabListeners;
2364}
2365/**
2366 * Removes the listener.
2367 *
2368 * @param listener the listener
2369 *
2370 * @exception IllegalArgumentException <ul>
2371 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
2372 * </ul>
2373 *
2374 * @exception SWTException <ul>
2375 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
2376 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
2377 * </ul>
2378 *
2379 * @deprecated see removeCTabFolderCloseListener(CTabFolderListener)
2380 */

2381public void removeCTabFolderListener(CTabFolderListener listener) {
2382    checkWidget();
2383    if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
2384    if (tabListeners.length == 0) return;
2385    int index = -1;
2386    for (int i = 0; i < tabListeners.length; i++) {
2387        if (listener == tabListeners[i]){
2388            index = i;
2389            break;
2390        }
2391    }
2392    if (index == -1) return;
2393    if (tabListeners.length == 1) {
2394        tabListeners = new CTabFolderListener[0];
2395        return;
2396    }
2397    CTabFolderListener[] newTabListeners = new CTabFolderListener[tabListeners.length - 1];
2398    System.arraycopy(tabListeners, 0, newTabListeners, 0, index);
2399    System.arraycopy(tabListeners, index + 1, newTabListeners, index, tabListeners.length - index - 1);
2400    tabListeners = newTabListeners;
2401}
2402/**
2403 * Removes the listener from the collection of listeners who will
2404 * be notified when the user changes the receiver's selection.
2405 *
2406 * @param listener the listener which should no longer be notified
2407 *
2408 * @exception IllegalArgumentException <ul>
2409 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
2410 * </ul>
2411 * @exception SWTException <ul>
2412 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2413 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2414 * </ul>
2415 *
2416 * @see SelectionListener
2417 * @see #addSelectionListener
2418 */

2419public void removeSelectionListener(SelectionListener listener) {
2420    checkWidget();
2421    if (listener == null) {
2422        SWT.error(SWT.ERROR_NULL_ARGUMENT);
2423    }
2424    removeListener(SWT.Selection, listener);
2425    removeListener(SWT.DefaultSelection, listener);
2426}
2427public void setBackground (Color color) {
2428    super.setBackground(color);
2429    redraw();
2430}
2431/**
2432 * Specify a gradient of colours to be drawn in the background of the unselected tabs.
2433 * For example to draw a gradient that varies from dark blue to blue and then to
2434 * white, use the following call to setBackground:
2435 * <pre>
2436 * cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
2437 * display.getSystemColor(SWT.COLOR_BLUE),
2438 * display.getSystemColor(SWT.COLOR_WHITE),
2439 * display.getSystemColor(SWT.COLOR_WHITE)},
2440 * new int[] {25, 50, 100});
2441 * </pre>
2442 *
2443 * @param colors an array of Color that specifies the colors to appear in the gradient
2444 * in order of appearance left to right. The value <code>null</code> clears the
2445 * background gradient. The value <code>null</code> can be used inside the array of
2446 * Color to specify the background color.
2447 * @param percents an array of integers between 0 and 100 specifying the percent of the width
2448 * of the widget at which the color should change. The size of the percents array must be one
2449 * less than the size of the colors array.
2450 *
2451 * @exception SWTException <ul>
2452 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
2453 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
2454 * </ul>
2455 *
2456 * @since 3.0
2457 */

2458void setBackground(Color[] colors, int[] percents) {
2459    setBackground(colors, percents, false);
2460}
2461/**
2462 * Specify a gradient of colours to be drawn in the background of the unselected tab.
2463 * For example to draw a vertical gradient that varies from dark blue to blue and then to
2464 * white, use the following call to setBackground:
2465 * <pre>
2466 * cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
2467 * display.getSystemColor(SWT.COLOR_BLUE),
2468 * display.getSystemColor(SWT.COLOR_WHITE),
2469 * display.getSystemColor(SWT.COLOR_WHITE)},
2470 * new int[] {25, 50, 100}, true);
2471 * </pre>
2472 *
2473 * @param colors an array of Color that specifies the colors to appear in the gradient
2474 * in order of appearance left to right. The value <code>null</code> clears the
2475 * background gradient. The value <code>null</code> can be used inside the array of
2476 * Color to specify the background color.
2477 * @param percents an array of integers between 0 and 100 specifying the percent of the width
2478 * of the widget at which the color should change. The size of the percents array must be one
2479 * less than the size of the colors array.
2480 *
2481 * @param vertical indicate the direction of the gradient. True is vertical and false is horizontal.
2482 *
2483 * @exception SWTException <ul>
2484 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
2485 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
2486 * </ul>
2487 *
2488 * @since 3.0
2489 */

2490void setBackground(Color[] colors, int[] percents, boolean vertical) {
2491    checkWidget();
2492    if (colors != null) {
2493        if (percents == null || percents.length != colors.length - 1) {
2494            SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2495        }
2496        for (int i = 0; i < percents.length; i++) {
2497            if (percents[i] < 0 || percents[i] > 100) {
2498                SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2499            }
2500            if (i > 0 && percents[i] < percents[i-1]) {
2501                SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2502            }
2503        }
2504        if (getDisplay().getDepth() < 15) {
2505            // Don't use gradients on low color displays
2506
colors = new Color[] {colors[colors.length - 1]};
2507            percents = new int[] {};
2508        }
2509    }
2510    
2511    // Are these settings the same as before?
2512
if (bgImage == null) {
2513        if ((gradientColors != null) && (colors != null) &&
2514            (gradientColors.length == colors.length)) {
2515            boolean same = false;
2516            for (int i = 0; i < gradientColors.length; i++) {
2517                if (gradientColors[i] == null) {
2518                    same = colors[i] == null;
2519                } else {
2520                    same = gradientColors[i].equals(colors[i]);
2521                }
2522                if (!same) break;
2523            }
2524            if (same) {
2525                for (int i = 0; i < gradientPercents.length; i++) {
2526                    same = gradientPercents[i] == percents[i];
2527                    if (!same) break;
2528                }
2529            }
2530            if (same && this.gradientVertical == vertical) return;
2531        }
2532    } else {
2533        bgImage = null;
2534    }
2535    // Store the new settings
2536
if (colors == null) {
2537        gradientColors = null;
2538        gradientPercents = null;
2539        gradientVertical = false;
2540        setBackground((Color)null);
2541    } else {
2542        gradientColors = new Color[colors.length];
2543        for (int i = 0; i < colors.length; ++i) {
2544            gradientColors[i] = colors[i];
2545        }
2546        gradientPercents = new int[percents.length];
2547        for (int i = 0; i < percents.length; ++i) {
2548            gradientPercents[i] = percents[i];
2549        }
2550        gradientVertical = vertical;
2551        setBackground(gradientColors[gradientColors.length-1]);
2552    }
2553
2554    // Refresh with the new settings
2555
redraw();
2556}
2557
2558/**
2559 * Set the image to be drawn in the background of the unselected tab. Image
2560 * is stretched or compressed to cover entire unselected tab area.
2561 *
2562 * @param image the image to be drawn in the background
2563 *
2564 * @exception SWTException <ul>
2565 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2566 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2567 * </ul>
2568 *
2569 * @since 3.0
2570 */

2571void setBackground(Image image) {
2572    checkWidget();
2573    if (image == bgImage) return;
2574    if (image != null) {
2575        gradientColors = null;
2576        gradientPercents = null;
2577    }
2578    bgImage = image;
2579    redraw();
2580}
2581/**
2582 * Toggle the visibility of the border
2583 *
2584 * @param show true if the border should be displayed
2585 *
2586 * @exception SWTException <ul>
2587 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2588 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2589 * </ul>
2590 */

2591public void setBorderVisible(boolean show) {
2592    checkWidget();
2593    if ((borderLeft == 1) == show) return;
2594    borderLeft = borderRight = show ? 1 : 0;
2595    borderTop = onBottom ? borderLeft : 0;
2596    borderBottom = onBottom ? 0 : borderLeft;
2597    Rectangle rectBefore = getClientArea();
2598    updateItems();
2599    Rectangle rectAfter = getClientArea();
2600    if (!rectBefore.equals(rectAfter)) {
2601        notifyListeners(SWT.Resize, new Event());
2602    }
2603    redraw();
2604}
2605void setButtonBounds() {
2606    Point size = getSize();
2607    int oldX, oldY, oldWidth, oldHeight;
2608    // max button
2609
oldX = maxRect.x;
2610    oldY = maxRect.y;
2611    oldWidth = maxRect.width;
2612    oldHeight = maxRect.height;
2613    maxRect.x = maxRect.y = maxRect.width = maxRect.height = 0;
2614    if (showMax) {
2615        maxRect.x = size.x - borderRight - BUTTON_SIZE - 3;
2616        if (borderRight > 0) maxRect.x += 1;
2617        maxRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - BUTTON_SIZE)/2: borderTop + (tabHeight - BUTTON_SIZE)/2;
2618        maxRect.width = BUTTON_SIZE;
2619        maxRect.height = BUTTON_SIZE;
2620    }
2621    if (oldX != maxRect.x || oldWidth != maxRect.width ||
2622        oldY != maxRect.y || oldHeight != maxRect.height) {
2623        int left = Math.min(oldX, maxRect.x);
2624        int right = Math.max(oldX + oldWidth, maxRect.x + maxRect.width);
2625        int top = onBottom ? size.y - borderBottom - tabHeight: borderTop + 1;
2626        redraw(left, top, right - left, tabHeight, false);
2627    }
2628    
2629    // min button
2630
oldX = minRect.x;
2631    oldY = minRect.y;
2632    oldWidth = minRect.width;
2633    oldHeight = minRect.height;
2634    minRect.x = minRect.y = minRect.width = minRect.height = 0;
2635    if (showMin) {
2636        minRect.x = size.x - borderRight - maxRect.width - BUTTON_SIZE - 3;
2637        if (borderRight > 0) minRect.x += 1;
2638        minRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - BUTTON_SIZE)/2: borderTop + (tabHeight - BUTTON_SIZE)/2;
2639        minRect.width = BUTTON_SIZE;
2640        minRect.height = BUTTON_SIZE;
2641    }
2642    if (oldX != minRect.x || oldWidth != minRect.width ||
2643        oldY != minRect.y || oldHeight != minRect.height) {
2644        int left = Math.min(oldX, minRect.x);
2645        int right = Math.max(oldX + oldWidth, minRect.x + minRect.width);
2646        int top = onBottom ? size.y - borderBottom - tabHeight: borderTop + 1;
2647        redraw(left, top, right - left, tabHeight, false);
2648    }
2649    
2650    // top right control
2651
oldX = topRightRect.x;
2652    oldY = topRightRect.y;
2653    oldWidth = topRightRect.width;
2654    oldHeight = topRightRect.height;
2655    topRightRect.x = topRightRect.y = topRightRect.width = topRightRect.height = 0;
2656    if (topRight != null) {
2657        switch (topRightAlignment) {
2658            case SWT.FILL: {
2659                int rightEdge = size.x - borderRight - 3 - maxRect.width - minRect.width;
2660                if (!simple && borderRight > 0 && !showMax && !showMin) rightEdge -= 2;
2661                if (single) {
2662                    if (items.length == 0 || selectedIndex == -1) {
2663                        topRightRect.x = borderLeft + 3;
2664                        topRightRect.width = rightEdge - topRightRect.x;
2665                    } else {
2666                        // fill size is 0 if item compressed
2667
CTabItem item = items[selectedIndex];
2668                        if (item.x + item.width + 7 + 3*BUTTON_SIZE/2 >= rightEdge) break;
2669                        topRightRect.x = item.x + item.width + 7 + 3*BUTTON_SIZE/2;
2670                        topRightRect.width = rightEdge - topRightRect.x;
2671                    }
2672                } else {
2673                    // fill size is 0 if chevron showing
2674
if (showChevron) break;
2675                    if (items.length == 0) {
2676                        topRightRect.x = borderLeft + 3;
2677                    } else {
2678                        CTabItem item = items[items.length - 1];
2679                        topRightRect.x = item.x + item.width;
2680                        if (!simple && items.length - 1 == selectedIndex) topRightRect.x += curveWidth - curveIndent;
2681                    }
2682                    topRightRect.width = Math.max(0, rightEdge - topRightRect.x);
2683                }
2684                topRightRect.y = onBottom ? size.y - borderBottom - tabHeight: borderTop + 1;
2685                topRightRect.height = tabHeight - 1;
2686                break;
2687            }
2688            case SWT.RIGHT: {
2689                Point topRightSize = topRight.computeSize(SWT.DEFAULT, tabHeight, false);
2690                int rightEdge = size.x - borderRight - 3 - maxRect.width - minRect.width;
2691                if (!simple && borderRight > 0 && !showMax && !showMin) rightEdge -= 2;
2692                topRightRect.x = rightEdge - topRightSize.x;
2693                topRightRect.width = topRightSize.x;
2694                topRightRect.y = onBottom ? size.y - borderBottom - tabHeight: borderTop + 1;
2695                topRightRect.height = tabHeight - 1;
2696            }
2697        }
2698        topRight.setBounds(topRightRect);
2699    }
2700    if (oldX != topRightRect.x || oldWidth != topRightRect.width ||
2701        oldY != topRightRect.y || oldHeight != topRightRect.height) {
2702        int left = Math.min(oldX, topRightRect.x);
2703        int right = Math.max(oldX + oldWidth, topRightRect.x + topRightRect.width);
2704        int top = onBottom ? size.y - borderBottom - tabHeight : borderTop + 1;
2705        redraw(left, top, right - left, tabHeight, false);
2706    }
2707    
2708    // chevron button
2709
oldX = chevronRect.x;
2710    oldY = chevronRect.y;
2711    oldWidth = chevronRect.width;
2712    oldHeight = chevronRect.height;
2713    chevronRect.x = chevronRect.y = chevronRect.height = chevronRect.width = 0;
2714    if (single) {
2715        if (selectedIndex == -1 || items.length > 1) {
2716            chevronRect.width = 3*BUTTON_SIZE/2;
2717            chevronRect.height = BUTTON_SIZE;
2718            chevronRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - chevronRect.height)/2 : borderTop + (tabHeight - chevronRect.height)/2;
2719            if (selectedIndex == -1) {
2720                chevronRect.x = size.x - borderRight - 3 - minRect.width - maxRect.width - topRightRect.width - chevronRect.width;
2721            } else {
2722                CTabItem item = items[selectedIndex];
2723                int w = size.x - borderRight - 3 - minRect.width - maxRect.width - chevronRect.width;
2724                if (topRightRect.width > 0) w -= topRightRect.width + 3;
2725                chevronRect.x = Math.min(item.x + item.width + 3, w);
2726            }
2727            if (borderRight > 0) chevronRect.x += 1;
2728        }
2729    } else {
2730        if (showChevron) {
2731            chevronRect.width = 3*BUTTON_SIZE/2;
2732            chevronRect.height = BUTTON_SIZE;
2733            int i = 0, lastIndex = -1;
2734            while (i < priority.length && items[priority[i]].showing) {
2735                lastIndex = Math.max(lastIndex, priority[i++]);
2736            }
2737            if (lastIndex == -1) lastIndex = firstIndex;
2738            CTabItem lastItem = items[lastIndex];
2739            int w = lastItem.x + lastItem.width + 3;
2740            if (!simple && lastIndex == selectedIndex) w += curveWidth - 2*curveIndent;
2741            chevronRect.x = Math.min(w, getRightItemEdge());
2742            chevronRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - chevronRect.height)/2 : borderTop + (tabHeight - chevronRect.height)/2;
2743        }
2744    }
2745    if (oldX != chevronRect.x || oldWidth != chevronRect.width ||
2746        oldY != chevronRect.y || oldHeight != chevronRect.height) {
2747        int left = Math.min(oldX, chevronRect.x);
2748        int right = Math.max(oldX + oldWidth, chevronRect.x + chevronRect.width);
2749        int top = onBottom ? size.y - borderBottom - tabHeight: borderTop + 1;
2750        redraw(left, top, right - left, tabHeight, false);
2751    }
2752}
2753public void setFont(Font font) {
2754    checkWidget();
2755    if (font != null && font.equals(getFont())) return;
2756    super.setFont(font);
2757    oldFont = getFont();
2758    if (!updateTabHeight(false)) {
2759        updateItems();
2760        redraw();
2761    }
2762}
2763public void setForeground (Color color) {
2764    super.setForeground(color);
2765    redraw();
2766}
2767/**
2768 * Display an insert marker before or after the specified tab item.
2769 *
2770 * A value of null will clear the mark.
2771 *
2772 * @param item the item with which the mark is associated or null
2773 *
2774 * @param after true if the mark should be displayed after the specified item
2775 *
2776 * @exception SWTException <ul>
2777 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2778 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2779 * </ul>
2780 */

2781public void setInsertMark(CTabItem item, boolean after) {
2782    checkWidget();
2783}
2784/**
2785 * Display an insert marker before or after the specified tab item.
2786 *
2787 * A value of -1 will clear the mark.
2788 *
2789 * @param index the index of the item with which the mark is associated or null
2790 *
2791 * @param after true if the mark should be displayed after the specified item
2792 *
2793 * @exception IllegalArgumentException<ul>
2794 * </ul>
2795 *
2796 * @exception SWTException <ul>
2797 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2798 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2799 * </ul>
2800 */

2801public void setInsertMark(int index, boolean after) {
2802    checkWidget();
2803    if (index < -1 || index >= getItemCount()) {
2804        SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2805    }
2806}
2807boolean setItemLocation() {
2808    boolean changed = false;
2809    if (items.length == 0) return false;
2810    Point size = getSize();
2811    int y = onBottom ? Math.max(borderBottom, size.y - borderBottom - tabHeight) : borderTop;
2812    if (single) {
2813        int defaultX = getDisplay().getBounds().width + 10; // off screen
2814
for (int i = 0; i < items.length; i++) {
2815            CTabItem item = items[i];
2816            if (i == selectedIndex) {
2817                firstIndex = selectedIndex;
2818                int oldX = item.x, oldY = item.y;
2819                item.x = borderLeft;
2820                item.y = y;
2821                item.showing = true;
2822                if (showClose || item.showClose) {
2823                    item.closeRect.x = borderLeft + CTabItem.LEFT_MARGIN;
2824                    item.closeRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - BUTTON_SIZE)/2: borderTop + (tabHeight - BUTTON_SIZE)/2;
2825                }
2826                if (item.x != oldX || item.y != oldY) changed = true;
2827            } else {
2828                item.x = defaultX;
2829                item.showing = false;
2830            }
2831        }
2832    } else {
2833        int rightItemEdge = getRightItemEdge();
2834        int maxWidth = rightItemEdge - borderLeft;
2835        int width = 0;
2836        for (int i = 0; i < priority.length; i++) {
2837            CTabItem item = items[priority[i]];
2838            width += item.width;
2839            item.showing = i == 0 ? true : item.width > 0 && width <= maxWidth;
2840            if (!simple && priority[i] == selectedIndex) width += curveWidth - 2*curveIndent;
2841        }
2842        int x = 0;
2843        int defaultX = getDisplay().getBounds().width + 10; // off screen
2844
firstIndex = items.length - 1;
2845        for (int i = 0; i < items.length; i++) {
2846            CTabItem item = items[i];
2847            if (!item.showing) {
2848                if (item.x != defaultX) changed = true;
2849                item.x = defaultX;
2850            } else {
2851                firstIndex = Math.min(firstIndex, i);
2852                if (item.x != x || item.y != y) changed = true;
2853                item.x = x;
2854                item.y = y;
2855                if (i == selectedIndex) {
2856                    int edge = Math.min(item.x + item.width, rightItemEdge);
2857                    item.closeRect.x = edge - CTabItem.RIGHT_MARGIN - BUTTON_SIZE;
2858                } else {
2859                    item.closeRect.x = item.x + item.width - CTabItem.RIGHT_MARGIN - BUTTON_SIZE;
2860                }
2861                item.closeRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - BUTTON_SIZE)/2: borderTop + (tabHeight - BUTTON_SIZE)/2;
2862                x = x + item.width;
2863                if (!simple && i == selectedIndex) x += curveWidth - 2*curveIndent;
2864            }
2865        }
2866    }
2867    return changed;
2868}
2869boolean setItemSize() {
2870    boolean changed = false;
2871    if (isDisposed()) return changed;
2872    Point size = getSize();
2873    if (size.x <= 0 || size.y <= 0) return changed;
2874    xClient = borderLeft + marginWidth + highlight_margin;
2875    if (onBottom) {
2876        yClient = borderTop + highlight_margin + marginHeight;
2877    } else {
2878        yClient = borderTop + tabHeight + highlight_header + marginHeight;
2879    }
2880    showChevron = false;
2881    if (single) {
2882        showChevron = true;
2883        if (selectedIndex != -1) {
2884            CTabItem tab = items[selectedIndex];
2885            GC gc = new GC(this);
2886            int width = tab.preferredWidth(gc, true, false);
2887            gc.dispose();
2888            width = Math.min(width, getRightItemEdge() - borderLeft);
2889            if (tab.height != tabHeight || tab.width != width) {
2890                changed = true;
2891                tab.shortenedText = null;
2892                tab.shortenedTextWidth = 0;
2893                tab.height = tabHeight;
2894                tab.width = width;
2895                tab.closeRect.width = tab.closeRect.height = 0;
2896                if (showClose || tab.showClose) {
2897                    tab.closeRect.width = BUTTON_SIZE;
2898                    tab.closeRect.height = BUTTON_SIZE;
2899                }
2900            }
2901        }
2902        return changed;
2903    }
2904    
2905    if (items.length == 0) return changed;
2906
2907    int[] widths;
2908    GC gc = new GC(this);
2909    int tabAreaWidth = size.x - borderLeft - borderRight - 3;
2910    if (showMin) tabAreaWidth -= BUTTON_SIZE;
2911    if (showMax) tabAreaWidth -= BUTTON_SIZE;
2912    if (topRightAlignment == SWT.RIGHT && topRight != null) {
2913        Point rightSize = topRight.computeSize(SWT.DEFAULT, SWT.DEFAULT, false);
2914        tabAreaWidth -= rightSize.x + 3;
2915    }
2916    if (!simple) tabAreaWidth -= curveWidth - 2*curveIndent;
2917    tabAreaWidth = Math.max(0, tabAreaWidth);
2918    
2919    // First, try the minimum tab size at full compression.
2920
int minWidth = 0;
2921    int[] minWidths = new int[items.length];
2922    for (int i = 0; i < priority.length; i++) {
2923        int index = priority[i];
2924        minWidths[index] = items[index].preferredWidth(gc, index == selectedIndex, true);
2925        minWidth += minWidths[index];
2926        if (minWidth > tabAreaWidth) break;
2927    }
2928    if (minWidth > tabAreaWidth) {
2929        // full compression required and a chevron
2930
showChevron = items.length > 1;
2931        if (showChevron) tabAreaWidth -= 3*BUTTON_SIZE/2;
2932        widths = minWidths;
2933        int index = selectedIndex != -1 ? selectedIndex : 0;
2934        if (tabAreaWidth < widths[index]) {
2935            widths[index] = Math.max(0, tabAreaWidth);
2936        }
2937    } else {
2938        int maxWidth = 0;
2939        int[] maxWidths = new int[items.length];
2940        for (int i = 0; i < items.length; i++) {
2941            maxWidths[i] = items[i].preferredWidth(gc, i == selectedIndex, false);
2942            maxWidth += maxWidths[i];
2943        }
2944        if (maxWidth <= tabAreaWidth) {
2945            // no compression required
2946
widths = maxWidths;
2947        } else {
2948            // determine compression for each item
2949
int extra = (tabAreaWidth - minWidth) / items.length;
2950            while (true) {
2951                int large = 0, totalWidth = 0;
2952                for (int i = 0 ; i < items.length; i++) {
2953                    if (maxWidths[i] > minWidths[i] + extra) {
2954                        totalWidth += minWidths[i] + extra;
2955                        large++;
2956                    } else {
2957                        totalWidth += maxWidths[i];
2958                    }
2959                }
2960                if (totalWidth >= tabAreaWidth) {
2961                    extra--;
2962                    break;
2963                }
2964                if (large == 0 || tabAreaWidth - totalWidth < large) break;
2965                extra++;
2966            }
2967            widths = new int[items.length];
2968            for (int i = 0; i < items.length; i++) {
2969                widths[i] = Math.min(maxWidths[i], minWidths[i] + extra);
2970            }
2971        }
2972    }
2973    gc.dispose();
2974
2975    for (int i = 0; i < items.length; i++) {
2976        CTabItem tab = items[i];
2977        int width = widths[i];
2978        if (tab.height != tabHeight || tab.width != width) {
2979            changed = true;
2980            tab.shortenedText = null;
2981            tab.shortenedTextWidth = 0;
2982            tab.height = tabHeight;
2983            tab.width = width;
2984            tab.closeRect.width = tab.closeRect.height = 0;
2985            if (showClose || tab.showClose) {
2986                if (i == selectedIndex || showUnselectedClose) {
2987                    tab.closeRect.width = BUTTON_SIZE;
2988                    tab.closeRect.height = BUTTON_SIZE;
2989                }
2990            }
2991        }
2992    }
2993    return changed;
2994}
2995/**
2996 * Marks the receiver's maximize button as visible if the argument is <code>true</code>,
2997 * and marks it invisible otherwise.
2998 *
2999 * @param visible the new visibility state
3000 *
3001 * @exception SWTException <ul>
3002 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3003 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3004 * </ul>
3005 *
3006 * @since 3.0
3007 */

3008public void setMaximizeVisible(boolean visible) {
3009    checkWidget();
3010    if (showMax == visible) return;
3011    // display maximize button
3012
showMax = visible;
3013    updateItems();
3014    redraw();
3015}
3016/**
3017 * Sets the layout which is associated with the receiver to be
3018 * the argument which may be null.
3019 * <p>
3020 * Note: No Layout can be set on this Control because it already
3021 * manages the size and position of its children.
3022 * </p>
3023 *
3024 * @param layout the receiver's new layout or null
3025 *
3026 * @exception SWTException <ul>
3027 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3028 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3029 * </ul>
3030 */

3031public void setLayout (Layout layout) {
3032    checkWidget();
3033    return;
3034}
3035/**
3036 * Sets the maximized state of the receiver.
3037 *
3038 * @param maximize the new maximized state
3039 *
3040 * @exception SWTException <ul>
3041 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3042 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3043 * </ul>
3044 *
3045 * @since 3.0
3046 */

3047public void setMaximized(boolean maximize) {
3048    checkWidget ();
3049    if (this.maximized == maximize) return;
3050    if (maximize && this.minimized) setMinimized(false);
3051    this.maximized = maximize;
3052    redraw(maxRect.x, maxRect.y, maxRect.width, maxRect.height, false);
3053}
3054/**
3055 * Marks the receiver's minimize button as visible if the argument is <code>true</code>,
3056 * and marks it invisible otherwise.
3057 *
3058 * @param visible the new visibility state
3059 *
3060 * @exception SWTException <ul>
3061 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3062 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3063 * </ul>
3064 *
3065 * @since 3.0
3066 */

3067public void setMinimizeVisible(boolean visible) {
3068    checkWidget();
3069    if (showMin == visible) return;
3070    // display minimize button
3071
showMin = visible;
3072    updateItems();
3073    redraw();
3074}
3075/**
3076 * Sets the minimized state of the receiver.
3077 *
3078 * @param minimize the new minimized state
3079 *
3080 * @exception SWTException <ul>
3081 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3082 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3083 * </ul>
3084 *
3085 * @since 3.0
3086 */

3087public void setMinimized(boolean minimize) {
3088    checkWidget ();
3089    if (this.minimized == minimize) return;
3090    if (minimize && this.maximized) setMaximized(false);
3091    this.minimized = minimize;
3092    redraw(minRect.x, minRect.y, minRect.width, minRect.height, false);
3093}
3094
3095/**
3096 * Sets the minimum number of characters that will
3097 * be displayed in a fully compressed tab.
3098 *
3099 * @param count the minimum number of characters that will be displayed in a fully compressed tab
3100 *
3101 * @exception SWTException <ul>
3102 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3103 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3104 * <li>ERROR_INVALID_RANGE - if the count is less than zero</li>
3105 * </ul>
3106 *
3107 * @since 3.0
3108 */

3109public void setMinimumCharacters(int count) {
3110    checkWidget ();
3111    if (count < 0) SWT.error(SWT.ERROR_INVALID_RANGE);
3112    if (minChars == count) return;
3113    minChars = count;
3114    if (updateItems()) redrawTabs();
3115}
3116
3117/**
3118 * When there is not enough horizontal space to show all the tabs,
3119 * by default, tabs are shown sequentially from left to right in
3120 * order of their index. When the MRU visibility is turned on,
3121 * the tabs that are visible will be the tabs most recently selected.
3122 * Tabs will still maintain their left to right order based on index
3123 * but only the most recently selected tabs are visible.
3124 * <p>
3125 * For example, consider a CTabFolder that contains "Tab 1", "Tab 2",
3126 * "Tab 3" and "Tab 4" (in order by index). The user selects
3127 * "Tab 1" and then "Tab 3". If the CTabFolder is now
3128 * compressed so that only two tabs are visible, by default,
3129 * "Tab 2" and "Tab 3" will be shown ("Tab 3" since it is currently
3130 * selected and "Tab 2" because it is the previous item in index order).
3131 * If MRU visibility is enabled, the two visible tabs will be "Tab 1"
3132 * and "Tab 3" (in that order from left to right).</p>
3133 *
3134 * @param show the new visibility state
3135 *
3136 * @exception SWTException <ul>
3137 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3138 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3139 * </ul>
3140 *
3141 * @since 3.1
3142 */

3143public void setMRUVisible(boolean show) {
3144    checkWidget();
3145    if (mru == show) return;
3146    mru = show;
3147    if (!mru) {
3148        int idx = firstIndex;
3149        int next = 0;
3150        for (int i = firstIndex; i < items.length; i++) {
3151            priority[next++] = i;
3152        }
3153        for (int i = 0; i < idx; i++) {
3154            priority[next++] = i;
3155        }
3156        if (updateItems()) redrawTabs();
3157    }
3158}
3159/**
3160 * Set the selection to the tab at the specified item.
3161 *
3162 * @param item the tab item to be selected
3163 *
3164 * @exception IllegalArgumentException <ul>
3165 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
3166 * </ul>
3167 *
3168 * @exception SWTException <ul>
3169 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
3170 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
3171 * </ul>
3172 */

3173public void setSelection(CTabItem item) {
3174    checkWidget();
3175    if (item == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
3176    int index = indexOf(item);
3177    setSelection(index);
3178}
3179/**
3180 * Set the selection to the tab at the specified index.
3181 *
3182 * @param index the index of the tab item to be selected
3183 *
3184 * @exception SWTException <ul>
3185 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3186 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3187 * </ul>
3188 */

3189public void setSelection(int index) {
3190    checkWidget();
3191    if (index < 0 || index >= items.length) return;
3192    CTabItem selection = items[index];
3193    if (selectedIndex == index) {
3194        showItem(selection);
3195        return;
3196    }
3197    
3198    int oldIndex = selectedIndex;
3199    selectedIndex = index;
3200    if (oldIndex != -1) {
3201        items[oldIndex].closeImageState = NONE;
3202    }
3203    selection.closeImageState = NORMAL;
3204    selection.showing = false;
3205
3206    Control control = selection.control;
3207    if (control != null && !control.isDisposed()) {
3208        control.setBounds(getClientArea());
3209        control.setVisible(true);
3210    }
3211    
3212    if (oldIndex != -1) {
3213        control = items[oldIndex].control;
3214        if (control != null && !control.isDisposed()) {
3215            control.setVisible(false);
3216        }
3217    }
3218    showItem(selection);
3219    redraw();
3220}
3221void setSelection(int index, boolean notify) {
3222    int oldSelectedIndex = selectedIndex;
3223    setSelection(index);
3224    if (notify && selectedIndex != oldSelectedIndex && selectedIndex != -1) {
3225        Event event = new Event();
3226        event.item = getItem(selectedIndex);
3227        notifyListeners(SWT.Selection, event);
3228    }
3229}
3230/**
3231 * Sets the receiver's selection background color to the color specified
3232 * by the argument, or to the default system color for the control
3233 * if the argument is null.
3234 *
3235 * @param color the new color (or null)
3236 *
3237 * @exception IllegalArgumentException <ul>
3238 * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
3239 * </ul>
3240 * @exception SWTException <ul>
3241 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3242 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3243 * </ul>
3244 *
3245 * @since 3.0
3246 */

3247public void setSelectionBackground (Color color) {
3248    checkWidget();
3249    setSelectionHighlightGradientColor(null);
3250    if (selectionBackground == color) return;
3251    if (color == null) color = getDisplay().getSystemColor(SELECTION_BACKGROUND);
3252    selectionBackground = color;
3253    if (selectedIndex > -1) redraw();
3254}
3255/**
3256 * Specify a gradient of colours to be draw in the background of the selected tab.
3257 * For example to draw a gradient that varies from dark blue to blue and then to
3258 * white, use the following call to setBackground:
3259 * <pre>
3260 * cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
3261 * display.getSystemColor(SWT.COLOR_BLUE),
3262 * display.getSystemColor(SWT.COLOR_WHITE),
3263 * display.getSystemColor(SWT.COLOR_WHITE)},
3264 * new int[] {25, 50, 100});
3265 * </pre>
3266 *
3267 * @param colors an array of Color that specifies the colors to appear in the gradient
3268 * in order of appearance left to right. The value <code>null</code> clears the
3269 * background gradient. The value <code>null</code> can be used inside the array of
3270 * Color to specify the background color.
3271 * @param percents an array of integers between 0 and 100 specifying the percent of the width
3272 * of the widget at which the color should change. The size of the percents array must be one
3273 * less than the size of the colors array.
3274 *
3275 * @exception SWTException <ul>
3276 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
3277 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
3278 * </ul>
3279 */

3280public void setSelectionBackground(Color[] colors, int[] percents) {
3281    setSelectionBackground(colors, percents, false);
3282}
3283/**
3284 * Specify a gradient of colours to be draw in the background of the selected tab.
3285 * For example to draw a vertical gradient that varies from dark blue to blue and then to
3286 * white, use the following call to setBackground:
3287 * <pre>
3288 * cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
3289 * display.getSystemColor(SWT.COLOR_BLUE),
3290 * display.getSystemColor(SWT.COLOR_WHITE),
3291 * display.getSystemColor(SWT.COLOR_WHITE)},
3292 * new int[] {25, 50, 100}, true);
3293 * </pre>
3294 *
3295 * @param colors an array of Color that specifies the colors to appear in the gradient
3296 * in order of appearance left to right. The value <code>null</code> clears the
3297 * background gradient. The value <code>null</code> can be used inside the array of
3298 * Color to specify the background color.
3299 * @param percents an array of integers between 0 and 100 specifying the percent of the width
3300 * of the widget at which the color should change. The size of the percents array must be one
3301 * less than the size of the colors array.
3302 *
3303 * @param vertical indicate the direction of the gradient. True is vertical and false is horizontal.
3304 *
3305 * @exception SWTException <ul>
3306 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
3307 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
3308 * </ul>
3309 *
3310 * @since 3.0
3311 */

3312public void setSelectionBackground(Color[] colors, int[] percents, boolean vertical) {
3313    checkWidget();
3314    int colorsLength;
3315    Color highlightBeginColor = null; //null == no highlight
3316

3317    if (colors != null) {
3318        //The colors array can optionally have an extra entry which describes the highlight top color
3319
//Thus its either one or two larger than the percents array
3320
if (percents == null ||
3321                ! ((percents.length == colors.length - 1) || (percents.length == colors.length - 2))){
3322            SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3323        }
3324        for (int i = 0; i < percents.length; i++) {
3325            if (percents[i] < 0 || percents[i] > 100) {
3326                SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3327            }
3328            if (i > 0 && percents[i] < percents[i-1]) {
3329                SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3330            }
3331        }
3332        //If the colors is exactly two more than percents then last is highlight
3333
//Keep track of *real* colorsLength (minus the highlight)
3334
if(percents.length == colors.length - 2) {
3335            highlightBeginColor = colors[colors.length - 1];
3336            colorsLength = colors.length - 1;
3337        } else {
3338            colorsLength = colors.length;
3339        }
3340        if (getDisplay().getDepth() < 15) {
3341            // Don't use gradients on low color displays
3342
colors = new Color[] {colors[colorsLength - 1]};
3343            colorsLength = colors.length;
3344            percents = new int[] {};
3345        }
3346    } else {
3347        colorsLength = 0;
3348    }
3349    
3350    // Are these settings the same as before?
3351
if (selectionBgImage == null) {
3352        if ((selectionGradientColors != null) && (colors != null) &&
3353            (selectionGradientColors.length == colorsLength)) {
3354            boolean same = false;
3355            for (int i = 0; i < selectionGradientColors.length; i++) {
3356                if (selectionGradientColors[i] == null) {
3357                    same = colors[i] == null;
3358                } else {
3359                    same = selectionGradientColors[i].equals(colors[i]);
3360                }
3361                if (!same) break;
3362            }
3363            if (same) {
3364                for (int i = 0; i < selectionGradientPercents.length; i++) {
3365                    same = selectionGradientPercents[i] == percents[i];
3366                    if (!same) break;
3367                }
3368            }
3369            if (same && this.selectionGradientVertical == vertical) return;
3370        }
3371    } else {
3372        selectionBgImage = null;
3373    }
3374    // Store the new settings
3375
if (colors == null) {
3376        selectionGradientColors = null;
3377        selectionGradientPercents = null;
3378        selectionGradientVertical = false;
3379        setSelectionBackground((Color)null);
3380        setSelectionHighlightGradientColor(null);
3381    } else {
3382        selectionGradientColors = new Color[colorsLength];
3383        for (int i = 0; i < colorsLength; ++i) {
3384            selectionGradientColors[i] = colors[i];
3385        }
3386        selectionGradientPercents = new int[percents.length];
3387        for (int i = 0; i < percents.length; ++i) {
3388            selectionGradientPercents[i] = percents[i];
3389        }
3390        selectionGradientVertical = vertical;
3391        setSelectionBackground(selectionGradientColors[selectionGradientColors.length-1]);
3392        setSelectionHighlightGradientColor(highlightBeginColor);
3393    }
3394
3395    // Refresh with the new settings
3396
if (selectedIndex > -1) redraw();
3397}
3398
3399/*
3400 * Set the color for the highlight start for selected tabs.
3401 * Update the cache of highlight gradient colors if required.
3402 */

3403
3404void setSelectionHighlightGradientColor(Color start) {
3405    //Set to null to match all the early return cases.
3406
//For early returns, don't realloc the cache, we may get a cache hit next time we're given the highlight
3407
selectionHighlightGradientBegin = null;
3408
3409    if(start == null)
3410        return;
3411
3412    //don't bother on low colour
3413
if (getDisplay().getDepth() < 15)
3414        return;
3415    
3416    //don't bother if we don't have a background gradient
3417
if(selectionGradientColors.length < 2)
3418        return;
3419
3420    //OK we know its a valid gradient now
3421
selectionHighlightGradientBegin = start;
3422
3423    if(! isSelectionHighlightColorsCacheHit(start))
3424        createSelectionHighlightGradientColors(start); //if no cache hit then compute new ones
3425
}
3426
3427/*
3428 * Return true if given start color, the cache of highlight colors we have
3429 * would match the highlight colors we'd compute.
3430 */

3431boolean isSelectionHighlightColorsCacheHit(Color start) {
3432
3433    if(selectionHighlightGradientColorsCache == null)
3434        return false;
3435    
3436    //this case should never happen but check to be safe before accessing array indexes
3437
if(selectionHighlightGradientColorsCache.length < 2)
3438        return false;
3439
3440    Color highlightBegin = selectionHighlightGradientColorsCache[0];
3441    Color highlightEnd = selectionHighlightGradientColorsCache[selectionHighlightGradientColorsCache.length - 1];
3442
3443    if(! highlightBegin.equals(start))
3444        return false;
3445    
3446    //Compare number of colours we have vs. we'd compute
3447
if(selectionHighlightGradientColorsCache.length != tabHeight)
3448        return false;
3449    
3450    //Compare existing highlight end to what it would be (selectionBackground)
3451
if(! highlightEnd.equals(selectionBackground))
3452        return false;
3453    
3454    return true;
3455}
3456
3457/**
3458 * Set the image to be drawn in the background of the selected tab. Image
3459 * is stretched or compressed to cover entire selection tab area.
3460 *
3461 * @param image the image to be drawn in the background
3462 *
3463 * @exception SWTException <ul>
3464 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3465 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3466 * </ul>
3467 */

3468public void setSelectionBackground(Image image) {
3469    checkWidget();
3470    setSelectionHighlightGradientColor(null);
3471    if (image == selectionBgImage) return;
3472    if (image != null) {
3473        selectionGradientColors = null;
3474        selectionGradientPercents = null;
3475        disposeSelectionHighlightGradientColors();
3476    }
3477    selectionBgImage = image;
3478    if (selectedIndex > -1) redraw();
3479}
3480/**
3481 * Set the foreground color of the selected tab.
3482 *
3483 * @param color the color of the text displayed in the selected tab
3484 *
3485 * @exception SWTException <ul>
3486 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3487 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3488 * </ul>
3489 */

3490public void setSelectionForeground (Color color) {
3491    checkWidget();
3492    if (selectionForeground == color) return;
3493    if (color == null) color = getDisplay().getSystemColor(SELECTION_FOREGROUND);
3494    selectionForeground = color;
3495    if (selectedIndex > -1) redraw();
3496}
3497
3498/*
3499 * Allocate colors for the highlight line.
3500 * Colours will be a gradual blend ranging from to.
3501 * Blend length will be tab height.
3502 * Recompute this if tab height changes.
3503 * Could remain null if there'd be no gradient (start=end or low colour display)
3504 */

3505void createSelectionHighlightGradientColors(Color start) {
3506    disposeSelectionHighlightGradientColors(); //dispose if existing
3507

3508    if(start == null) //shouldn't happen but just to be safe
3509
return;
3510
3511    //alloc colours for entire height to ensure it matches wherever we stop drawing
3512
int fadeGradientSize = tabHeight;
3513
3514    RGB from = start.getRGB();
3515    RGB to = selectionBackground.getRGB();
3516
3517    selectionHighlightGradientColorsCache = new Color[fadeGradientSize];
3518    int denom = fadeGradientSize - 1;
3519
3520    for (int i = 0; i < fadeGradientSize; i++) {
3521        int propFrom = denom - i;
3522        int propTo = i;
3523        int red = (to.red * propTo + from.red * propFrom) / denom;
3524        int green = (to.green * propTo + from.green * propFrom) / denom;
3525        int blue = (to.blue * propTo + from.blue * propFrom) / denom;
3526        selectionHighlightGradientColorsCache[i] = new Color(getDisplay(), red, green, blue);
3527    }
3528}
3529
3530void disposeSelectionHighlightGradientColors() {
3531    if(selectionHighlightGradientColorsCache == null)
3532        return;
3533    for (int i = 0; i < selectionHighlightGradientColorsCache.length; i++) {
3534        selectionHighlightGradientColorsCache[i].dispose();
3535    }
3536    selectionHighlightGradientColorsCache = null;
3537}
3538
3539/*
3540 * Return the gradient start color for selected tabs, which is the start of the tab fade
3541 * (end is selectionBackground).
3542 */

3543Color getSelectionBackgroundGradientBegin() {
3544    if (selectionGradientColors == null)
3545        return getSelectionBackground();
3546    if (selectionGradientColors.length == 0)
3547        return getSelectionBackground();
3548    return selectionGradientColors[0];
3549}
3550
3551/**
3552 * Sets the shape that the CTabFolder will use to render itself.
3553 *
3554 * @param simple <code>true</code> if the CTabFolder should render itself in a simple, traditional style
3555 *
3556 * @exception SWTException <ul>
3557 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3558 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3559 * </ul>
3560 *
3561 * @since 3.0
3562 */

3563public void setSimple(boolean simple) {
3564    checkWidget();
3565    if (this.simple != simple) {
3566        this.simple = simple;
3567        Rectangle rectBefore = getClientArea();
3568        updateItems();
3569        Rectangle rectAfter = getClientArea();
3570        if (!rectBefore.equals(rectAfter)) {
3571            notifyListeners(SWT.Resize, new Event());
3572        }
3573        redraw();
3574    }
3575}
3576/**
3577 * Sets the number of tabs that the CTabFolder should display
3578 *
3579 * @param single <code>true</code> if only the selected tab should be displayed otherwise, multiple tabs will be shown.
3580 *
3581 * @exception SWTException <ul>
3582 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3583 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3584 * </ul>
3585 *
3586 * @since 3.0
3587 */

3588public void setSingle(boolean single) {
3589    checkWidget();
3590    if (this.single != single) {
3591        this.single = single;
3592        if (!single) {
3593            for (int i = 0; i < items.length; i++) {
3594                if (i != selectedIndex && items[i].closeImageState == NORMAL) {
3595                    items[i].closeImageState = NONE;
3596                }
3597            }
3598        }
3599        Rectangle rectBefore = getClientArea();
3600        updateItems();
3601        Rectangle rectAfter = getClientArea();
3602        if (!rectBefore.equals(rectAfter)) {
3603            notifyListeners(SWT.Resize, new Event());
3604        }
3605        redraw();
3606    }
3607}
3608/**
3609 * Specify a fixed height for the tab items. If no height is specified,
3610 * the default height is the height of the text or the image, whichever
3611 * is greater. Specifying a height of -1 will revert to the default height.
3612 *
3613 * @param height the pixel value of the height or -1
3614 *
3615 * @exception SWTException <ul>
3616 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3617 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3618 * <li>ERROR_INVALID_ARGUMENT - if called with a height of less than 0</li>
3619 * </ul>
3620 */

3621public void setTabHeight(int height) {
3622    checkWidget();
3623    if (height < -1) {
3624        SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3625    }
3626    fixedTabHeight = height;
3627    updateTabHeight(false);
3628}
3629/**
3630 * Specify whether the tabs should appear along the top of the folder
3631 * or along the bottom of the folder.
3632 *
3633 * @param position <code>SWT.TOP</code> for tabs along the top or <code>SWT.BOTTOM</code> for tabs along the bottom
3634 *
3635 * @exception SWTException <ul>
3636 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3637 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3638 * <li>ERROR_INVALID_ARGUMENT - if the position value is not either SWT.TOP or SWT.BOTTOM</li>
3639 * </ul>
3640 *
3641 * @since 3.0
3642 */

3643public void setTabPosition(int position) {
3644    checkWidget();
3645    if (position != SWT.TOP && position != SWT.BOTTOM) {
3646        SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3647    }
3648    if (onBottom != (position == SWT.BOTTOM)) {
3649        onBottom = position == SWT.BOTTOM;
3650        borderTop = onBottom ? borderLeft : 0;
3651        borderBottom = onBottom ? 0 : borderRight;
3652        updateTabHeight(true);
3653        Rectangle rectBefore = getClientArea();
3654        updateItems();
3655        Rectangle rectAfter = getClientArea();
3656        if (!rectBefore.equals(rectAfter)) {
3657            notifyListeners(SWT.Resize, new Event());
3658        }
3659        redraw();
3660    }
3661}
3662/**
3663 * Set the control that appears in the top right corner of the tab folder.
3664 * Typically this is a close button or a composite with a Menu and close button.
3665 * The topRight control is optional. Setting the top right control to null will
3666 * remove it from the tab folder.
3667 *
3668 * @param control the control to be displayed in the top right corner or null
3669 *
3670 * @exception SWTException <ul>
3671 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3672 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3673 * <li>ERROR_INVALID_ARGUMENT - if the control is not a child of this CTabFolder</li>
3674 * </ul>
3675 *
3676 * @since 2.1
3677 */

3678public void setTopRight(Control control) {
3679    setTopRight(control, SWT.RIGHT);
3680}
3681/**
3682 * Set the control that appears in the top right corner of the tab folder.
3683 * Typically this is a close button or a composite with a Menu and close button.
3684 * The topRight control is optional. Setting the top right control to null
3685 * will remove it from the tab folder.
3686 * <p>
3687 * The alignment parameter sets the layout of the control in the tab area.
3688 * <code>SWT.RIGHT</code> will cause the control to be positioned on the far
3689 * right of the folder and it will have its default size. <code>SWT.FILL</code>
3690 * will size the control to fill all the available space to the right of the
3691 * last tab. If there is no available space, the control will not be visible.
3692 * </p>
3693 *
3694 * @param control the control to be displayed in the top right corner or null
3695 * @param alignment <code>SWT.RIGHT</code> or <code>SWT.FILL</code>
3696 *
3697 * @exception SWTException <ul>
3698 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3699 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3700 * <li>ERROR_INVALID_ARGUMENT - if the control is not a child of this CTabFolder</li>
3701 * </ul>
3702 *
3703 * @since 3.0
3704 */

3705public void setTopRight(Control control, int alignment) {
3706    checkWidget();
3707    if (alignment != SWT.RIGHT && alignment != SWT.FILL) {
3708        SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3709    }
3710    if (control != null && control.getParent() != this) {
3711        SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3712    }
3713    topRight = control;
3714    topRightAlignment = alignment;
3715    if (updateItems()) redraw();
3716}
3717/**
3718 * Specify whether the close button appears
3719 * when the user hovers over an unselected tabs.
3720 *
3721 * @param visible <code>true</code> makes the close button appear
3722 *
3723 * @exception SWTException <ul>
3724 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3725 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3726 * </ul>
3727 *
3728 * @since 3.0
3729 */

3730public void setUnselectedCloseVisible(boolean visible) {
3731    checkWidget();
3732    if (showUnselectedClose == visible) return;
3733    // display close button when mouse hovers
3734
showUnselectedClose = visible;
3735    updateItems();
3736    redraw();
3737}
3738/**
3739 * Specify whether the image appears on unselected tabs.
3740 *
3741 * @param visible <code>true</code> makes the image appear
3742 *
3743 * @exception SWTException <ul>
3744 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3745 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3746 * </ul>
3747 *
3748 * @since 3.0
3749 */

3750public void setUnselectedImageVisible(boolean visible) {
3751    checkWidget();
3752    if (showUnselectedImage == visible) return;
3753    // display image on unselected items
3754
showUnselectedImage = visible;
3755    updateItems();
3756    redraw();
3757}
3758/**
3759 * Shows the item. If the item is already showing in the receiver,
3760 * this method simply returns. Otherwise, the items are scrolled until
3761 * the item is visible.
3762 *
3763 * @param item the item to be shown
3764 *
3765 * @exception IllegalArgumentException <ul>
3766 * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
3767 * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
3768 * </ul>
3769 * @exception SWTException <ul>
3770 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3771 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3772 * </ul>
3773 *
3774 * @see CTabFolder#showSelection()
3775 *
3776 * @since 2.0
3777 */

3778public void showItem (CTabItem item) {
3779    checkWidget();
3780    if (item == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
3781    if (item.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3782    int index = indexOf(item);
3783    if (index == -1) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3784    int idx = -1;
3785    for (int i = 0; i < priority.length; i++) {
3786        if (priority[i] == index) {
3787            idx = i;
3788            break;
3789        }
3790    }
3791    if (mru) {
3792        // move to front of mru order
3793
int[] newPriority = new int[priority.length];
3794        System.arraycopy(priority, 0, newPriority, 1, idx);
3795        System.arraycopy(priority, idx+1, newPriority, idx+1, priority.length - idx - 1);
3796        newPriority[0] = index;
3797        priority = newPriority;
3798    }
3799    if (item.isShowing()) return;
3800    updateItems(index);
3801    redrawTabs();
3802}
3803void showList (Rectangle rect) {
3804    if (items.length == 0 || !showChevron) return;
3805    if (showMenu == null || showMenu.isDisposed()) {
3806        showMenu = new Menu(this);
3807    } else {
3808        MenuItem[] items = showMenu.getItems();
3809        for (int i = 0; i < items.length; i++) {
3810            items[i].dispose();
3811        }
3812    }
3813    final String JavaDoc id = "CTabFolder_showList_Index"; //$NON-NLS-1$
3814
for (int i = 0; i < items.length; i++) {
3815        CTabItem tab = items[i];
3816        if (tab.showing) continue;
3817        MenuItem item = new MenuItem(showMenu, SWT.NONE);
3818        item.setText(tab.getText());
3819        item.setImage(tab.getImage());
3820        item.setData(id, tab);
3821        item.addSelectionListener(new SelectionAdapter() {
3822            public void widgetSelected(SelectionEvent e) {
3823                MenuItem menuItem = (MenuItem)e.widget;
3824                int index = indexOf((CTabItem)menuItem.getData(id));
3825                CTabFolder.this.setSelection(index, true);
3826            }
3827        });
3828    }
3829    int x = rect.x;
3830    int y = rect.y + rect.height;
3831    Point location = getDisplay().map(this, null, x, y);
3832    showMenu.setLocation(location.x, location.y);
3833    showMenu.setVisible(true);
3834}
3835/**
3836 * Shows the selection. If the selection is already showing in the receiver,
3837 * this method simply returns. Otherwise, the items are scrolled until
3838 * the selection is visible.
3839 *
3840 * @exception SWTException <ul>
3841 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3842 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3843 * </ul>
3844 *
3845 * @see CTabFolder#showItem(CTabItem)
3846 *
3847 * @since 2.0
3848 */

3849public void showSelection () {
3850    checkWidget ();
3851    if (selectedIndex != -1) {
3852        showItem(getSelection());
3853    }
3854}
3855
3856void _setToolTipText (int x, int y) {
3857    String JavaDoc oldTip = getToolTipText();
3858    String JavaDoc newTip = _getToolTip(x, y);
3859    if (newTip == null || !newTip.equals(oldTip)) {
3860        setToolTipText(newTip);
3861    }
3862}
3863
3864boolean updateItems() {
3865    return updateItems(selectedIndex);
3866}
3867
3868boolean updateItems(int showIndex) {
3869    if (!single && !mru && showIndex != -1) {
3870        // make sure selected item will be showing
3871
int firstIndex = showIndex;
3872        if (priority[0] < showIndex) {
3873            int maxWidth = getRightItemEdge() - borderLeft;
3874            if (!simple) maxWidth -= curveWidth - 2*curveIndent;
3875            int width = 0;
3876            int[] widths = new int[items.length];
3877            GC gc = new GC(this);
3878            for (int i = priority[0]; i <= showIndex; i++) {
3879                widths[i] = items[i].preferredWidth(gc, i == selectedIndex, true);
3880                width += widths[i];
3881                if (width > maxWidth) break;
3882            }
3883            if (width > maxWidth) {
3884                width = 0;
3885                for (int i = showIndex; i >= 0; i--) {
3886                    if (widths[i] == 0) widths[i] = items[i].preferredWidth(gc, i == selectedIndex, true);
3887                    width += widths[i];
3888                    if (width > maxWidth) break;
3889                    firstIndex = i;
3890                }
3891            } else {
3892                firstIndex = priority[0];
3893                for (int i = showIndex + 1; i < items.length; i++) {
3894                    widths[i] = items[i].preferredWidth(gc, i == selectedIndex, true);
3895                    width += widths[i];
3896                    if (width >= maxWidth) break;
3897                }
3898                if (width < maxWidth) {
3899                    for (int i = priority[0] - 1; i >= 0; i--) {
3900                        if (widths[i] == 0) widths[i] = items[i].preferredWidth(gc, i == selectedIndex, true);
3901                        width += widths[i];
3902                        if (width > maxWidth) break;
3903                        firstIndex = i;
3904                    }
3905                }
3906            }
3907            gc.dispose();
3908        }
3909        if (firstIndex != priority[0]) {
3910            int index = 0;
3911            for (int i = firstIndex; i < items.length; i++) {
3912                priority[index++] = i;
3913            }
3914            for (int i = 0; i < firstIndex; i++) {
3915                priority[index++] = i;
3916            }
3917        }
3918    }
3919    
3920    boolean oldShowChevron = showChevron;
3921    boolean changed = setItemSize();
3922    changed |= setItemLocation();
3923    setButtonBounds();
3924    changed |= showChevron != oldShowChevron;
3925    if (changed && getToolTipText() != null) {
3926        Point pt = getDisplay().getCursorLocation();
3927        pt = toControl(pt);
3928        _setToolTipText(pt.x, pt.y);
3929    }
3930    return changed;
3931}
3932boolean updateTabHeight(boolean force){
3933    int style = getStyle();
3934    if (fixedTabHeight == 0 && (style & SWT.FLAT) != 0 && (style & SWT.BORDER) == 0) highlight_header = 0;
3935    int oldHeight = tabHeight;
3936    if (fixedTabHeight != SWT.DEFAULT) {
3937        tabHeight = fixedTabHeight == 0 ? 0 : fixedTabHeight + 1; // +1 for line drawn across top of tab
3938
} else {
3939        int tempHeight = 0;
3940        GC gc = new GC(this);
3941        if (items.length == 0) {
3942            tempHeight = gc.textExtent("Default", CTabItem.FLAGS).y + CTabItem.TOP_MARGIN + CTabItem.BOTTOM_MARGIN; //$NON-NLS-1$
3943
} else {
3944            for (int i=0; i < items.length; i++) {
3945                tempHeight = Math.max(tempHeight, items[i].preferredHeight(gc));
3946            }
3947        }
3948        gc.dispose();
3949        tabHeight = tempHeight;
3950    }
3951    if (!force && tabHeight == oldHeight) return false;
3952    
3953    oldSize = null;
3954    if (onBottom) {
3955        int d = tabHeight - 12;
3956        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,
3957                          11,7+d, 11+d,7,
3958                          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};
3959        curveWidth = 26+d;
3960        curveIndent = curveWidth/3;
3961    } else {
3962        int d = tabHeight - 12;
3963        curve = new int[]{0,0, 0,1, 2,1, 3,2, 5,2, 6,3, 7,3, 9,5, 10,5,
3964                          11,6, 11+d,6+d,
3965                          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};
3966        curveWidth = 26+d;
3967        curveIndent = curveWidth/3;
3968        
3969        //this could be static but since values depend on curve, better to keep in one place
3970
topCurveHighlightStart = new int[] {
3971                0, 2, 1, 2, 2, 2,
3972                3, 3, 4, 3, 5, 3,
3973                6, 4, 7, 4,
3974                8, 5,
3975                9, 6, 10, 6};
3976        
3977        //also, by adding in 'd' here we save some math cost when drawing the curve
3978
topCurveHighlightEnd = new int[] {
3979                10+d, 6+d,
3980                11+d, 7+d,
3981                12+d, 8+d, 13+d, 8+d,
3982                14+d, 9+d,
3983                15+d, 10+d, 16+d, 10+d,
3984                17+d, 11+d, 18+d, 11+d, 19+d, 11+d,
3985                20+d, 12+d, 21+d, 12+d, 22+d, 12+d };
3986    }
3987    notifyListeners(SWT.Resize, new Event());
3988    return true;
3989}
3990String JavaDoc _getToolTip(int x, int y) {
3991    if (showMin && minRect.contains(x, y)) return minimized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Minimize"); //$NON-NLS-1$ //$NON-NLS-2$
3992
if (showMax && maxRect.contains(x, y)) return maximized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Maximize"); //$NON-NLS-1$ //$NON-NLS-2$
3993
if (showChevron && chevronRect.contains(x, y)) return SWT.getMessage("SWT_ShowList"); //$NON-NLS-1$
3994
CTabItem item = getItem(new Point (x, y));
3995    if (item == null) return null;
3996    if (!item.showing) return null;
3997    if ((showClose || item.showClose) && item.closeRect.contains(x, y)) {
3998        return SWT.getMessage("SWT_Close"); //$NON-NLS-1$
3999
}
4000    return item.getToolTipText();
4001}
4002}
4003
Popular Tags