KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > internal > presentations > r21 > widgets > CTabFolder


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 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.ui.internal.presentations.r21.widgets;
12
13 import org.eclipse.swt.SWT;
14 import org.eclipse.swt.SWTError;
15 import org.eclipse.swt.SWTException;
16 import org.eclipse.swt.accessibility.ACC;
17 import org.eclipse.swt.accessibility.Accessible;
18 import org.eclipse.swt.accessibility.AccessibleAdapter;
19 import org.eclipse.swt.accessibility.AccessibleControlAdapter;
20 import org.eclipse.swt.accessibility.AccessibleControlEvent;
21 import org.eclipse.swt.accessibility.AccessibleEvent;
22 import org.eclipse.swt.events.SelectionListener;
23 import org.eclipse.swt.graphics.Color;
24 import org.eclipse.swt.graphics.Font;
25 import org.eclipse.swt.graphics.GC;
26 import org.eclipse.swt.graphics.Image;
27 import org.eclipse.swt.graphics.ImageData;
28 import org.eclipse.swt.graphics.PaletteData;
29 import org.eclipse.swt.graphics.Point;
30 import org.eclipse.swt.graphics.RGB;
31 import org.eclipse.swt.graphics.Rectangle;
32 import org.eclipse.swt.widgets.Composite;
33 import org.eclipse.swt.widgets.Control;
34 import org.eclipse.swt.widgets.Display;
35 import org.eclipse.swt.widgets.Event;
36 import org.eclipse.swt.widgets.Label;
37 import org.eclipse.swt.widgets.Listener;
38 import org.eclipse.swt.widgets.Shell;
39 import org.eclipse.swt.widgets.ToolBar;
40 import org.eclipse.swt.widgets.ToolItem;
41 import org.eclipse.swt.widgets.TypedListener;
42
43 /**
44  * Instances of this class implement the notebook user interface
45  * metaphor. It allows the user to select a notebook page from
46  * set of pages.
47  * <p>
48  * The item children that may be added to instances of this class
49  * must be of type <code>CTabItem</code>.
50  * <code>Control</code> children are created and then set into a
51  * tab item using <code>CTabItem#setControl</code>.
52  * </p><p>
53  * Note that although this class is a subclass of <code>Composite</code>,
54  * it does not make sense to set a layout on it.
55  * </p><p>
56  * <dl>
57  * <dt><b>Styles:</b></dt>
58  * <dd>TOP, BOTTOM, FLAT</dd>
59  * <dt><b>Events:</b></dt>
60  * <dd>Selection</dd>
61  * <dd>"CTabFolder"</dd>
62  * </dl>
63  * <p>
64  * Note: Only one of the styles TOP and BOTTOM
65  * may be specified.
66  * </p><p>
67  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
68  * </p>
69  */

