KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > JTabbedPane


1 /*
2  * @(#)JTabbedPane.java 1.140 04/04/02
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7  
8 package javax.swing;
9
10 import java.awt.*;
11 import java.awt.event.*;
12 import java.beans.*;
13 import java.util.*;
14 import javax.swing.event.*;
15 import javax.swing.plaf.*;
16 import javax.accessibility.*;
17
18 import java.io.Serializable JavaDoc;
19 import java.io.ObjectOutputStream JavaDoc;
20 import java.io.ObjectInputStream JavaDoc;
21 import java.io.IOException JavaDoc;
22
23 /**
24  * A component that lets the user switch between a group of components by
25  * clicking on a tab with a given title and/or icon.
26  * For examples and information on using tabbed panes see
27  * <a HREF="http://java.sun.com/docs/books/tutorial/uiswing/components/tabbedpane.html">How to Use Tabbed Panes</a>,
28  * a section in <em>The Java Tutorial</em>.
29  * <p>
30  * Tabs/components are added to a <code>TabbedPane</code> object by using the
31  * <code>addTab</code> and <code>insertTab</code> methods.
32  * A tab is represented by an index corresponding
33  * to the position it was added in, where the first tab has an index equal to 0
34  * and the last tab has an index equal to the tab count minus 1.
35  * <p>
36  * The <code>TabbedPane</code> uses a <code>SingleSelectionModel</code>
37  * to represent the set
38  * of tab indices and the currently selected index. If the tab count
39  * is greater than 0, then there will always be a selected index, which
40  * by default will be initialized to the first tab. If the tab count is
41  * 0, then the selected index will be -1.
42  * <p>
43  * <strong>Warning:</strong>
44  * Serialized objects of this class will not be compatible with
45  * future Swing releases. The current serialization support is
46  * appropriate for short term storage or RMI between applications running
47  * the same version of Swing. As of 1.4, support for long term storage
48  * of all JavaBeans<sup><font size="-2">TM</font></sup>
49  * has been added to the <code>java.beans</code> package.
50  * Please see {@link java.beans.XMLEncoder}.
51  *
52  * @beaninfo
53  * attribute: isContainer true
54  * description: A component which provides a tab folder metaphor for
55  * displaying one component from a set of components.
56  *
57  * @version 1.140 04/02/04
58  * @author Dave Moore
59  * @author Philip Milne
60  * @author Amy Fowler
61  *
62  * @see SingleSelectionModel
63  */