70
71 public class CTabFolder extends Composite {
72
73     /**
74      * marginWidth specifies the number of pixels of horizontal margin
75      * that will be placed along the left and right edges of the form.
76      *
77      * The default value is 0.
78      */

79     public int marginWidth = 0;
80
81     /**
82      * marginHeight specifies the number of pixels of vertical margin
83      * that will be placed along the top and bottom edges of the form.
84      *
85      * The default value is 0.
86      */

87     public int marginHeight = 0;
88
89     /**
90      * Color of innermost line of drop shadow border.
91      */

92     public static RGB borderInsideRGB = new RGB(132, 130, 132);
93
94     /**
95      * Color of middle line of drop shadow border.
96      */

97     public static RGB borderMiddleRGB = new RGB(143, 141, 138);
98
99     /**
100      * Color of outermost line of drop shadow border.
101      */

102     public static RGB borderOutsideRGB = new RGB(171, 168, 165);
103
104     /*
105      * A multiple of the tab height that specifies the minimum width to which a tab
106      * will be compressed before scrolling arrows are used to navigate the tabs.
107      */

108     public int MIN_TAB_WIDTH = 3;
109
110     /* sizing, positioning */
111     int xClient, yClient;
112
113     boolean onBottom = false;
114
115     boolean fixedTabHeight;
116
117     int tabHeight;
118
119     /* item management */
120     private CTabItem items[] = new CTabItem[0];
121
122     private int selectedIndex = -1;
123
124     int topTabIndex = -1; // index of the left most visible tab.
125

126     /* External Listener management */
127     private CTabFolderListener[] tabListeners = new CTabFolderListener[0];
128
129     /* Color appearance */
130     Image backgroundImage;
131
132     Color[] gradientColors;
133
134     int[] gradientPercents;
135
136     Color selectionForeground;
137
138     Color background;
139
140     // internal constants
141
private static final int DEFAULT_WIDTH = 64;
142
143     private static final int DEFAULT_HEIGHT = 64;
144
145     // scrolling arrows
146
private ToolBar arrowBar;
147
148     private Image arrowLeftImage;
149
150     private Image arrowRightImage;
151
152     private Control topRight;
153
154     // close button
155
boolean showClose = false;
156
157     private Image closeImage;
158
159     ToolBar closeBar;
160
161     private ToolBar inactiveCloseBar;
162
163     private CTabItem inactiveItem;
164
165     // borders
166
boolean showBorders = false;
167
168     private int borderBottom = 0;
169
170     private int borderLeft = 0;
171
172     private int borderRight = 0;
173
174     private int borderTop = 0;
175
176     private Color borderColor1;
177
178     private Color borderColor2;
179
180     private Color borderColor3;
181
182     // when disposing CTabFolder, don't try to layout the items or
183
// change the selection as each child is destroyed.
184
private boolean inDispose = false;
185
186     // keep track of size changes in order to redraw only affected area
187
// on Resize
188
private Point oldSize;
189
190     private Font oldFont;
191
192     // insertion marker
193
int insertionIndex = -2; // Index of insert marker. Marker always shown after index.
194

195     // -2 means no insert marker
196

197     // tool tip
198
private Shell tip;
199
200     private Label label;
201
202     private boolean showToolTip = false;
203
204     private CTabItem toolTipItem;
205
206     /**
207      * Constructs a new instance of this class given its parent
208      * and a style value describing its behavior and appearance.
209      * <p>
210      * The style value is either one of the style constants defined in
211      * class <code>SWT</code> which is applicable to instances of this
212      * class, or must be built by <em>bitwise OR</em>'ing together
213      * (that is, using the <code>int</code> "|" operator) two or more
214      * of those <code>SWT</code> style constants. The class description
215      * lists the style constants that are applicable to the class.
216      * Style bits are also inherited from superclasses.
217      * </p>
218      *
219      * @param parent a widget which will be the parent of the new instance (cannot be null)
220      * @param style the style of widget to construct
221      *
222      * @exception IllegalArgumentException <ul>
223      * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
224      * </ul>
225      * @exception SWTException <ul>
226      * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
227      * </ul>
228      *
229      * @see SWT#TOP
230      * @see SWT#BOTTOM
231      * @see SWT#FLAT
232      * @see #getStyle()
233      */

234     public CTabFolder(Composite parent, int style) {
235         super(parent, checkStyle(style));
236
237         onBottom = (getStyle() & SWT.BOTTOM) != 0;
238
239         borderColor1 = new Color(getDisplay(), borderInsideRGB);
240         borderColor2 = new Color(getDisplay(), borderMiddleRGB);
241         borderColor3 = new Color(getDisplay(), borderOutsideRGB);
242
243         // tool tip support
244
tip = new Shell(getShell(), SWT.ON_TOP);
245         label = new Label(tip, SWT.CENTER);
246
247         // Add all listeners
248
Listener listener = new Listener() {
249             public void handleEvent(Event event) {
250                 switch (event.type) {
251                 case SWT.Dispose:
252                     onDispose();
253                     break;
254                 case SWT.Paint:
255                     onPaint(event);
256                     break;
257                 case SWT.Resize:
258                     onResize();
259                     break;
260                 case SWT.MouseDoubleClick:
261                     onMouseDoubleClick(event);
262                     break;
263                 case SWT.MouseDown:
264                     onMouseDown(event);
265                     break;
266                 case SWT.MouseExit:
267                     onMouseExit(event);
268                     break;
269                 case SWT.MouseHover:
270                     onMouseHover(event);
271                     break;
272                 case SWT.MouseMove:
273                     onMouseMove(event);
274                     break;
275                 case SWT.FocusIn:
276                     onFocus(event);
277                     break;
278                 case SWT.FocusOut:
279                     onFocus(event);
280                     break;
281                 case SWT.KeyDown:
282                     onKeyDown(event);
283                     break;
284                 case SWT.Traverse:
285                     onTraverse(event);
286                     break;
287                 }
288             }
289         };
290
291         int[] folderEvents = new int[] { SWT.Dispose, SWT.Paint, SWT.Resize,
292                 SWT.MouseDoubleClick, SWT.MouseDown, SWT.MouseExit,
293                 SWT.MouseHover, SWT.MouseMove, SWT.FocusIn, SWT.FocusOut,
294                 SWT.KeyDown, SWT.Traverse, };
295         for (int i = 0; i < folderEvents.length; i++) {
296             addListener(folderEvents[i], listener);
297         }
298
299         createArrowBar();
300         createCloseBar();
301
302         setBorderVisible((style & SWT.BORDER) != 0);
303
304         initAccessible();
305
306     }
307
308     private static int checkStyle(int style) {
309         int mask = SWT.TOP | SWT.BOTTOM | SWT.FLAT | SWT.LEFT_TO_RIGHT
310                 | SWT.RIGHT_TO_LEFT;
311         style = style & mask;
312         // TOP and BOTTOM are mutually exlusive.
313
// TOP is the default
314
if ((style & SWT.TOP) != 0) {
315             style = style & ~(SWT.TOP | SWT.BOTTOM) | SWT.TOP;
316         }
317         // reduce the flash by not redrawing the entire area on a Resize event
318
style |= SWT.NO_REDRAW_RESIZE;
319         return style;
320     }
321
322     /**
323      * Adds the listener to receive events.
324      * <p>
325      *
326      * @param listener the listener
327      *
328      * @exception SWTError <ul>
329      * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
330      * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
331      * <li>ERROR_NULL_ARGUMENT when listener is null</li>
332      * </ul>
333      */

334     public void addSelectionListener(SelectionListener listener) {
335         checkWidget();
336         if (listener == null) {
337             SWT.error(SWT.ERROR_NULL_ARGUMENT);
338         }
339         TypedListener typedListener = new TypedListener(listener);
340         addListener(SWT.Selection, typedListener);
341         addListener(SWT.DefaultSelection, typedListener);
342     }
343
344     /**
345      * Adds the listener to the collection of listeners who will
346      * be notified when a tab item is closed.
347      *
348      * @param listener the listener which should be notified
349      *
350      * @exception IllegalArgumentException <ul>
351      * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
352      * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
353      * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
354      * </ul>
355      *
356      * @see CTabFolderListener
357      * @see #removeCTabFolderListener
358      */

359     public void addCTabFolderListener(CTabFolderListener listener) {
360         checkWidget();
361         if (listener == null) {
362             SWT.error(SWT.ERROR_NULL_ARGUMENT);
363         }
364         // add to array
365
CTabFolderListener[] newTabListeners = new CTabFolderListener[tabListeners.length + 1];
366         System.arraycopy(tabListeners, 0, newTabListeners, 0,
367                 tabListeners.length);
368         tabListeners = newTabListeners;
369         tabListeners[tabListeners.length - 1] = listener;
370         showClose = true;
371         setButtonBounds();
372     }
373
374     private void closeNotify(CTabItem item, int time) {
375         if (item == null) {
376             return;
377         }
378
379         CTabFolderEvent event = new CTabFolderEvent(this);
380         event.widget = this;
381         event.time = time;
382         event.item = item;
383         event.doit = true;
384         if (tabListeners != null) {
385             for (int i = 0; i < tabListeners.length; i++) {
386                 tabListeners[i].itemClosed(event);
387             }
388         }
389         if (event.doit) {
390             item.dispose();
391         }
392     }
393
394     public Point computeSize(int wHint, int hHint, boolean changed) {
395         checkWidget();
396         int minWidth = 0;
397         int minHeight = 0;
398
399         // preferred width of tab area to show all tabs
400
GC gc = new GC(this);
401         for (int i = 0; i < items.length; i++) {
402             minWidth += items[i].preferredWidth(gc);
403         }
404         gc.dispose();
405
406         // preferred size of controls in tab items
407
for (int i = 0; i < items.length; i++) {
408             Control control = items[i].getControl();
409             if (control != null && !control.isDisposed()) {
410                 Point size = control.computeSize(wHint, hHint);
411                 minWidth = Math.max(minWidth, size.x);
412                 minHeight = Math.max(minHeight, size.y);
413             }
414         }
415         if (minWidth == 0) {
416             minWidth = DEFAULT_WIDTH;
417         }
418         if (minHeight == 0) {
419             minHeight = DEFAULT_HEIGHT;
420         }
421
422         if (wHint != SWT.DEFAULT) {
423             minWidth = wHint;
424         }
425         if (hHint != SWT.DEFAULT) {
426             minHeight = hHint;
427         }
428
429         Rectangle trim = computeTrim(0, 0, minWidth, minHeight);
430         return new Point(trim.width, trim.height);
431     }
432
433     public Rectangle computeTrim(int x, int y, int width, int height) {
434         checkWidget();
435         if (items.length == 0) {
436             if (!showBorders) {
437                 return new Rectangle(x, y, width, height);
438             }
439             int trimX = x - borderRight - 1;
440             int trimY = y - borderBottom - 1;
441             int trimWidth = width + borderRight + 2;
442             int trimHeight = height + borderBottom + 2;
443             return new Rectangle(trimX, trimY, trimWidth, trimHeight);
444         } else {
445             int trimX = x - marginWidth - borderLeft;
446             int trimY = y - marginHeight - tabHeight - borderTop - 1;
447             // -1 is for the line at the bottom of the tabs
448
if (onBottom) {
449                 trimY = y - marginHeight - borderTop;
450             }
451             int trimWidth = width + borderLeft + borderRight + 2 * marginWidth;
452             int trimHeight = height + borderTop + borderBottom + 2
453                     * marginHeight + tabHeight + 1;
454             return new Rectangle(trimX, trimY, trimWidth, trimHeight);
455         }
456     }
457
458     /**
459      * Create the specified item at 'index'.
460      */

461     void createItem(CTabItem item, int index) {
462         if (0 > index || index > getItemCount()) {
463             SWT.error(SWT.ERROR_INVALID_RANGE);
464         }
465         // grow by one and rearrange the array.
466
CTabItem[] newItems = new CTabItem[items.length + 1];
467         System.arraycopy(items, 0, newItems, 0, index);
468         newItems[index] = item;
469         System.arraycopy(items, index, newItems, index + 1, items.length
470                 - index);
471         items = newItems;
472
473         item.parent = this;
474
475         if (selectedIndex >= index) {
476             selectedIndex++;
477         }
478         if (items.length == 1) {
479             topTabIndex = 0;
480             resetTabSize(true);
481         } else {
482             setItemBounds();
483             showItem(item);
484         }
485
486         if (items.length == 1) {
487             redraw();
488         } else {
489             redrawTabArea(-1);
490         }
491     }
492
493     private void createArrowBar() {
494         // create arrow buttons for scrolling
495
arrowBar = new ToolBar(this, SWT.FLAT);
496         arrowBar.setVisible(false);
497         arrowBar.setBackground(background);
498         ToolItem scrollLeft = new ToolItem(arrowBar, SWT.PUSH);
499         scrollLeft.setEnabled(false);
500         ToolItem scrollRight = new ToolItem(arrowBar, SWT.PUSH);
501         scrollRight.setEnabled(false);
502
503         scrollLeft.addListener(SWT.Selection, new Listener() {
504             public void handleEvent(Event event) {
505                 scroll_scrollLeft();
506             }
507         });
508         scrollRight.addListener(SWT.Selection, new Listener() {
509             public void handleEvent(Event event) {
510                 scroll_scrollRight();
511             }
512         });
513
514     }
515
516     private void createCloseBar() {
517         closeBar = new ToolBar(this, SWT.FLAT);
518         closeBar.setVisible(false);
519         if (gradientColors != null && gradientColors.length > 0) {
520             closeBar.setBackground(gradientColors[gradientColors.length - 1]);
521         } else {
522             closeBar.setBackground(background);
523         }
524         ToolItem closeItem = new ToolItem(closeBar, SWT.PUSH);
525
526         inactiveCloseBar = new ToolBar(this, SWT.FLAT);
527         inactiveCloseBar.setVisible(false);
528         inactiveCloseBar.setBackground(background);
529         ToolItem inactiveCloseItem = new ToolItem(inactiveCloseBar, SWT.PUSH);
530
531         closeItem.addListener(SWT.Selection, new Listener() {
532             public void handleEvent(Event event) {
533                 closeNotify(getSelection(), event.time);
534             }
535         });
536         inactiveCloseItem.addListener(SWT.Selection, new Listener() {
537             public void handleEvent(Event event) {
538                 closeNotify(inactiveItem, event.time);
539                 inactiveCloseBar.setVisible(false);
540                 inactiveItem = null;
541             }
542         });
543         inactiveCloseBar.addListener(SWT.MouseExit, new Listener() {
544             public void handleEvent(Event event) {
545                 if (inactiveItem != null) {
546                     Rectangle itemBounds = inactiveItem.getBounds();
547                     if (itemBounds.contains(event.x, event.y)) {
548                         return;
549                     }
550                 }
551                 inactiveCloseBar.setVisible(false);
552                 inactiveItem = null;
553             }
554         });
555
556     }
557
558     /**
559      * Destroy the specified item.
560      */

561     void destroyItem(CTabItem item) {
562         if (inDispose) {
563             return;
564         }
565
566         int index = indexOf(item);
567         if (index == -1) {
568             return; // should this trigger an error?
569
}
570
571         insertionIndex = -2;
572
573         if (items.length == 1) {
574             items = new CTabItem[0];
575             selectedIndex = -1;
576             topTabIndex = 0;
577
578             Control control = item.getControl();
579             if (control != null && !control.isDisposed()) {
580                 control.setVisible(false);
581             }
582             closeBar.setVisible(false);
583             if (!fixedTabHeight) {
584                 tabHeight = 0;
585             }
586             redraw();
587             return;
588         }
589
590         // shrink by one and rearrange the array.
591
CTabItem[] newItems = new CTabItem[items.length - 1];
592         System.arraycopy(items, 0, newItems, 0, index);
593         System.arraycopy(items, index + 1, newItems, index, items.length
594                 - index - 1);
595         items = newItems;
596
597         if (topTabIndex == items.length) {
598             --topTabIndex;
599         }
600
601         // move the selection if this item is selected
602
if (selectedIndex == index) {
603             Control control = item.getControl();
604             if (control != null && !control.isDisposed()) {
605                 control.setVisible(false);
606             }
607             selectedIndex = -1;
608             setSelection(Math.max(0, index - 1), true);
609         } else if (selectedIndex > index) {
610             selectedIndex--;
611         }
612
613         setItemBounds();
614         redrawTabArea(-1);
615     }
616
617     private void onKeyDown(Event e) {
618         if (e.keyCode != SWT.ARROW_LEFT && e.keyCode != SWT.ARROW_RIGHT) {
619             return;
620         }
621         int leadKey = (getStyle() & SWT.MIRRORED) != 0 ? SWT.ARROW_RIGHT
622                 : SWT.ARROW_LEFT;
623         if (e.keyCode == leadKey) {
624             if (selectedIndex > 0) {
625                 setSelection(selectedIndex - 1, true);
626             }
627         } else {
628             if (selectedIndex < items.length - 1) {
629                 setSelection(selectedIndex + 1, true);
630             }
631         }
632     }
633
634     /**
635      * Dispose the items of the receiver
636      */

637     private void onDispose() {
638         /*
639          * Usually when an item is disposed, destroyItem will change the size of the items array,
640          * reset the bounds of all the tabs and manage the widget associated with the tab.
641          * Since the whole folder is being disposed, this is not necessary. For speed
642          * the inDispose flag is used to skip over this part of the item dispose.
643          */

644         inDispose = true;
645
646         int length = items.length;
647         for (int i = 0; i < length; i++) {
648             if (items[i] != null) {
649                 items[i].dispose();
650             }
651         }
652
653         // clean up resources
654
if (tip != null && !tip.isDisposed()) {
655             tip.dispose();
656             tip = null;
657             label = null;
658         }
659
660         if (arrowLeftImage != null) {
661             arrowLeftImage.dispose();
662         }
663         arrowLeftImage = null;
664         if (arrowRightImage != null) {
665             arrowRightImage.dispose();
666         }
667         arrowRightImage = null;
668         if (closeImage != null) {
669             closeImage.dispose();
670         }
671         closeImage = null;
672
673         gradientColors = null;
674         gradientPercents = null;
675         backgroundImage = null;
676
677         if (borderColor1 != null) {
678             borderColor1.dispose();
679         }
680         borderColor1 = null;
681
682         if (borderColor2 != null) {
683             borderColor2.dispose();
684         }
685         borderColor2 = null;
686
687         if (borderColor3 != null) {
688             borderColor3.dispose();
689         }
690         borderColor3 = null;
691     }
692
693     private void onFocus(Event e) {
694         checkWidget();
695         if (selectedIndex >= 0) {
696             redrawTabArea(selectedIndex);
697         } else {
698             setSelection(0, true);
699         }
700     }
701
702     /**
703      * Draw a border around the receiver.
704      */

705     private void drawBorder(GC gc) {
706
707         Rectangle d = super.getClientArea();
708
709         if (showBorders) {
710             if ((getStyle() & SWT.FLAT) != 0) {
711                 gc.setForeground(borderColor1);
712                 gc.drawRectangle(d.x, d.y, d.x + d.width - 1, d.y + d.height
713                         - 1);
714             } else {
715                 gc.setForeground(borderColor1);
716                 gc.drawRectangle(d.x, d.y, d.x + d.width - 3, d.y + d.height
717                         - 3);
718
719                 gc.setForeground(borderColor2);
720                 gc.drawLine(d.x + 1, d.y + d.height - 2, d.x + d.width - 1, d.y
721                         + d.height - 2);
722                 gc.drawLine(d.x + d.width - 2, d.y + 1, d.x + d.width - 2, d.y
723                         + d.height - 1);
724
725                 gc.setForeground(borderColor3);
726                 gc.drawLine(d.x + 2, d.y + d.height - 1, d.x + d.width - 2, d.y
727                         + d.height - 1);
728                 gc.drawLine(d.x + d.width - 1, d.y + 2, d.x + d.width - 1, d.y
729                         + d.height - 2);
730
731                 // fill in corners with parent's background
732
gc.setForeground(getParent().getBackground());
733                 gc.drawLine(d.x + d.width - 2, d.y, d.x + d.width - 1, d.y);
734                 gc.drawLine(d.x + d.width - 1, d.y + 1, d.x + d.width - 1,
735                         d.y + 1);
736
737                 gc.drawLine(d.x, d.y + d.height - 2, d.x, d.y + d.height - 2);
738                 gc.drawLine(d.x, d.y + d.height - 1, d.x + 1, d.y + d.height
739                         - 1);
740
741                 gc.drawLine(d.x + d.width - 1, d.y + d.height - 1, d.x
742                         + d.width - 1, d.y + d.height - 1);
743             }
744
745         }
746
747         // draw a separator line
748
if (items.length > 0) {
749             int lineY = d.y + borderTop + tabHeight;
750             if (onBottom) {
751                 lineY = d.y + d.height - borderBottom - tabHeight - 1;
752             }
753             gc.setForeground(borderColor1);
754             gc.drawLine(d.x + borderLeft, lineY, d.x + d.width - borderRight,
755                     lineY);
756         }
757
758         gc.setForeground(getForeground());
759     }
760
761     public Rectangle getClientArea() {
762         checkWidget();
763         Point size = getSize();
764         if (items.length == 0) {
765             if (!showBorders) {
766                 return super.getClientArea();
767             }
768             int width = size.x - borderRight - 2;
769             int height = size.y - borderBottom - 2;
770             return new Rectangle(borderRight + 1, borderBottom + 1, width,
771                     height);
772         } else {
773             int width = size.x - 2 * marginWidth - borderLeft - borderRight;
774             int height = size.y - 2 * marginHeight - borderTop - borderBottom
775                     - tabHeight - 1;
776             return new Rectangle(xClient, yClient, width, height);
777         }
778     }
779
780     /**
781      * Returns the height of the tab
782      *
783      * @return the height of the tab
784      *
785      * @exception SWTError <ul>
786      * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
787      * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
788      * </ul>
789      */

790     public int getTabHeight() {
791         checkWidget();
792         return tabHeight;
793     }
794
795     /**
796      * Return the tab that is located at the specified index.
797      *
798      * @return the item at the specified index
799      */

800     public CTabItem getItem(int index) {
801         //checkWidget();
802
if (index < 0 || index >= items.length) {
803             SWT.error(SWT.ERROR_INVALID_RANGE);
804         }
805         return items[index];
806     }
807
808     /**
809      * Gets the item at a point in the widget.
810      * <p>
811      *
812      * @return the item at a point
813      */

814     public CTabItem getItem(Point pt) {
815         //checkWidget();
816
if (items.length == 0) {
817             return null;
818         }
819         int lastItem = getLastItem();
820         lastItem = Math.min(items.length - 1, lastItem + 1);
821         for (int i = topTabIndex; i <= lastItem; i++) {
822             Rectangle bounds = items[i].getBounds();
823             if (bounds.contains(pt)) {
824                 return items[i];
825             }
826         }
827         return null;
828     }
829
830     /**
831      * Return the number of tabs in the folder.
832      *
833      * @return the number of tabs in the folder
834      */

835     public int getItemCount() {
836         //checkWidget();
837
return items.length;
838     }
839
840     /**
841      * Return the tab items.
842      *
843      * @return the tab items
844      */

845     public CTabItem[] getItems() {
846         //checkWidget();
847
CTabItem[] tabItems = new CTabItem[items.length];
848         System.arraycopy(items, 0, tabItems, 0, items.length);
849         return tabItems;
850     }
851
852     private int getLastItem() {
853         if (items.length == 0) {
854             return -1;
855         }
856         Rectangle area = getClientArea();
857         if (area.width <= 0) {
858             return 0;
859         }
860         Rectangle toolspace = getToolSpace();
861         if (toolspace.width == 0) {
862             return items.length - 1;
863         }
864         int width = area.width - toolspace.width;
865         int index = topTabIndex;
866         int tabWidth = items[index].width;
867         while (index < items.length - 1) {
868             tabWidth += items[index + 1].width;
869             if (tabWidth > width) {
870                 break;
871             }
872             index++;
873         }
874         return index;
875     }
876
877     /**
878      * Return the selected tab item, or an empty array if there
879      * is no selection.
880      *
881      * @return the selected tab item
882      */

883     public CTabItem getSelection() {
884         //checkWidget();
885
if (selectedIndex == -1) {
886             return null;
887         }
888         return items[selectedIndex];
889     }
890
891     /**
892      * Return the index of the selected tab item, or -1 if there
893      * is no selection.
894      *
895      * @return the index of the selected tab item or -1
896      */

897     public int getSelectionIndex() {
898         //checkWidget();
899
return selectedIndex;
900     }
901
902     private Rectangle getToolSpace() {
903         boolean showArrows = scroll_leftVisible() || scroll_rightVisible();
904         if (!showArrows && topRight == null) {
905             return new Rectangle(0, 0, 0, 0);
906         }
907         Rectangle toolspace;
908         if (showArrows) {
909             toolspace = arrowBar.getBounds();
910             toolspace.width += borderRight;
911             if (topRight != null) {
912                 toolspace.width += topRight.getSize().x;
913             }
914         } else {
915             toolspace = topRight.getBounds();
916             toolspace.width += borderRight;
917         }
918         return toolspace;
919     }
920
921     /**
922      * Returns the control in the top right corner of the tab folder.
923      * Typically this is a close button or a composite with a menu and close button.
924      *
925      * @since 2.1
926      *
927      * @return the control in the top right corner of the tab folder or null
928      *
929      * @exception SWTError <ul>
930      * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
931      * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
932      * </ul>
933      */

934     public Control getTopRight() {
935         checkWidget();
936         return topRight;
937     }
938
939     /**
940      * Return the index of the specified tab or -1 if the tab is not
941      * in the receiver.
942      *
943      * @return the index of the specified tab item or -1
944      *
945      * @exception SWTError <ul>
946      * <li>ERROR_NULL_ARGUMENT when the item is null</li>
947      * </ul>
948      */

949     public int indexOf(CTabItem item) {
950         //checkWidget();
951
if (item == null) {
952             SWT.error(SWT.ERROR_NULL_ARGUMENT);
953         }
954         for (int i = 0; i < items.length; i++) {
955             if (items[i] == item) {
956                 return i;
957             }
958         }
959         return -1;
960     }
961
962     private void initAccessible() {
963         final Accessible accessible = getAccessible();
964         accessible.addAccessibleListener(new AccessibleAdapter() {
965             public void getName(AccessibleEvent e) {
966                 String JavaDoc name = null;
967                 int childID = e.childID;
968                 if (childID >= 0 && childID < items.length) {
969                     name = items[childID].getText();
970                     int index = name.indexOf('&');
971                     if (index > 0) {
972                         name = name.substring(0, index)
973                                 + name.substring(index + 1);
974                     }
975                 }
976                 e.result = name;
977             }
978
979             public void getHelp(AccessibleEvent e) {
980                 String JavaDoc help = null;
981                 int childID = e.childID;
982                 if (childID == ACC.CHILDID_SELF) {
983                     help = getToolTipText();
984                 } else if (childID >= 0 && childID < items.length) {
985                     help = items[childID].getToolTipText();
986                 }
987                 e.result = help;
988             }
989
990             public void getKeyboardShortcut(AccessibleEvent e) {
991                 String JavaDoc shortcut = null;
992                 int childID = e.childID;
993                 if (childID >= 0 && childID < items.length) {
994                     String JavaDoc text = items[childID].getText();
995                     if (text != null) {
996                         char mnemonic = getMnemonic(text);
997                         if (mnemonic != '\0') {
998                             shortcut = "Alt+" + mnemonic; //$NON-NLS-1$
999
}
1000                    }
1001                }
1002                e.result = shortcut;
1003            }
1004        });
1005
1006        accessible.addAccessibleControlListener(new AccessibleControlAdapter() {
1007            public void getChildAtPoint(AccessibleControlEvent e) {
1008                Point testPoint = toControl(new Point(e.x, e.y));
1009                int childID = ACC.CHILDID_NONE;
1010                for (int i = 0; i < items.length; i++) {
1011                    if (items[i].getBounds().contains(testPoint)) {
1012                        childID = i;
1013                        break;
1014                    }
1015                }
1016                if (childID == ACC.CHILDID_NONE) {
1017                    Rectangle location = getBounds();
1018                    location.height = location.height - getClientArea().height;
1019                    if (location.contains(testPoint)) {
1020                        childID = ACC.CHILDID_SELF;
1021                    }
1022                }
1023                e.childID = childID;
1024            }
1025
1026            public void getLocation(AccessibleControlEvent e) {
1027                Rectangle location = null;
1028                int childID = e.childID;
1029                if (childID == ACC.CHILDID_SELF) {
1030                    location = getBounds();
1031                }
1032                if (childID >= 0 && childID < items.length) {
1033                    location = items[childID].getBounds();
1034                }
1035                if (location != null) {
1036                    Point pt = toDisplay(new Point(location.x, location.y));
1037                    e.x = pt.x;
1038                    e.y = pt.y;
1039                    e.width = location.width;
1040                    e.height = location.height;
1041                }
1042            }
1043
1044            public void getChildCount(AccessibleControlEvent e) {
1045                e.detail = items.length;
1046            }
1047
1048            public void getDefaultAction(AccessibleControlEvent e) {
1049                String JavaDoc action = null;
1050                int childID = e.childID;
1051                if (childID >= 0 && childID < items.length) {
1052                    action = "Switch"; //$NON-NLS-1$
1053
}
1054                e.result = action;
1055            }
1056
1057            public void getFocus(AccessibleControlEvent e) {
1058                int childID = ACC.CHILDID_NONE;
1059                if (isFocusControl()) {
1060                    if (selectedIndex == -1) {
1061                        childID = ACC.CHILDID_SELF;
1062                    } else {
1063                        childID = selectedIndex;
1064                    }
1065                }
1066                e.childID = childID;
1067            }
1068
1069            public void getRole(AccessibleControlEvent e) {
1070                int role = 0;
1071                int childID = e.childID;
1072                if (childID == ACC.CHILDID_SELF) {
1073                    role = ACC.ROLE_TABFOLDER;
1074                } else if (childID >= 0 && childID < items.length) {
1075                    role = ACC.ROLE_TABITEM;
1076                }
1077                e.detail = role;
1078            }
1079
1080            public void getSelection(AccessibleControlEvent e) {
1081                e.childID = (selectedIndex == -1) ? ACC.CHILDID_NONE
1082                        : selectedIndex;
1083            }
1084
1085            public void getState(AccessibleControlEvent e) {
1086                int state = 0;
1087                int childID = e.childID;
1088                if (childID == ACC.CHILDID_SELF) {
1089                    state = ACC.STATE_NORMAL;
1090                } else if (childID >= 0 && childID < items.length) {
1091                    state = ACC.STATE_SELECTABLE;
1092                    if (isFocusControl()) {
1093                        state |= ACC.STATE_FOCUSABLE;
1094                    }
1095                    if (selectedIndex == childID) {
1096                        state |= ACC.STATE_SELECTED;
1097                        if (isFocusControl()) {
1098                            state |= ACC.STATE_FOCUSED;
1099                        }
1100                    }
1101                }
1102                e.detail = state;
1103            }
1104
1105            public void getChildren(AccessibleControlEvent e) {
1106                Object JavaDoc[] children = new Object JavaDoc[items.length];
1107                for (int i = 0; i < items.length; i++) {
1108                    children[i] = new Integer JavaDoc(i);
1109                }
1110                e.children = children;
1111            }
1112        });
1113
1114        addListener(SWT.Selection, new Listener() {
1115            public void handleEvent(Event event) {
1116                if (isFocusControl()) {
1117                    if (selectedIndex == -1) {
1118                        accessible.setFocus(ACC.CHILDID_SELF);
1119                    } else {
1120                        accessible.setFocus(selectedIndex);
1121                    }
1122                }
1123            }
1124        });
1125
1126        addListener(SWT.FocusIn, new Listener() {
1127            public void handleEvent(Event event) {
1128                if (selectedIndex == -1) {
1129                    accessible.setFocus(ACC.CHILDID_SELF);
1130                } else {
1131                    accessible.setFocus(selectedIndex);
1132                }
1133            }
1134        });
1135    }
1136
1137    private void setButtonBounds() {
1138
1139        updateArrowBar();
1140        updateCloseBar();
1141
1142        Rectangle area = super.getClientArea();
1143
1144        int offset = 0;
1145        if (topRight != null) {
1146            Point size = topRight.computeSize(SWT.DEFAULT, tabHeight);
1147            int x = area.x + area.width - borderRight - size.x;
1148            int y = onBottom ? area.y + area.height - borderBottom - size.y
1149                    : area.y + borderTop;
1150            topRight.setBounds(x, y, size.x, size.y);
1151            offset = size.x;
1152        }
1153        boolean leftVisible = scroll_leftVisible();
1154        boolean rightVisible = scroll_rightVisible();
1155        if (leftVisible || rightVisible) {
1156            Point size = arrowBar.computeSize(SWT.DEFAULT, tabHeight);
1157            int x = area.x + area.width - borderRight - size.x - offset;
1158            int y = (onBottom) ? area.y + area.height - borderBottom - size.y
1159                    : area.y + borderTop;
1160
1161            arrowBar.setBounds(x, y, size.x, size.y);
1162            ToolItem[] items = arrowBar.getItems();
1163            items[0].setEnabled(leftVisible);
1164            items[1].setEnabled(rightVisible);
1165            arrowBar.setVisible(true);
1166        } else {
1167            arrowBar.setVisible(false);
1168        }
1169
1170        // When the close button is right at the edge of the Tab folder, hide it because
1171
// otherwise it may block off a part of the border on the right
1172
if (showClose) {
1173            inactiveCloseBar.setVisible(false);
1174            CTabItem item = getSelection();
1175            if (item == null) {
1176                closeBar.setVisible(false);
1177            } else {
1178                int toolbarHeight = tabHeight - CTabItem.TOP_MARGIN
1179                        - CTabItem.BOTTOM_MARGIN + 2; // +2 to ignore gap between focus rectangle
1180
Point size = closeBar.computeSize(SWT.DEFAULT, toolbarHeight);
1181                int x = item.x + item.width - size.x - 2; // -2 to not overlap focus rectangle and trim
1182
int y = item.y + Math.max(0, (item.height - toolbarHeight) / 2);
1183                closeBar.setBounds(x, y, size.x, toolbarHeight);
1184                Rectangle toolspace = getToolSpace();
1185                Point folderSize = getSize();
1186                boolean visible = (toolspace.width == 0 || x < toolspace.x)
1187                        && x + size.x < folderSize.x - borderRight;
1188                closeBar.setVisible(visible);
1189            }
1190        }
1191    }
1192
1193    private boolean setItemLocation() {
1194        if (items.length == 0) {
1195            return false;
1196        }
1197        Rectangle area = super.getClientArea();
1198        int x = area.x;
1199        int y = area.y + borderTop;
1200        if (onBottom) {
1201            y = Math.max(0, area.y + area.height - borderBottom - tabHeight);
1202        }
1203
1204        boolean changed = false;
1205        for (int i = topTabIndex - 1; i >= 0; i--) {
1206            // if the first visible tab is not the first tab
1207
CTabItem tab = items[i];
1208            x -= tab.width;
1209            if (!changed && (tab.x != x || tab.y != y)) {
1210                changed = true;
1211            }
1212            // layout tab items from right to left thus making them invisible
1213
tab.x = x;
1214            tab.y = y;
1215        }
1216
1217        x = area.x + borderLeft;
1218        for (int i = topTabIndex; i < items.length; i++) {
1219            // continue laying out remaining, visible items left to right
1220
CTabItem tab = items[i];
1221            tab.x = x;
1222            tab.y = y;
1223            x = x + tab.width;
1224        }
1225        setButtonBounds();
1226        return changed;
1227    }
1228
1229    private void setLastItem(int index) {
1230        if (index < 0 || index > items.length - 1) {
1231            return;
1232        }
1233        Rectangle area = getClientArea();
1234        if (area.width <= 0) {
1235            return;
1236        }
1237        int maxWidth = area.width;
1238        Rectangle toolspace = getToolSpace();
1239        if (toolspace.width > 0) {
1240            maxWidth -= toolspace.width;
1241        }
1242        int tabWidth = items[index].width;
1243        while (index > 0) {
1244            tabWidth += items[index - 1].width;
1245            if (tabWidth > maxWidth) {
1246                break;
1247            }
1248            index--;
1249        }
1250        topTabIndex = index;
1251        setItemLocation();
1252        redrawTabArea(-1);
1253    }
1254
1255    /**
1256     * Layout the items and store the client area size.
1257     */

1258    boolean setItemBounds() {
1259        boolean changed = false;
1260        if (isDisposed()) {
1261            return changed;
1262        }
1263        Rectangle area = super.getClientArea();
1264
1265        xClient = area.x + borderLeft + marginWidth;
1266        if (onBottom) {
1267            yClient = area.y + borderTop + marginHeight;
1268        } else {
1269            yClient = area.y + borderTop + tabHeight + 1 + marginHeight;
1270            // +1 is for the line at the bottom of the tabs
1271
}
1272
1273        if (area.width <= 0 || area.height <= 0 || items.length == 0) {
1274            return changed;
1275        }
1276
1277        int[] widths = new int[items.length];
1278        GC gc = new GC(this);
1279        for (int i = 0; i < items.length; i++) {
1280            widths[i] = items[i].preferredWidth(gc);
1281        }
1282        gc.dispose();
1283
1284        int oldAverageWidth = 0;
1285        int averageWidth = (area.width - borderLeft - borderRight)
1286                / items.length;
1287        while (averageWidth > oldAverageWidth) {
1288            int width = area.width - borderLeft - borderRight;
1289            int count = items.length;
1290            for (int i = 0; i < items.length; i++) {
1291                if (widths[i] < averageWidth) {
1292                    width -= widths[i];
1293                    count--;
1294                }
1295            }
1296            oldAverageWidth = averageWidth;
1297            if (count > 0) {
1298                averageWidth = width / count;
1299            }
1300        }
1301        averageWidth = Math.max(averageWidth, MIN_TAB_WIDTH * tabHeight);
1302        for (int i = 0; i < items.length; i++) {
1303            if (widths[i] > averageWidth) {
1304                widths[i] = averageWidth;
1305            }
1306        }
1307
1308        int totalWidth = 0;
1309        for (int i = 0; i < items.length; i++) {
1310            CTabItem tab = items[i];
1311            if (tab.height != tabHeight || tab.width != widths[i]) {
1312                changed = true;
1313            }
1314            tab.height = tabHeight;
1315            tab.width = widths[i];
1316            totalWidth += widths[i];
1317        }
1318
1319        int areaWidth = area.x + area.width - borderRight;
1320        if (totalWidth <= areaWidth) {
1321            topTabIndex = 0;
1322        }
1323        if (setItemLocation()) {
1324            changed = true;
1325        }
1326
1327        // Is there a gap after last item showing
1328
if (correctLastItem()) {
1329            changed = true;
1330        }
1331        return changed;
1332    }
1333
1334    private boolean onMnemonic(Event event) {
1335        char key = event.character;
1336        for (int i = 0; i < items.length; i++) {
1337            if (items[i] != null) {
1338                char mnemonic = getMnemonic(items[i].getText());
1339                if (mnemonic != '\0') {
1340                    if (Character.toUpperCase(key) == Character
1341                            .toUpperCase(mnemonic)) {
1342                        setSelection(i, true);
1343                        return true;
1344                    }
1345                }
1346            }
1347        }
1348        return false;
1349    }
1350
1351    /**
1352     * Paint the receiver.
1353     */

1354    private void onPaint(Event event) {
1355        Font font = getFont();
1356        if (oldFont == null || !oldFont.equals(font)) {
1357            oldFont = font;
1358            resetTabSize(true);
1359        }
1360        GC gc = event.gc;
1361        Rectangle rect = super.getClientArea();
1362        if (items.length == 0) {
1363            if (showBorders) {
1364                if ((getStyle() & SWT.FLAT) != 0) {
1365                    gc.setForeground(borderColor1);
1366                    gc.drawRectangle(rect.x, rect.y, rect.x + rect.width - 1,
1367                            rect.y + rect.height - 1);
1368                } else {
1369                    gc.setForeground(borderColor1);
1370                    gc.drawRectangle(rect.x, rect.y, rect.x + rect.width - 3,
1371                            rect.y + rect.height - 3);
1372
1373                    // fill in right and bottom edges with parent's background
1374
gc.setBackground(getParent().getBackground());
1375                    gc.fillRectangle(rect.x + rect.width - 2, rect.y, 2,
1376                            rect.height);
1377                    gc.fillRectangle(rect.x, rect.y + rect.height - 2,
1378                            rect.width, 2);
1379                }
1380                gc.setForeground(getForeground());
1381            }
1382            return;
1383        }
1384
1385        // redraw the Border
1386
drawBorder(gc);
1387
1388        rect.x += borderLeft;
1389        rect.y += borderTop;
1390        rect.width -= borderLeft + borderRight;
1391        rect.height -= borderTop + borderBottom;
1392        Rectangle clip = gc.getClipping();
1393        gc.setClipping(clip.intersection(rect));
1394
1395        // Draw the unselected tabs first.
1396
for (int i = 0; i < items.length; i++) {
1397            if (i != selectedIndex
1398                    && event.getBounds().intersects(items[i].getBounds())) {
1399                items[i].onPaint(gc, false);
1400            }
1401        }
1402        // Selected tab comes last
1403
if (selectedIndex != -1) {
1404            items[selectedIndex].onPaint(gc, true);
1405        }
1406
1407        // draw insertion mark
1408
if (insertionIndex > -2) {
1409            gc.setForeground(getDisplay().getSystemColor(
1410                    SWT.COLOR_LIST_SELECTION));
1411            if (insertionIndex == -1) {
1412                Rectangle bounds = items[0].getBounds();
1413                gc.drawLine(bounds.x, bounds.y, bounds.x, bounds.y
1414                        + bounds.height - 1);
1415                gc.drawLine(bounds.x - 2, bounds.y, bounds.x + 2, bounds.y);
1416                gc.drawLine(bounds.x - 1, bounds.y + 1, bounds.x + 1,
1417                        bounds.y + 1);
1418                gc.drawLine(bounds.x - 1, bounds.y + bounds.height - 2,
1419                        bounds.x + 1, bounds.y + bounds.height - 2);
1420                gc.drawLine(bounds.x - 2, bounds.y + bounds.height - 1,
1421                        bounds.x + 2, bounds.y + bounds.height - 1);
1422
1423            } else {
1424                Rectangle bounds = items[insertionIndex].getBounds();
1425                gc.drawLine(bounds.x + bounds.width, bounds.y, bounds.x
1426                        + bounds.width, bounds.y + bounds.height - 1);
1427                gc.drawLine(bounds.x + bounds.width - 2, bounds.y, bounds.x
1428                        + bounds.width + 2, bounds.y);
1429                gc.drawLine(bounds.x + bounds.width - 1, bounds.y + 1, bounds.x
1430                        + bounds.width + 1, bounds.y + 1);
1431                gc.drawLine(bounds.x + bounds.width - 1, bounds.y
1432                        + bounds.height - 2, bounds.x + bounds.width + 1,
1433                        bounds.y + bounds.height - 2);
1434                gc.drawLine(bounds.x + bounds.width - 2, bounds.y
1435                        + bounds.height - 1, bounds.x + bounds.width + 2,
1436                        bounds.y + bounds.height - 1);
1437            }
1438        }
1439
1440        gc.setForeground(getForeground());
1441        gc.setBackground(getBackground());
1442    }
1443
1444    private void redrawTabArea(int index) {
1445        int x = 0, y = 0, width = 0, height = 0;
1446        if (index == -1) {
1447            Rectangle area = super.getClientArea();
1448            if (area.width == 0 || area.height == 0) {
1449                return;
1450            }
1451            width = area.x + area.width - borderLeft - borderRight;
1452            height = tabHeight + 1; // +1 causes top line between content and tabs to be redrawn
1453
x = area.x + borderLeft;
1454            y = area.y + borderTop;
1455            if (onBottom) {
1456                y = Math.max(0, area.y + area.height - borderBottom - height);
1457            }
1458        } else {
1459            CTabItem item = items[index];
1460            x = item.x;
1461            y = item.y;
1462            Rectangle area = super.getClientArea();
1463            width = area.x + area.width - x;
1464            height = item.height;
1465        }
1466        redraw(x, y, width, height, false);
1467    }
1468
1469    /**
1470     * Removes the listener.
1471     *
1472     * @param listener the listener
1473     *
1474     * @exception SWTError
1475     * <ul><li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1476     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1477     * <li>ERROR_NULL_ARGUMENT when listener is null</li></ul>
1478     */

1479    public void removeSelectionListener(SelectionListener listener) {
1480        checkWidget();
1481        if (listener == null) {
1482            SWT.error(SWT.ERROR_NULL_ARGUMENT);
1483        }
1484        removeListener(SWT.Selection, listener);
1485        removeListener(SWT.DefaultSelection, listener);
1486    }
1487
1488    /**
1489     * Removes the listener.
1490     *
1491     * @param listener the listener
1492     *
1493     * @exception SWTError
1494     * <ul><li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1495     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1496     * <li>ERROR_NULL_ARGUMENT when listener is null</li></ul>
1497     */

1498    public void removeCTabFolderListener(CTabFolderListener listener) {
1499        checkWidget();
1500        if (listener == null) {
1501            SWT.error(SWT.ERROR_NULL_ARGUMENT);
1502        }
1503        if (tabListeners.length == 0) {
1504            return;
1505        }
1506        int index = -1;
1507        for (int i = 0; i < tabListeners.length; i++) {
1508            if (listener == tabListeners[i]) {
1509                index = i;
1510                break;
1511            }
1512        }
1513        if (index == -1) {
1514            return;
1515        }
1516        if (tabListeners.length == 1) {
1517            tabListeners = new CTabFolderListener[0];
1518            showClose = false;
1519            setButtonBounds();
1520            return;
1521        }
1522        CTabFolderListener[] newTabListeners = new CTabFolderListener[tabListeners.length - 1];
1523        System.arraycopy(tabListeners, 0, newTabListeners, 0, index);
1524        System.arraycopy(tabListeners, index + 1, newTabListeners, index,
1525                tabListeners.length - index - 1);
1526        tabListeners = newTabListeners;
1527    }
1528
1529    /**
1530     * The widget was resized. Adjust the size of the currently selected page.
1531     */

1532    private void onResize() {
1533
1534        if (items.length == 0) {
1535            redraw();
1536            return;
1537        }
1538
1539        if (setItemBounds()) {
1540            redrawTabArea(-1);
1541        }
1542
1543        Point size = getSize();
1544        if (oldSize == null) {
1545            redraw();
1546        } else {
1547            if (onBottom && size.y != oldSize.y) {
1548                redraw();
1549            } else {
1550                int x1 = Math.min(size.x, oldSize.x);
1551                if (size.x != oldSize.x) {
1552                    x1 -= 10;
1553                }
1554                int y1 = Math.min(size.y, oldSize.y);
1555                if (size.y != oldSize.y) {
1556                    y1 -= 10;
1557                }
1558                int x2 = Math.max(size.x, oldSize.x);
1559                int y2 = Math.max(size.y, oldSize.y);
1560                redraw(0, y1, x2 + 10, y2 - y1, false);
1561                redraw(x1, 0, x2 - x1, y2, false);
1562            }
1563        }
1564        oldSize = size;
1565
1566        // resize content
1567
if (selectedIndex != -1) {
1568            Control control = items[selectedIndex].getControl();
1569            if (control != null && !control.isDisposed()) {
1570                control.setBounds(getClientArea());
1571            }
1572        }
1573    }
1574
1575    public void setBackground(Color color) {
1576        super.setBackground(color);
1577        background = color;
1578        // init inactive close button
1579
inactiveCloseBar.setBackground(color);
1580
1581        // init scroll buttons
1582
arrowBar.setBackground(color);
1583
1584        //init topRight control
1585
if (topRight != null) {
1586            topRight.setBackground(color);
1587        }
1588
1589        // init close button
1590
if (gradientColors == null) {
1591            closeBar.setBackground(color);
1592        }
1593    }
1594
1595    /**
1596     * Specify a gradient of colours to be draw in the background of the selected tab.
1597     * For example to draw a gradient that varies from dark blue to blue and then to
1598     * white, use the following call to setBackground:
1599     * <pre>
1600     * cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
1601     * display.getSystemColor(SWT.COLOR_BLUE),
1602     * display.getSystemColor(SWT.COLOR_WHITE),
1603     * display.getSystemColor(SWT.COLOR_WHITE)},
1604     * new int[] {25, 50, 100});
1605     * </pre>
1606     *
1607     * @param colors an array of Color that specifies the colors to appear in the gradient
1608     * in order of appearance left to right. The value <code>null</code> clears the
1609     * background gradient. The value <code>null</code> can be used inside the array of
1610     * Color to specify the background color.
1611     * @param percents an array of integers between 0 and 100 specifying the percent of the width
1612     * of the widget at which the color should change. The size of the percents array must be one
1613     * less than the size of the colors array.
1614     *
1615     * @exception SWTError <ul>
1616     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1617     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1618     * </ul>
1619     */

1620
1621    public void setSelectionBackground(Color[] colors, int[] percents) {
1622        checkWidget();
1623        if (colors != null) {
1624            if (percents == null || percents.length != colors.length - 1) {
1625                SWT.error(SWT.ERROR_INVALID_ARGUMENT);
1626            }
1627            if (getDisplay().getDepth() < 15) {
1628                // Don't use gradients on low color displays
1629
colors = new Color[] { colors[0] };
1630                percents = new int[] {};
1631            }
1632            for (int i = 0; i < percents.length; i++) {
1633                if (percents[i] < 0 || percents[i] > 100) {
1634                    SWT.error(SWT.ERROR_INVALID_ARGUMENT);
1635                }
1636                if (i > 0 && percents[i] < percents[i - 1]) {
1637                    SWT.error(SWT.ERROR_INVALID_ARGUMENT);
1638                }
1639            }
1640        }
1641
1642        // Are these settings the same as before?
1643
if (backgroundImage == null) {
1644            if ((gradientColors != null) && (colors != null)
1645                    && (gradientColors.length == colors.length)) {
1646                boolean same = false;
1647                for (int i = 0; i < gradientColors.length; i++) {
1648                    if (gradientColors[i] == null) {
1649                        same = colors[i] == null;
1650                    } else {
1651                        same = gradientColors[i].equals(colors[i]);
1652                    }
1653                    if (!same) {
1654                        break;
1655                    }
1656                }
1657                if (same) {
1658                    for (int i = 0; i < gradientPercents.length; i++) {
1659                        same = gradientPercents[i] == percents[i];
1660                        if (!same) {
1661                            break;
1662                        }
1663                    }
1664                }
1665                if (same) {
1666                    return;
1667                }
1668            }
1669        } else {
1670            backgroundImage = null;
1671        }
1672        // Store the new settings
1673
if (colors == null) {
1674            gradientColors = null;
1675            gradientPercents = null;
1676            closeBar.setBackground(background);
1677        } else {
1678            gradientColors = new Color[colors.length];
1679            for (int i = 0; i < colors.length; ++i) {
1680                gradientColors[i] = colors[i];
1681            }
1682            gradientPercents = new int[percents.length];
1683            for (int i = 0; i < percents.length; ++i) {
1684                gradientPercents[i] = percents[i];
1685            }
1686            if (getDisplay().getDepth() < 15) {
1687                closeBar.setBackground(background);
1688            } else {
1689                closeBar
1690                        .setBackground(gradientColors[gradientColors.length - 1]);
1691            }
1692        }
1693
1694        // Refresh with the new settings
1695
if (selectedIndex > -1) {
1696            redrawTabArea(selectedIndex);
1697        }
1698    }
1699
1700    /**
1701     * Set the image to be drawn in the background of the selected tab.
1702     *
1703     * @param image the image to be drawn in the background
1704     *
1705     * @exception SWTException <ul>
1706     * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1707     * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1708     * </ul>
1709     */

1710    public void setSelectionBackground(Image image) {
1711        checkWidget();
1712        if (image == backgroundImage) {
1713            return;
1714        }
1715        if (image != null) {
1716            gradientColors = null;
1717            gradientPercents = null;
1718        }
1719        backgroundImage = image;
1720        redrawTabArea(selectedIndex);
1721    }
1722
1723    /**
1724     * Toggle the visibility of the border
1725     *
1726     * @param show true if the border should be displayed
1727     *
1728     * @exception SWTException <ul>
1729     * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1730     * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1731     * </ul>
1732     */

1733    public void setBorderVisible(boolean show) {
1734        checkWidget();
1735        // if (showBorders == show) return;
1736

1737        showBorders = show;
1738        if (showBorders) {
1739            if ((getStyle() & SWT.FLAT) != 0) {
1740                borderBottom = borderTop = borderLeft = borderRight = 1;
1741            } else {
1742                borderLeft = borderTop = 1;
1743                borderRight = borderBottom = 3;
1744            }
1745        } else {
1746            borderBottom = borderTop = borderLeft = borderRight = 0;
1747        }
1748        oldSize = null;
1749        notifyListeners(SWT.Resize, new Event());
1750    }
1751
1752    public void setFont(Font font) {
1753        checkWidget();
1754        if (font != null && font.equals(getFont())) {
1755            return;
1756        }
1757        super.setFont(font);
1758        oldFont = getFont();
1759        resetTabSize(true);
1760    }
1761
1762    /**
1763     * Set the foreground color of the selected tab.
1764     *
1765     * @param color the color of the text displayed in the selected tab
1766     *
1767     * @exception SWTException <ul>
1768     * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1769     * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1770     * </ul>
1771     */

1772    public void setSelectionForeground(Color color) {
1773        checkWidget();
1774        if (selectionForeground == color) {
1775            return;
1776        }
1777        if (color == null) {
1778            color = getForeground();
1779        }
1780        selectionForeground = color;
1781        if (selectedIndex > -1) {
1782            redrawTabArea(selectedIndex);
1783        }
1784    }
1785
1786    /**
1787     * Display an insert marker before or after the specified tab item.
1788     *
1789     * A value of null will clear the mark.
1790     *
1791     * @param item the item with which the mark is associated or null
1792     *
1793     * @param after true if the mark should be displayed after the specified item
1794     *
1795     * @exception SWTException <ul>
1796     * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1797     * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1798     * </ul>
1799     */

1800    public void setInsertMark(CTabItem item, boolean after) {
1801        checkWidget();
1802        int index = -1;
1803        if (item != null) {
1804            index = indexOf(item);
1805        }
1806        setInsertMark(index, after);
1807    }
1808
1809    /**
1810     * Display an insert marker before or after the specified tab item.
1811     *
1812     * A value of -1 will clear the mark.
1813     *
1814     * @param index the index of the item with which the mark is associated or null
1815     *
1816     * @param after true if the mark should be displayed after the specified item
1817     *
1818     * @exception SWTException <ul>
1819     * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1820     * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1821     * </ul>
1822     */

1823    public void setInsertMark(int index, boolean after) {
1824        checkWidget();
1825        if (index < -1 || index >= getItemCount()) {
1826            SWT.error(SWT.ERROR_INVALID_ARGUMENT);
1827        }
1828
1829        if (index == -1) {
1830            index = -2;
1831        } else {
1832            index = after ? index : --index;
1833        }
1834
1835        if (insertionIndex == index) {
1836            return;
1837        }
1838        int oldIndex = insertionIndex;
1839        insertionIndex = index;
1840        if (index > -1) {
1841            redrawTabArea(index);
1842        }
1843        if (oldIndex > 1) {
1844            redrawTabArea(oldIndex);
1845        }
1846    }
1847
1848    /**
1849     * Set the selection to the tab at the specified index.
1850     *
1851     * @param index the index of the tab item to be selected
1852     *
1853     * @exception SWTException <ul>
1854     * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1855     * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1856     * </ul>
1857     */

1858    public void setSelection(int index) {
1859        checkWidget();
1860        if (index < 0 || index >= items.length) {
1861            return;
1862        }
1863        if (selectedIndex == index) {
1864            return;
1865        }
1866
1867        int oldIndex = selectedIndex;
1868        selectedIndex = index;
1869
1870        Control control = items[index].control;
1871        if (control != null && !control.isDisposed()) {
1872            control.setBounds(getClientArea());
1873            control.setVisible(true);
1874        }
1875
1876        if (oldIndex != -1) {
1877            control = items[oldIndex].control;
1878            if (control != null && !control.isDisposed()) {
1879                control.setVisible(false);
1880            }
1881        }
1882        showItem(items[selectedIndex]);
1883        setButtonBounds();
1884        redrawTabArea(-1);
1885    }
1886
1887    /**
1888     * Set the control that appears in the top right corner of the tab folder.
1889     * Typically this is a close button or a composite with a Menu and close button.
1890     * The topRight control is optional. Setting the top right control to null will remove it from the tab folder.
1891     *
1892     * @since 2.1
1893     *
1894     * @param control the control to be displayed in the top right corner or null
1895     *
1896     * @exception SWTException <ul>
1897     * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1898     * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1899     * <li>ERROR_INVALID_ARGUMENT - if the control is not a child of this CTabFolder</li>
1900     * </ul>
1901     */

1902    public void setTopRight(Control control) {
1903        checkWidget();
1904        if (control != null && control.getParent() != this) {
1905            SWT.error(SWT.ERROR_INVALID_ARGUMENT);
1906        }
1907        topRight = control;
1908        resetTabSize(true);
1909    }
1910
1911    /**
1912     * Shows the item. If the item is already showing in the receiver,
1913     * this method simply returns. Otherwise, the items are scrolled until
1914     * the item is visible.
1915     *
1916     * @param item the item to be shown
1917     *
1918     * @exception IllegalArgumentException <ul>
1919     * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
1920     * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
1921     * </ul>
1922     * @exception SWTException <ul>
1923     * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1924     * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1925     * </ul>
1926     *
1927     * @see CTabFolder#showSelection()
1928     *
1929     * @since 2.0
1930     */

1931    public void showItem(CTabItem item) {
1932        checkWidget();
1933        if (item == null) {
1934            SWT.error(SWT.ERROR_NULL_ARGUMENT);
1935        }
1936        if (item.isDisposed()) {
1937            SWT.error(SWT.ERROR_INVALID_ARGUMENT);
1938        }
1939
1940        int index = indexOf(item);
1941        if (index < topTabIndex) {
1942            topTabIndex = index;
1943            setItemLocation();
1944            redrawTabArea(-1);
1945            return;
1946        }
1947        Rectangle area = getClientArea();
1948        if (area.width <= 0) {
1949            topTabIndex = index;
1950            return;
1951        }
1952        int rightEdge = area.x + area.width;
1953        Rectangle rect = getToolSpace();
1954        if (rect.width > 0) {
1955            rightEdge -= rect.width;
1956        }
1957        if (item.x + item.width < rightEdge) {
1958            return;
1959        }
1960        setLastItem(index);
1961    }
1962
1963    /**
1964     * Shows the selection. If the selection is already showing in the receiver,
1965     * this method simply returns. Otherwise, the items are scrolled until
1966     * the selection is visible.
1967     *
1968     * @exception IllegalArgumentException <ul>
1969     * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
1970     * </ul>
1971     * @exception SWTException <ul>
1972     * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1973     * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1974     * </ul>
1975     *
1976     * @see CTabFolder#showItem(CTabItem)
1977     *
1978     * @since 2.0
1979     *
1980     */

1981    public void showSelection() {
1982        checkWidget();
1983        if (selectedIndex != -1) {
1984            showItem(getSelection());
1985        }
1986    }
1987
1988    char getMnemonic(String JavaDoc string) {
1989        int index = 0;
1990        int length = string.length();
1991        do {
1992            while ((index < length) && (string.charAt(index) != '&')) {
1993                index++;
1994            }
1995            if (++index >= length) {
1996                return '\0';
1997            }
1998            if (string.charAt(index) != '&') {
1999                return string.charAt(index);
2000            }
2001            index++;
2002        } while (index < length);
2003        return '\0';
2004    }
2005
2006    /**
2007     * Set the selection to the tab at the specified item.
2008     *
2009     * @param item the tab item to be selected
2010     *
2011     * @exception SWTException <ul>
2012     * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2013     * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2014     * <li>ERROR_NULL_ARGUMENT - if argument is null</li>
2015     * </ul>
2016     */

2017    public void setSelection(CTabItem item) {
2018        checkWidget();
2019        if (item == null) {
2020            SWT.error(SWT.ERROR_NULL_ARGUMENT);
2021        }
2022        int index = indexOf(item);
2023        setSelection(index);
2024    }
2025
2026    /**
2027     * Set the selection to the tab at the specified index.
2028     */

2029    private void setSelection(int index, boolean notify) {
2030        int oldSelectedIndex = selectedIndex;
2031        setSelection(index);
2032        if (notify && selectedIndex != oldSelectedIndex && selectedIndex != -1) {
2033            Event event = new Event();
2034            event.item = getItem(selectedIndex);
2035            notifyListeners(SWT.Selection, event);
2036        }
2037    }
2038
2039    private Image scaleImage(Image image, int oldSize, int newSize) {
2040        Display display = getDisplay();
2041        Color foreground = getForeground();
2042        Color black = display.getSystemColor(SWT.COLOR_BLACK);
2043        Color background = getBackground();
2044        PaletteData palette = new PaletteData(new RGB[] { foreground.getRGB(),
2045                background.getRGB(), black.getRGB() });
2046        ImageData imageData = new ImageData(newSize, newSize, 4, palette);
2047        imageData.transparentPixel = 1;
2048        Image temp = new Image(display, imageData);
2049        GC gc = new GC(temp);
2050        gc.setBackground(background);
2051        gc.fillRectangle(0, 0, newSize, newSize);
2052        gc.drawImage(image, 0, 0, oldSize, oldSize, 0, 0, newSize, newSize);
2053        gc.dispose();
2054        return temp;
2055    }
2056
2057    private void updateCloseBar() {
2058        //Temporary code - need a better way to determine toolBar trim
2059
int toolbarTrim = 4;
2060        String JavaDoc platform = SWT.getPlatform();
2061        if ("photon".equals(platform)) { //$NON-NLS-1$
2062
toolbarTrim = 6;
2063        }
2064        if ("gtk".equals(platform)) { //$NON-NLS-1$
2065
toolbarTrim = 8;
2066        }
2067
2068        int maxHeight = tabHeight - CTabItem.TOP_MARGIN
2069                - CTabItem.BOTTOM_MARGIN - toolbarTrim;
2070        if (maxHeight < 3) {
2071            return;
2072        }
2073        int imageHeight = (maxHeight < 9) ? 9 : maxHeight;
2074
2075        if (closeImage != null && closeImage.getBounds().height == imageHeight) {
2076            return;
2077        }
2078
2079        if (closeBar != null) {
2080            closeBar.dispose();
2081        }
2082        closeBar = null;
2083        if (inactiveCloseBar != null) {
2084            inactiveCloseBar.dispose();
2085        }
2086        inactiveCloseBar = null;
2087        createCloseBar();
2088
2089        ToolItem closeItem = closeBar.getItems()[0];
2090        ToolItem inactiveCloseItem = inactiveCloseBar.getItems()[0];
2091
2092        if (closeImage != null) {
2093            closeImage.dispose();
2094        }
2095
2096        Display display = getDisplay();
2097        Color foreground = getForeground();
2098        Color black = display.getSystemColor(SWT.COLOR_BLACK);
2099        Color background = getBackground();
2100
2101        PaletteData palette = new PaletteData(new RGB[] { foreground.getRGB(),
2102                background.getRGB(), black.getRGB() });
2103        ImageData imageData = new ImageData(imageHeight, imageHeight, 4,
2104                palette);
2105        imageData.transparentPixel = 1;
2106        closeImage = new Image(display, imageData);
2107        GC gc = new GC(closeImage);
2108        gc.setBackground(background);
2109        gc.fillRectangle(0, 0, imageHeight, imageHeight);
2110        gc.setForeground(black);
2111
2112        //draw an 9x8 'x' centered in image
2113
int h = (imageHeight / 2) * 2;
2114        int inset = (h - 8) / 2;
2115        gc.drawLine(inset, inset, h - inset - 1, h - inset - 1);
2116        gc.drawLine(inset + 1, inset, h - inset, h - inset - 1);
2117        gc.drawLine(inset, h - inset - 1, h - inset - 1, inset);
2118        gc.drawLine(inset + 1, h - inset - 1, h - inset, inset);
2119
2120        gc.dispose();
2121
2122        if (maxHeight < imageHeight) {
2123            //rescale image
2124
Image temp = scaleImage(closeImage, imageHeight, maxHeight);
2125            closeImage.dispose();
2126            closeImage = temp;
2127        }
2128        closeItem.setImage(closeImage);
2129        inactiveCloseItem.setImage(closeImage);
2130    }
2131
2132    private void updateArrowBar() {
2133        //Temporary code - need a better way to determine toolBar trim
2134
int toolbarTrim = 6; // Windows needs 6, photon needs 6, gtk needs 8
2135
String JavaDoc platform = SWT.getPlatform();
2136        if ("gtk".equals(platform)) { //$NON-NLS-1$
2137
toolbarTrim = 8;
2138        }
2139
2140        int maxHeight = tabHeight - toolbarTrim;
2141        if (maxHeight < 3) {
2142            return;
2143        }
2144        int imageHeight = (maxHeight < 9) ? 9 : maxHeight;
2145
2146        if (arrowLeftImage != null
2147                && arrowLeftImage.getBounds().height == imageHeight) {
2148            return;
2149        }
2150
2151        if (arrowBar != null) {
2152            arrowBar.dispose();
2153        }
2154        arrowBar = null;
2155        if (arrowLeftImage != null) {
2156            arrowLeftImage.dispose();
2157        }
2158        if (arrowRightImage != null) {
2159            arrowRightImage.dispose();
2160        }
2161
2162        createArrowBar();
2163        ToolItem[] items = arrowBar.getItems();
2164        ToolItem left = items[0];
2165        ToolItem right = items[1];
2166
2167        Display display = getDisplay();
2168        Color foreground = getForeground();
2169        Color black = display.getSystemColor(SWT.COLOR_BLACK);
2170        Color background = getBackground();
2171
2172        PaletteData palette = new PaletteData(new RGB[] { foreground.getRGB(),
2173                background.getRGB(), black.getRGB() });
2174        ImageData imageData = new ImageData(7, imageHeight, 4, palette);
2175        imageData.transparentPixel = 1;
2176        arrowLeftImage = new Image(display, imageData);
2177        GC gc = new GC(arrowLeftImage);
2178        gc.setBackground(background);
2179        gc.fillRectangle(0, 0, 7, imageHeight);
2180        gc.setBackground(black);
2181        //draw a 9x5 '<' centered vertically in image
2182
int h = (imageHeight / 2) * 2;
2183        int midpoint = h / 2 - 1;
2184        int[] pointArr = new int[] { 6, midpoint - 5, 1, midpoint, 6,
2185                midpoint + 5, };
2186        gc.fillPolygon(pointArr);
2187        gc.dispose();
2188
2189        palette = new PaletteData(new RGB[] { foreground.getRGB(),
2190                background.getRGB(), black.getRGB() });
2191        imageData = new ImageData(7, imageHeight, 4, palette);
2192        imageData.transparentPixel = 1;
2193        arrowRightImage = new Image(display, imageData);
2194        gc = new GC(arrowRightImage);
2195        gc.setBackground(background);
2196        gc.fillRectangle(0, 0, 7, imageHeight);
2197        gc.setBackground(black);
2198        //draw a 9x5 '>' centered vertically in image
2199
pointArr = new int[] { 1, midpoint - 5, 6, midpoint, 1, midpoint + 5, };
2200        gc.fillPolygon(pointArr);
2201        gc.dispose();
2202
2203        if (maxHeight < imageHeight) {
2204            //rescale image
2205
Image leftTemp = scaleImage(arrowLeftImage, imageHeight, maxHeight);
2206            arrowLeftImage.dispose();
2207            arrowLeftImage = leftTemp;
2208
2209            Image rightTemp = scaleImage(arrowRightImage, imageHeight,
2210                    maxHeight);
2211            arrowRightImage.dispose();
2212            arrowRightImage = rightTemp;
2213        }
2214        left.setImage(arrowLeftImage);
2215        right.setImage(arrowRightImage);
2216    }
2217
2218    private void onMouseDoubleClick(Event event) {
2219        Event e = new Event();
2220        e.item = getItem(new Point(event.x, event.y));
2221        notifyListeners(SWT.DefaultSelection, e);
2222    }
2223
2224    /**
2225     * A mouse button was pressed down.
2226     * If a tab was hit select the tab.
2227     */

2228    private void onMouseDown(Event event) {
2229        for (int i = 0; i < items.length; i++) {
2230            if (items[i].getBounds().contains(new Point(event.x, event.y))) {
2231                if (i == selectedIndex) {
2232                    showSelection();
2233                    return;
2234                }
2235                forceFocus();
2236                setSelection(i, true);
2237                if (isFocusControl()) {
2238                    setFocus();
2239                }
2240                return;
2241            }
2242        }
2243    }
2244
2245    private void onMouseExit(Event event) {
2246        Rectangle inactiveBounds = inactiveCloseBar.getBounds();
2247        if (inactiveBounds.contains(event.x, event.y)) {
2248            return;
2249        }
2250        inactiveCloseBar.setVisible(false);
2251        inactiveItem = null;
2252
2253        showToolTip = false;
2254        toolTipItem = null;
2255        if (tip != null && !tip.isDisposed() && tip.isVisible()) {
2256            tip.setVisible(false);
2257        }
2258    }
2259
2260    private void onMouseHover(Event event) {
2261        if (tip == null || tip.isDisposed()) {
2262            return;
2263        }
2264        showToolTip = true;
2265        showToolTip(event.x, event.y);
2266    }
2267
2268    private void showToolTip(int x, int y) {
2269        CTabItem item = getItem(new Point(x, y));
2270        if (item != null) {
2271            if (item == toolTipItem) {
2272                return;
2273            }
2274            toolTipItem = item;
2275            String JavaDoc tooltip = item.getToolTipText();
2276            if (tooltip != null && tooltip.length() > 0) {
2277                Display display = tip.getDisplay();
2278                label.setForeground(display
2279                        .getSystemColor(SWT.COLOR_INFO_FOREGROUND));
2280                label.setBackground(display
2281                        .getSystemColor(SWT.COLOR_INFO_BACKGROUND));
2282                label.setText(tooltip);
2283                Point labelSize = label.computeSize(SWT.DEFAULT, SWT.DEFAULT);
2284                labelSize.x += 2;
2285                labelSize.y += 2;
2286                label.setSize(labelSize);
2287                tip.pack();
2288                /*
2289                 * On some platforms, there is a minimum size for a shell
2290                 * which may be greater than the label size.
2291                 * To avoid having the background of the tip shell showing
2292                 * around the label, force the label to fill the entire client area.
2293                 */

2294                Rectangle area = tip.getClientArea();
2295                label.setSize(area.width, area.height);
2296                /*
2297                 * Position the tooltip and ensure that it is not located off
2298                 * the screen.
2299                 */

2300                Point pt = new Point(item.x + item.width / 4, item.y
2301                        + item.height + 2);
2302                pt = toDisplay(pt);
2303                Rectangle rect = display.getBounds();
2304                Point tipSize = tip.getSize();
2305                pt.x = Math.max(0, Math.min(pt.x, rect.width - tipSize.x));
2306                pt.y = Math.max(0, Math.min(pt.y, rect.height - tipSize.y));
2307                tip.setLocation(pt);
2308                tip.setVisible(true);
2309                return;
2310            }
2311        }
2312
2313        toolTipItem = null;
2314        if (tip != null && !tip.isDisposed() && tip.isVisible()) {
2315            tip.setVisible(false);
2316        }
2317    }
2318
2319    private void onMouseMove(Event event) {
2320        if (showToolTip) {
2321            showToolTip(event.x, event.y);
2322        }
2323
2324        if (!showClose) {
2325            return;
2326        }
2327
2328        CTabItem item = null;
2329        for (int i = 0; i < items.length; i++) {
2330            Rectangle rect = items[i].getBounds();
2331            if (rect.contains(new Point(event.x, event.y))) {
2332                item = items[i];
2333                break;
2334            }
2335        }
2336        if (item == inactiveItem) {
2337            return;
2338        }
2339
2340        inactiveCloseBar.setVisible(false);
2341        inactiveItem = null;
2342
2343        if (item == null || item == getSelection()) {
2344            return;
2345        }
2346
2347        int toolbarHeight = tabHeight - CTabItem.TOP_MARGIN
2348                - CTabItem.BOTTOM_MARGIN + 2; // +2 to ignore gap between focus rectangle
2349
Point size = inactiveCloseBar.computeSize(SWT.DEFAULT, toolbarHeight);
2350        int x = item.x + item.width - size.x - 2; // -2 to not overlap focus rectangle and trim
2351
int y = item.y + Math.max(0, (item.height - toolbarHeight) / 2);
2352        Rectangle toolspace = getToolSpace();
2353        Point folderSize = getSize();
2354        if ((toolspace.width == 0 || x < toolspace.x)
2355                && x + size.x < folderSize.x - borderRight) {
2356            inactiveCloseBar.setBounds(x, y, size.x, toolbarHeight);
2357            inactiveCloseBar.setVisible(true);
2358            inactiveItem = item;
2359        }
2360    }
2361
2362    private void onTraverse(Event event) {
2363        switch (event.detail) {
2364        case SWT.TRAVERSE_ESCAPE:
2365        // TEMPORARY CODE See bug report 17372
2366
// case SWT.TRAVERSE_RETURN:
2367
case SWT.TRAVERSE_TAB_NEXT:
2368        case SWT.TRAVERSE_TAB_PREVIOUS:
2369            event.doit = true;
2370            break;
2371        case SWT.TRAVERSE_MNEMONIC:
2372            event.doit = onMnemonic(event);
2373            if (event.doit) {
2374                event.detail = SWT.TRAVERSE_NONE;
2375            }
2376            break;
2377        case SWT.TRAVERSE_PAGE_NEXT:
2378        case SWT.TRAVERSE_PAGE_PREVIOUS:
2379            event.doit = onPageTraversal(event);
2380            if (event.doit) {
2381                event.detail = SWT.TRAVERSE_NONE;
2382            }
2383            break;
2384        }
2385    }
2386
2387    private boolean onPageTraversal(Event event) {
2388        int count = getItemCount();
2389        if (count == 0) {
2390            return false;
2391        }
2392        int index = getSelectionIndex();
2393        if (index == -1) {
2394            index = 0;
2395        } else {
2396            int offset = (event.detail == SWT.TRAVERSE_PAGE_NEXT) ? 1 : -1;
2397            index = (index + offset + count) % count;
2398        }
2399        setSelection(index, true);
2400        return true;
2401    }
2402
2403    /**
2404     * Answer true if not all tabs can be visible in the receive
2405     * thus requiring the scroll buttons to be visible.
2406     */

2407    private boolean scroll_leftVisible() {
2408        return topTabIndex > 0;
2409    }
2410
2411    /**
2412     * Answer true if not all tabs can be visible in the receive
2413     * thus requiring the scroll buttons to be visible.
2414     */

2415    private boolean scroll_rightVisible() {
2416        // only show Scroll buttons if there is more than one item
2417
// and if we are not already at the last item
2418
if (items.length < 2) {
2419            return false;
2420        }
2421        Rectangle area = getClientArea();
2422        int rightEdge = area.x + area.width;
2423        if (rightEdge <= 0) {
2424            return false;
2425        }
2426        if (topTabIndex > 0) {
2427            rightEdge -= arrowBar.getSize().x;
2428        }
2429        if (topRight != null) {
2430            rightEdge -= topRight.getSize().x;
2431        }
2432        CTabItem item = items[items.length - 1];
2433        return (item.x + item.width > rightEdge);
2434    }
2435
2436    /**
2437     * Scroll the tab items to the left.
2438     */

2439    private void scroll_scrollLeft() {
2440        if (items.length == 0) {
2441            return;
2442        }
2443        setLastItem(topTabIndex - 1);
2444    }
2445
2446    /**
2447     * Scroll the tab items to the right.
2448     */

2449    private void scroll_scrollRight() {
2450        int lastIndex = getLastItem();
2451        topTabIndex = lastIndex + 1;
2452        setItemLocation();
2453        correctLastItem();
2454        redrawTabArea(-1);
2455    }
2456
2457    private boolean correctLastItem() {
2458        Rectangle area = getClientArea();
2459        int rightEdge = area.x + area.width;
2460        if (rightEdge <= 0) {
2461            return false;
2462        }
2463        Rectangle toolspace = getToolSpace();
2464        if (toolspace.width > 0) {
2465            rightEdge -= toolspace.width;
2466        }
2467        CTabItem item = items[items.length - 1];
2468        if (item.x + item.width < rightEdge) {
2469            setLastItem(items.length - 1);
2470            return true;
2471        }
2472        return false;
2473    }
2474
2475    /**
2476     * Specify a fixed height for the tab items. If no height is specified,
2477     * the default height is the height of the text or the image, whichever
2478     * is greater. Specifying a height of 0 will revert to the default height.
2479     *
2480     * @param height the pixel value of the height or 0
2481     *
2482     * @exception SWTException <ul>
2483     * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2484     * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2485     * <li>ERROR_INVALID_ARGUMENT - if called with a height of less than 0</li>
2486     * </ul>
2487     */

2488    public void setTabHeight(int height) {
2489        checkWidget();
2490        if (height < 0) {
2491            SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2492        }
2493        fixedTabHeight = true;
2494        if (tabHeight == height) {
2495            return;
2496        }
2497        tabHeight = height;
2498        oldSize = null;
2499        notifyListeners(SWT.Resize, new Event());
2500    }
2501
2502    void resetTabSize(boolean checkHeight) {
2503        int oldHeight = tabHeight;
2504        if (!fixedTabHeight && checkHeight) {
2505            int tempHeight = 0;
2506            GC gc = new GC(this);
2507            for (int i = 0; i < items.length; i++) {
2508                tempHeight = Math.max(tempHeight, items[i].preferredHeight(gc));
2509            }
2510            gc.dispose();
2511            if (topRight != null) {
2512                tempHeight = Math.max(tempHeight, topRight.computeSize(
2513                        SWT.DEFAULT, SWT.DEFAULT).y);
2514            }
2515            tabHeight = tempHeight;
2516        }
2517
2518        if (tabHeight != oldHeight) {
2519            oldSize = null;
2520            notifyListeners(SWT.Resize, new Event());
2521        } else {
2522            setItemBounds();
2523            redraw();
2524        }
2525    }
2526
2527    /**
2528     *
2529     * @exception SWTException <ul>
2530     * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2531     * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2532     * <li>ERROR_INVALID_ARGUMENT - if the position value is not either SWT.TOP or SWT.BOTTOM</li>
2533     * </ul>
2534     *
2535     * UNDER CONSTRUCTION
2536     * @since 3.0
2537     */

2538    public void setTabPosition(int position) {
2539        checkWidget();
2540        if (position != SWT.TOP && position != SWT.BOTTOM) {
2541            SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2542        }
2543        if (onBottom != (position == SWT.BOTTOM)) {
2544            onBottom = position == SWT.BOTTOM;
2545            setBorderVisible(showBorders);
2546            resetTabSize(true);
2547            // updateTabHeight(true);
2548
// Rectangle rectBefore = getClientArea();
2549
// updateItems();
2550
// Rectangle rectAfter = getClientArea();
2551
// if (!rectBefore.equals(rectAfter)) {
2552
// notifyListeners(SWT.Resize, new Event());
2553
// }
2554
// setItemBounds();
2555
// redrawTabArea(-1);
2556
// redraw();
2557
}
2558    }
2559
2560    public int getTabPosition() {
2561        if (onBottom) {
2562            return SWT.BOTTOM;
2563        }
2564        return SWT.TOP;
2565    }
2566
2567}
2568
Popular Tags