64 public class JTabbedPane extends JComponent JavaDoc
65        implements Serializable JavaDoc, Accessible, SwingConstants JavaDoc {
66
67    /**
68     * The tab layout policy for wrapping tabs in multiple runs when all
69     * tabs will not fit within a single run.
70     */

71     public static final int WRAP_TAB_LAYOUT = 0;
72
73    /**
74     * Tab layout policy for providing a subset of available tabs when all
75     * the tabs will not fit within a single run. If all the tabs do
76     * not fit within a single run the look and feel will provide a way
77     * to navigate to hidden tabs.
78     */

79     public static final int SCROLL_TAB_LAYOUT = 1;
80
81
82     /**
83      * @see #getUIClassID
84      * @see #readObject
85      */

86     private static final String JavaDoc uiClassID = "TabbedPaneUI";
87
88     /**
89      * Where the tabs are placed.
90      * @see #setTabPlacement
91      */

92     protected int tabPlacement = TOP;
93
94     private int tabLayoutPolicy;
95
96     /** The default selection model */
97     protected SingleSelectionModel JavaDoc model;
98
99     private boolean haveRegistered;
100
101     /**
102      * The <code>changeListener</code> is the listener we add to the
103      * model.
104      */

105     protected ChangeListener changeListener = null;
106
107     Vector pages;
108
109     /**
110      * Only one <code>ChangeEvent</code> is needed per <code>TabPane</code>
111      * instance since the
112      * event's only (read-only) state is the source property. The source
113      * of events generated here is always "this".
114      */

115     protected transient ChangeEvent changeEvent = null;
116
117     /**
118      * Creates an empty <code>TabbedPane</code> with a default
119      * tab placement of <code>JTabbedPane.TOP</code>.
120      * @see #addTab
121      */

122     public JTabbedPane() {
123         this(TOP, WRAP_TAB_LAYOUT);
124     }
125
126     /**
127      * Creates an empty <code>TabbedPane</code> with the specified tab placement
128      * of either: <code>JTabbedPane.TOP</code>, <code>JTabbedPane.BOTTOM</code>,
129      * <code>JTabbedPane.LEFT</code>, or <code>JTabbedPane.RIGHT</code>.
130      *
131      * @param tabPlacement the placement for the tabs relative to the content
132      * @see #addTab
133      */

134     public JTabbedPane(int tabPlacement) {
135     this(tabPlacement, WRAP_TAB_LAYOUT);
136     }
137
138     /**
139      * Creates an empty <code>TabbedPane</code> with the specified tab placement
140      * and tab layout policy. Tab placement may be either:
141      * <code>JTabbedPane.TOP</code>, <code>JTabbedPane.BOTTOM</code>,
142      * <code>JTabbedPane.LEFT</code>, or <code>JTabbedPane.RIGHT</code>.
143      * Tab layout policy may be either: <code>JTabbedPane.WRAP_TAB_LAYOUT</code>
144      * or <code>JTabbedPane.SCROLL_TAB_LAYOUT</code>.
145      *
146      * @param tabPlacement the placement for the tabs relative to the content
147      * @param tabLayoutPolicy the policy for laying out tabs when all tabs will not fit on one run
148      * @exception IllegalArgumentException if tab placement or tab layout policy are not
149      * one of the above supported values
150      * @see #addTab
151      * @since 1.4
152      */

153     public JTabbedPane(int tabPlacement, int tabLayoutPolicy) {
154         setTabPlacement(tabPlacement);
155     setTabLayoutPolicy(tabLayoutPolicy);
156         pages = new Vector(1);
157         setModel(new DefaultSingleSelectionModel JavaDoc());
158         updateUI();
159     }
160
161     /**
162      * Returns the UI object which implements the L&F for this component.
163      *
164      * @return a <code>TabbedPaneUI</code> object
165      * @see #setUI
166      */

167     public TabbedPaneUI getUI() {
168         return (TabbedPaneUI)ui;
169     }
170
171     /**
172      * Sets the UI object which implements the L&F for this component.
173      *
174      * @param ui the new UI object
175      * @see UIDefaults#getUI
176      * @beaninfo
177      * bound: true
178      * hidden: true
179      * attribute: visualUpdate true
180      * description: The UI object that implements the tabbedpane's LookAndFeel
181      */

182     public void setUI(TabbedPaneUI ui) {
183         super.setUI(ui);
184         // disabled icons are generated by LF so they should be unset here
185
for (int i = 0; i < getTabCount(); i++) {
186             Icon JavaDoc icon = ((Page)pages.elementAt(i)).disabledIcon;
187             if (icon instanceof UIResource) {
188                 setDisabledIconAt(i, null);
189             }
190         }
191     }
192
193     /**
194      * Resets the UI property to a value from the current look and feel.
195      *
196      * @see JComponent#updateUI
197      */

198     public void updateUI() {
199         setUI((TabbedPaneUI)UIManager.getUI(this));
200     }
201
202
203     /**
204      * Returns the name of the UI class that implements the
205      * L&F for this component.
206      *
207      * @return the string "TabbedPaneUI"
208      * @see JComponent#getUIClassID
209      * @see UIDefaults#getUI
210      */

211     public String JavaDoc getUIClassID() {
212         return uiClassID;
213     }
214
215
216     /**
217      * We pass <code>ModelChanged</code> events along to the listeners with
218      * the tabbedpane (instead of the model itself) as the event source.
219      */

220     protected class ModelListener implements ChangeListener, Serializable JavaDoc {
221         public void stateChanged(ChangeEvent e) {
222             fireStateChanged();
223         }
224     }
225
226     /**
227      * Subclasses that want to handle <code>ChangeEvents</code> differently
228      * can override this to return a subclass of <code>ModelListener</code> or
229      * another <code>ChangeListener</code> implementation.
230      *
231      * @see #fireStateChanged
232      */

233     protected ChangeListener createChangeListener() {
234         return new ModelListener();
235     }
236
237     /**
238      * Adds a <code>ChangeListener</code> to this tabbedpane.
239      *
240      * @param l the <code>ChangeListener</code> to add
241      * @see #fireStateChanged
242      * @see #removeChangeListener
243      */

244     public void addChangeListener(ChangeListener l) {
245         listenerList.add(ChangeListener.class, l);
246     }
247     
248     /**
249      * Removes a <code>ChangeListener</code> from this tabbedpane.
250      *
251      * @param l the <code>ChangeListener</code> to remove
252      * @see #fireStateChanged
253      * @see #addChangeListener
254      */

255     public void removeChangeListener(ChangeListener l) {
256         listenerList.remove(ChangeListener.class, l);
257     }
258         
259    /**
260      * Returns an array of all the <code>ChangeListener</code>s added
261      * to this <code>JTabbedPane</code> with <code>addChangeListener</code>.
262      *
263      * @return all of the <code>ChangeListener</code>s added or an empty
264      * array if no listeners have been added
265      * @since 1.4
266      */

267     public ChangeListener[] getChangeListeners() {
268         return (ChangeListener[])listenerList.getListeners(
269                 ChangeListener.class);
270     }
271
272     /**
273      * Sends a <code>ChangeEvent</code>, whose source is this tabbedpane,
274      * to each listener. This method method is called each time
275      * a <code>ChangeEvent</code> is received from the model.
276      *
277      * @see #addChangeListener
278      * @see EventListenerList
279      */

280     protected void fireStateChanged() {
281         // Guaranteed to return a non-null array
282
Object JavaDoc[] listeners = listenerList.getListenerList();
283         // Process the listeners last to first, notifying
284
// those that are interested in this event
285
for (int i = listeners.length-2; i>=0; i-=2) {
286             if (listeners[i]==ChangeListener.class) {
287                 // Lazily create the event:
288
if (changeEvent == null)
289                     changeEvent = new ChangeEvent(this);
290                 ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
291             }
292         }
293     }
294
295     /**
296      * Returns the model associated with this tabbedpane.
297      *
298      * @see #setModel
299      */

300     public SingleSelectionModel JavaDoc getModel() {
301         return model;
302     }
303
304     /**
305      * Sets the model to be used with this tabbedpane.
306      *
307      * @param model the model to be used
308      * @see #getModel
309      * @beaninfo
310      * bound: true
311      * description: The tabbedpane's SingleSelectionModel.
312      */

313     public void setModel(SingleSelectionModel JavaDoc model) {
314         SingleSelectionModel JavaDoc oldModel = getModel();
315
316         if (oldModel != null) {
317             oldModel.removeChangeListener(changeListener);
318             changeListener = null;
319         }
320
321         this.model = model;
322
323         if (model != null) {
324             changeListener = createChangeListener();
325             model.addChangeListener(changeListener);
326         }
327
328         firePropertyChange("model", oldModel, model);
329         repaint();
330     }
331
332     /**
333      * Returns the placement of the tabs for this tabbedpane.
334      * @see #setTabPlacement
335      */

336     public int getTabPlacement() {
337         return tabPlacement;
338     }
339
340     /**
341      * Sets the tab placement for this tabbedpane.
342      * Possible values are:<ul>
343      * <li><code>JTabbedPane.TOP</code>
344      * <li><code>JTabbedPane.BOTTOM</code>
345      * <li><code>JTabbedPane.LEFT</code>
346      * <li><code>JTabbedPane.RIGHT</code>
347      * </ul>
348      * The default value, if not set, is <code>SwingConstants.TOP</code>.
349      *
350      * @param tabPlacement the placement for the tabs relative to the content
351      * @exception IllegalArgumentException if tab placement value isn't one
352      * of the above valid values
353      *
354      * @beaninfo
355      * preferred: true
356      * bound: true
357      * attribute: visualUpdate true
358      * enum: TOP JTabbedPane.TOP
359      * LEFT JTabbedPane.LEFT
360      * BOTTOM JTabbedPane.BOTTOM
361      * RIGHT JTabbedPane.RIGHT
362      * description: The tabbedpane's tab placement.
363      *
364      */

365     public void setTabPlacement(int tabPlacement) {
366         if (tabPlacement != TOP && tabPlacement != LEFT &&
367             tabPlacement != BOTTOM && tabPlacement != RIGHT) {
368             throw new IllegalArgumentException JavaDoc("illegal tab placement: must be TOP, BOTTOM, LEFT, or RIGHT");
369         }
370         if (this.tabPlacement != tabPlacement) {
371             int oldValue = this.tabPlacement;
372             this.tabPlacement = tabPlacement;
373             firePropertyChange("tabPlacement", oldValue, tabPlacement);
374             revalidate();
375             repaint();
376         }
377     }
378
379     /**
380      * Returns the policy used by the tabbedpane to layout the tabs when all the
381      * tabs will not fit within a single run.
382      * @see #setTabLayoutPolicy
383      * @since 1.4
384      */

385     public int getTabLayoutPolicy() {
386         return tabLayoutPolicy;
387     }
388
389    /**
390      * Sets the policy which the tabbedpane will use in laying out the tabs
391      * when all the tabs will not fit within a single run.
392      * Possible values are:
393      * <ul>
394      * <li><code>JTabbedPane.WRAP_TAB_LAYOUT</code>
395      * <li><code>JTabbedPane.SCROLL_TAB_LAYOUT</code>
396      * </ul>
397      *
398      * The default value, if not set by the UI, is <code>JTabbedPane.WRAP_TAB_LAYOUT</code>.
399      * <p>
400      * Some look and feels might only support a subset of the possible
401      * layout policies, in which case the value of this property may be
402      * ignored.
403      *
404      * @param tabLayoutPolicy the policy used to layout the tabs
405      * @exception IllegalArgumentException if layoutPolicy value isn't one
406      * of the above valid values
407      * @see #getTabLayoutPolicy
408      * @since 1.4
409      *
410      * @beaninfo
411      * preferred: true
412      * bound: true
413      * attribute: visualUpdate true
414      * enum: WRAP_TAB_LAYOUT JTabbedPane.WRAP_TAB_LAYOUT
415      * SCROLL_TAB_LAYOUT JTabbedPane.SCROLL_TAB_LAYOUT
416      * description: The tabbedpane's policy for laying out the tabs
417      *
418      */

419     public void setTabLayoutPolicy(int tabLayoutPolicy) {
420         if (tabLayoutPolicy != WRAP_TAB_LAYOUT && tabLayoutPolicy != SCROLL_TAB_LAYOUT) {
421             throw new IllegalArgumentException JavaDoc("illegal tab layout policy: must be WRAP_TAB_LAYOUT or SCROLL_TAB_LAYOUT");
422         }
423         if (this.tabLayoutPolicy != tabLayoutPolicy) {
424             int oldValue = this.tabLayoutPolicy;
425             this.tabLayoutPolicy = tabLayoutPolicy;
426             firePropertyChange("tabLayoutPolicy", oldValue, tabLayoutPolicy);
427             revalidate();
428             repaint();
429         }
430     }
431
432     /**
433      * Returns the currently selected index for this tabbedpane.
434      * Returns -1 if there is no currently selected tab.
435      *
436      * @return the index of the selected tab
437      * @see #setSelectedIndex
438      */

439     public int getSelectedIndex() {
440         return model.getSelectedIndex();
441     }
442
443     /**
444      * Sets the selected index for this tabbedpane. The index must be
445      * a valid tab index or -1, which indicates that no tab should be selected
446      * (can also be used when there are no tabs in the tabbedpane). If a -1
447      * value is specified when the tabbedpane contains one or more tabs, then
448      * the results will be implementation defined.
449      *
450      * @param index the index to be selected
451      * @exception IndexOutOfBoundsException if index is out of range
452      * (index < -1 || index >= tab count)
453      *
454      * @see #getSelectedIndex
455      * @see SingleSelectionModel#setSelectedIndex
456      * @beaninfo
457      * preferred: true
458      * description: The tabbedpane's selected tab index.
459      */

460     public void setSelectedIndex(int index) {
461     if (index != -1) {
462         checkIndex(index);
463     }
464     setSelectedIndexImpl(index);
465     }
466
467
468     private void setSelectedIndexImpl(int index) {
469         int oldIndex = model.getSelectedIndex();
470         Page oldPage = null, newPage = null;
471         if ((oldIndex >= 0) && (oldIndex != index)) {
472             oldPage = (Page) pages.elementAt(oldIndex);
473         }
474         if ((index >= 0) && (oldIndex != index)) {
475             newPage = (Page) pages.elementAt(index);
476         }
477
478         model.setSelectedIndex(index);
479
480         String JavaDoc oldName = null;
481         String JavaDoc newName = null;
482
483         if (oldPage != null) {
484             oldPage.firePropertyChange(
485                 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
486                 AccessibleState.SELECTED, null);
487             // TIGER - 4840667
488
AccessibleContext ac = oldPage.getAccessibleContext();
489             if (ac != null) {
490                 oldName = ac.getAccessibleName();
491             }
492
493         }
494         if (newPage != null) {
495             newPage.firePropertyChange(
496                 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
497                 null, AccessibleState.SELECTED);
498             // TIGER - 4840667
499
AccessibleContext ac = newPage.getAccessibleContext();
500             if (ac != null) {
501                 newName = ac.getAccessibleName();
502             }
503
504         }
505
506         // TIGER - 4840667
507
if (newName != null) {
508             getAccessibleContext().firePropertyChange(
509                 AccessibleContext.ACCESSIBLE_NAME_PROPERTY, oldName, newName);
510         }
511
512     }
513
514     /**
515      * Returns the currently selected component for this tabbedpane.
516      * Returns <code>null</code> if there is no currently selected tab.
517      *
518      * @return the component corresponding to the selected tab
519      * @see #setSelectedComponent
520      */

521     public Component getSelectedComponent() {
522         int index = getSelectedIndex();
523         if (index == -1) {
524             return null;
525         }
526         return getComponentAt(index);
527     }
528
529     /**
530      * Sets the selected component for this tabbedpane. This
531      * will automatically set the <code>selectedIndex</code> to the index
532      * corresponding to the specified component.
533      *
534      * @exception IllegalArgumentException if component not found in tabbed
535      * pane
536      * @see #getSelectedComponent
537      * @beaninfo
538      * preferred: true
539      * description: The tabbedpane's selected component.
540      */

541     public void setSelectedComponent(Component c) {
542         int index = indexOfComponent(c);
543         if (index != -1) {
544             setSelectedIndex(index);
545         } else {
546             throw new IllegalArgumentException JavaDoc("component not found in tabbed pane");
547         }
548     }
549
550     /**
551      * Inserts a <code>component</code>, at <code>index</code>,
552      * represented by a <code>title</code> and/or <code>icon</code>,
553      * either of which may be <code>null</code>.
554      * Uses java.util.Vector internally, see <code>insertElementAt</code>
555      * for details of insertion conventions.
556      *
557      * @param title the title to be displayed in this tab
558      * @param icon the icon to be displayed in this tab
559      * @param component The component to be displayed when this tab is clicked.
560      * @param tip the tooltip to be displayed for this tab
561      * @param index the position to insert this new tab
562      *
563      * @see #addTab
564      * @see #removeTabAt
565      */

566     public void insertTab(String JavaDoc title, Icon JavaDoc icon, Component component, String JavaDoc tip, int index) {
567     int newIndex = index;
568
569         // If component already exists, remove corresponding
570
// tab so that new tab gets added correctly
571
// Note: we are allowing component=null because of compatibility,
572
// but we really should throw an exception because much of the
573
// rest of the JTabbedPane implementation isn't designed to deal
574
// with null components for tabs.
575
int removeIndex = indexOfComponent(component);
576         if (component != null && removeIndex != -1) {
577             removeTabAt(removeIndex);
578         if (newIndex > removeIndex) {
579         newIndex--;
580         }
581         }
582
583         int selectedIndex = getSelectedIndex();
584
585         pages.insertElementAt(new Page(this, title != null? title : "", icon, null,
586                                        component, tip), newIndex);
587
588
589         if (component != null) {
590             addImpl(component, null, -1);
591             component.setVisible(false);
592         }
593
594         if (pages.size() == 1) {
595             setSelectedIndex(0);
596         }
597
598         if (selectedIndex >= newIndex) {
599             setSelectedIndex(selectedIndex + 1);
600         }
601
602         if (!haveRegistered && tip != null) {
603             ToolTipManager.sharedInstance().registerComponent(this);
604             haveRegistered = true;
605         }
606
607         if (accessibleContext != null) {
608             accessibleContext.firePropertyChange(
609                     AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
610                     null, component);
611         }
612         revalidate();
613         repaint();
614     }
615
616     /**
617      * Adds a <code>component</code> and <code>tip</code>
618      * represented by a <code>title</code> and/or <code>icon</code>,
619      * either of which can be <code>null</code>.
620      * Cover method for <code>insertTab</code>.
621      *
622      * @param title the title to be displayed in this tab
623      * @param icon the icon to be displayed in this tab
624      * @param component the component to be displayed when this tab is clicked
625      * @param tip the tooltip to be displayed for this tab
626      *
627      * @see #insertTab
628      * @see #removeTabAt
629      */

630     public void addTab(String JavaDoc title, Icon JavaDoc icon, Component component, String JavaDoc tip) {
631         insertTab(title, icon, component, tip, pages.size());
632     }
633
634     /**
635      * Adds a <code>component</code> represented by a <code>title</code>
636      * and/or <code>icon</code>, either of which can be <code>null</code>.
637      * Cover method for <code>insertTab</code>.
638      *
639      * @param title the title to be displayed in this tab
640      * @param icon the icon to be displayed in this tab
641      * @param component the component to be displayed when this tab is clicked
642      *
643      * @see #insertTab
644      * @see #removeTabAt
645      */

646     public void addTab(String JavaDoc title, Icon JavaDoc icon, Component component) {
647         insertTab(title, icon, component, null, pages.size());
648     }
649
650     /**
651      * Adds a <code>component</code> represented by a <code>title</code>
652      * and no icon.
653      * Cover method for <code>insertTab</code>.
654      *
655      * @param title the title to be displayed in this tab
656      * @param component the component to be displayed when this tab is clicked
657      *
658      * @see #insertTab
659      * @see #removeTabAt
660      */

661     public void addTab(String JavaDoc title, Component component) {
662         insertTab(title, null, component, null, pages.size());
663     }
664
665     /**
666      * Adds a <code>component</code> with a tab title defaulting to
667      * the name of the component which is the result of calling
668      * <code>component.getName</code>.
669      * Cover method for <code>insertTab</code>.
670      *
671      * @param component the component to be displayed when this tab is clicked
672      * @return the component
673      *
674      * @see #insertTab
675      * @see #removeTabAt
676      */

677     public Component add(Component component) {
678     if (!(component instanceof UIResource)) {
679             addTab(component.getName(), component);
680     } else {
681         super.add(component);
682     }
683         return component;
684     }
685
686     /**
687      * Adds a <code>component</code> with the specified tab title.
688      * Cover method for <code>insertTab</code>.
689      *
690      * @param title the title to be displayed in this tab
691      * @param component the component to be displayed when this tab is clicked
692      * @return the component
693      *
694      * @see #insertTab
695      * @see #removeTabAt
696      */

697     public Component add(String JavaDoc title, Component component) {
698     if (!(component instanceof UIResource)) {
699             addTab(title, component);
700     } else {
701         super.add(title, component);
702     }
703         return component;
704     }
705
706     /**
707      * Adds a <code>component</code> at the specified tab index with a tab
708      * title defaulting to the name of the component.
709      * Cover method for <code>insertTab</code>.
710      *
711      * @param component the component to be displayed when this tab is clicked
712      * @param index the position to insert this new tab
713      * @return the component
714      *
715      * @see #insertTab
716      * @see #removeTabAt
717      */

718     public Component add(Component component, int index) {
719     if (!(component instanceof UIResource)) {
720             // Container.add() interprets -1 as "append", so convert
721
// the index appropriately to be handled by the vector
722
insertTab(component.getName(), null, component, null,
723                       index == -1? getTabCount() : index);
724     } else {
725         super.add(component, index);
726     }
727         return component;
728     }
729
730     /**
731      * Adds a <code>component</code> to the tabbed pane.
732      * If <code>constraints</code> is a <code>String</code> or an
733      * <code>Icon</code>, it will be used for the tab title,
734      * otherwise the component's name will be used as the tab title.
735      * Cover method for <code>insertTab</code>.
736      *
737      * @param component the component to be displayed when this tab is clicked
738      * @param constraints the object to be displayed in the tab
739      *
740      * @see #insertTab
741      * @see #removeTabAt
742      */

743     public void add(Component component, Object JavaDoc constraints) {
744     if (!(component instanceof UIResource)) {
745             if (constraints instanceof String JavaDoc) {
746                 addTab((String JavaDoc)constraints, component);
747             } else if (constraints instanceof Icon JavaDoc) {
748                 addTab(null, (Icon JavaDoc)constraints, component);
749             } else {
750                 add(component);
751         }
752         } else {
753         super.add(component, constraints);
754     }
755     }
756
757     /**
758      * Adds a <code>component</code> at the specified tab index.
759      * If <code>constraints</code> is a <code>String</code> or an
760      * <code>Icon</code>, it will be used for the tab title,
761      * otherwise the component's name will be used as the tab title.
762      * Cover method for <code>insertTab</code>.
763      *
764      * @param component the component to be displayed when this tab is clicked
765      * @param constraints the object to be displayed in the tab
766      * @param index the position to insert this new tab
767      *
768      * @see #insertTab
769      * @see #removeTabAt
770      */

771     public void add(Component component, Object JavaDoc constraints, int index) {
772     if (!(component instanceof UIResource)) {
773
774             Icon JavaDoc icon = constraints instanceof Icon JavaDoc? (Icon JavaDoc)constraints : null;
775             String JavaDoc title = constraints instanceof String JavaDoc? (String JavaDoc)constraints : null;
776             // Container.add() interprets -1 as "append", so convert
777
// the index appropriately to be handled by the vector
778
insertTab(title, icon, component, null, index == -1? getTabCount() : index);
779     } else {
780         super.add(component, constraints, index);
781     }
782     }
783
784     /**
785      * Removes the tab at <code>index</code>.
786      * After the component associated with <code>index</code> is removed,
787      * its visibility is reset to true to ensure it will be visible
788      * if added to other containers.
789      * @param index the index of the tab to be removed
790      * @exception IndexOutOfBoundsException if index is out of range
791      * (index < 0 || index >= tab count)
792      *
793      * @see #addTab
794      * @see #insertTab
795      */

796     public void removeTabAt(int index) {
797         checkIndex(index);
798
799         // If we are removing the currently selected tab AND
800
// it happens to be the last tab in the bunch, then
801
// select the previous tab
802
int tabCount = getTabCount();
803         int selected = getSelectedIndex();
804         if (selected >= (tabCount - 1)) {
805             setSelectedIndexImpl(selected - 1);
806         }
807
808         Component component = getComponentAt(index);
809
810         if (accessibleContext != null) {
811             accessibleContext.firePropertyChange(
812                     AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
813                     component, null);
814         }
815
816         pages.removeElementAt(index);
817
818         // NOTE 4/15/2002 (joutwate):
819
// This fix is implemented using client properties since there is
820
// currently no IndexPropertyChangeEvent. Once
821
// IndexPropertyChangeEvents have been added this code should be
822
// modified to use it.
823
putClientProperty("__index_to_remove__", new Integer JavaDoc(index));
824
825         // We can't assume the tab indices correspond to the
826
// container's children array indices, so make sure we
827
// remove the correct child!
828
if (component != null) {
829         Component components[] = getComponents();
830         for (int i = components.length; --i >= 0; ) {
831         if (components[i] == component) {
832                     super.remove(i);
833                     component.setVisible(true);
834                     break;
835         }
836         }
837         }
838
839         revalidate();
840         repaint();
841     }
842
843     /**
844      * Removes the specified <code>Component</code> from the
845      * <code>JTabbedPane</code>. The method does nothing
846      * if the <code>component</code> is null.
847      *
848      * @param component the component to remove from the tabbedpane
849      * @see #addTab
850      * @see #removeTabAt
851      */

852     public void remove(Component component) {
853         int index = indexOfComponent(component);
854         if (index != -1) {
855             removeTabAt(index);
856         } else {
857         // Container#remove(comp) invokes Container#remove(int)
858
// so make sure JTabbedPane#remove(int) isn't called here
859
Component children[] = getComponents();
860         for (int i=0; i < children.length; i++) {
861         if (component == children[i]) {
862             super.remove(i);
863             break;
864         }
865         }
866         }
867     }
868
869     /**
870      * Removes the tab and component which corresponds to the specified index.
871      *
872      * @param index the index of the component to remove from the
873      * <code>tabbedpane</code>
874      * @exception IndexOutOfBoundsException if index is out of range
875      * (index < 0 || index >= tab count)
876      * @see #addTab
877      * @see #removeTabAt
878      */

879     public void remove(int index) {
880         removeTabAt(index);
881     }
882
883     /**
884      * Removes all the tabs and their corresponding components
885      * from the <code>tabbedpane</code>.
886      *
887      * @see #addTab
888      * @see #removeTabAt
889      */

890     public void removeAll() {
891         setSelectedIndexImpl(-1);
892
893         int tabCount = getTabCount();
894         // We invoke removeTabAt for each tab, otherwise we may end up
895
// removing Components added by the UI.
896
while (tabCount-- > 0) {
897             removeTabAt(tabCount);
898         }
899     }
900
901     /**
902      * Returns the number of tabs in this <code>tabbedpane</code>.
903      *
904      * @return an integer specifying the number of tabbed pages
905      */

906     public int getTabCount() {
907         return pages.size();
908     }
909
910     /**
911      * Returns the number of tab runs currently used to display
912      * the tabs.
913      * @return an integer giving the number of rows if the
914      * <code>tabPlacement</code>
915      * is <code>TOP</code> or <code>BOTTOM</code>
916      * and the number of columns if
917      * <code>tabPlacement</code>
918      * is <code>LEFT</code> or <code>RIGHT</code>,
919      * or 0 if there is no UI set on this <code>tabbedpane</code>
920      */

921     public int getTabRunCount() {
922         if (ui != null) {
923             return ((TabbedPaneUI)ui).getTabRunCount(this);
924         }
925         return 0;
926     }
927
928
929 // Getters for the Pages
930

931     /**
932      * Returns the tab title at <code>index</code>.
933      *
934      * @param index the index of the item being queried
935      * @return the title at <code>index</code>
936      * @exception IndexOutOfBoundsException if index is out of range
937      * (index < 0 || index >= tab count)
938      * @see #setTitleAt
939      */

940     public String JavaDoc getTitleAt(int index) {
941         return ((Page)pages.elementAt(index)).title;
942     }
943
944     /**
945      * Returns the tab icon at <code>index</code>.
946      *
947      * @param index the index of the item being queried
948      * @return the icon at <code>index</code>
949      * @exception IndexOutOfBoundsException if index is out of range
950      * (index < 0 || index >= tab count)
951      *
952      * @see #setIconAt
953      */

954     public Icon JavaDoc getIconAt(int index) {
955         return ((Page)pages.elementAt(index)).icon;
956     }
957
958     /**
959      * Returns the tab disabled icon at <code>index</code>.
960      * If the tab disabled icon doesn't exist at <code>index</code>
961      * this will forward the call to the look and feel to construct
962      * an appropriate disabled Icon from the corresponding enabled
963      * Icon. Some look and feels might not render the disabled Icon,
964      * in which case it won't be created.
965      *
966      * @param index the index of the item being queried
967      * @return the icon at <code>index</code>
968      * @exception IndexOutOfBoundsException if index is out of range
969      * (index < 0 || index >= tab count)
970      *
971      * @see #setDisabledIconAt
972      */

973     public Icon JavaDoc getDisabledIconAt(int index) {
974         Page page = ((Page)pages.elementAt(index));
975         if (page.disabledIcon == null) {
976             page.disabledIcon = UIManager.getLookAndFeel().getDisabledIcon(this, page.icon);
977         }
978         return page.disabledIcon;
979     }
980
981     /**
982      * Returns the tab tooltip text at <code>index</code>.
983      *
984      * @param index the index of the item being queried
985      * @return a string containing the tool tip text at <code>index</code>
986      * @exception IndexOutOfBoundsException if index is out of range
987      * (index < 0 || index >= tab count)
988      *
989      * @see #setToolTipTextAt
990      */

991     public String JavaDoc getToolTipTextAt(int index) {
992         return ((Page)pages.elementAt(index)).tip;
993     }
994
995     /**
996      * Returns the tab background color at <code>index</code>.
997      *
998      * @param index the index of the item being queried
999      * @return the <code>Color</code> of the tab background at
1000     * <code>index</code>
1001     * @exception IndexOutOfBoundsException if index is out of range
1002     * (index < 0 || index >= tab count)
1003     *
1004     * @see #setBackgroundAt
1005     */

1006    public Color getBackgroundAt(int index) {
1007        return ((Page)pages.elementAt(index)).getBackground();
1008    }
1009
1010    /**
1011     * Returns the tab foreground color at <code>index</code>.
1012     *
1013