OSDN Git Service

ee6af857ee33436d0e75908553d1bbb5fc104783
[pf3gnuchains/gcc-fork.git] / libjava / classpath / javax / swing / JTabbedPane.java
1 /* JTabbedPane.java --
2    Copyright (C) 2002, 2004, 2005, 2006,  Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 package javax.swing;
40
41 import java.awt.Color;
42 import java.awt.Component;
43 import java.awt.Point;
44 import java.awt.Rectangle;
45 import java.awt.event.MouseEvent;
46 import java.io.Serializable;
47 import java.util.Locale;
48 import java.util.Vector;
49
50 import javax.accessibility.Accessible;
51 import javax.accessibility.AccessibleContext;
52 import javax.accessibility.AccessibleRole;
53 import javax.accessibility.AccessibleSelection;
54 import javax.accessibility.AccessibleState;
55 import javax.accessibility.AccessibleStateSet;
56 import javax.swing.event.ChangeEvent;
57 import javax.swing.event.ChangeListener;
58 import javax.swing.plaf.TabbedPaneUI;
59 import javax.swing.plaf.UIResource;
60
61 /**
62  * This is a container for components where only one component is displayed at
63  * a given time and the displayed component can be switched by clicking on
64  * tabs.
65  * 
66  * <p>
67  * Tabs can be oriented in several ways. They can be above, below, left and
68  * right of the component. Tabs can either wrap around (by creating multiple
69  * rows of tabs) or they can be scrolled (where only a subset of the  tabs
70  * can be seen at once). More tabs can be added by calling the
71  * add/addTab/insertTab methods.
72  * </p>
73  */
74 public class JTabbedPane extends JComponent implements Serializable,
75                                                        Accessible,
76                                                        SwingConstants
77 {
78   /**
79    * Accessibility support for <code>JTabbedPane</code>.
80    */
81   protected class AccessibleJTabbedPane extends JComponent.AccessibleJComponent
82     implements AccessibleSelection, ChangeListener
83   {
84     /**
85      * The serialization UID.
86      */
87     private static final long serialVersionUID = 7610530885966830483L;
88
89     /**
90      * Creates a new AccessibleJTabbedPane object.
91      */
92     public AccessibleJTabbedPane()
93     {
94       super();
95     }
96
97     /**
98      * Receives notification when the selection state of the
99      * <code>JTabbedPane</code> changes and fires appropriate property change
100      * events to interested listeners.
101      *
102      * @param e the change event describing the change
103      */
104     public void stateChanged(ChangeEvent e)
105     {
106       // I couldn't figure out what else should be done here.
107       Object source = e.getSource();
108       firePropertyChange(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
109                          null, source);
110     }
111
112     /**
113      * Returns the accessible role of the <code>JTabbedPane</code>, which is
114      * {@link AccessibleRole#PAGE_TAB_LIST}.
115      *
116      * @return the accessible role of the <code>JTabbedPane</code>
117      */
118     public AccessibleRole getAccessibleRole()
119     {
120       return AccessibleRole.PAGE_TAB_LIST;
121     }
122
123     /**
124      * Returns the number of accessible child components of the
125      * <code>JTabbedPane</code>.
126      *
127      * @return the number of accessible child components of the
128      *         <code>JTabbedPane</code>
129      */
130     public int getAccessibleChildrenCount()
131     {
132       return getTabCount();
133     }
134
135     /**
136      * Returns the accessible child component at the specified index.
137      *
138      * @param i the index of the child component to fetch
139      *
140      * @return the accessible child component at the specified index
141      */
142     public Accessible getAccessibleChild(int i)
143     {
144       // Testing shows that the reference implementation returns instances
145       // of page here.
146       Accessible child = null;
147       if (i >= 0 && i < tabs.size())
148         child = (Page) tabs.get(i);
149       return child;
150     }
151
152     /**
153      * Returns the current selection state of the <code>JTabbedPane</code>
154      * as AccessibleSelection object.
155      *
156      * @return the current selection state of the <code>JTabbedPane</code>
157      */
158     public AccessibleSelection getAccessibleSelection()
159     {
160       return this;
161     }
162
163     /**
164      * Returns the accessible child component at the specified coordinates.
165      * If there is no child component at this location, then return the
166      * currently selected tab.
167      *
168      * @param p the coordinates at which to look up the child component
169      *
170      * @return the accessible child component at the specified coordinates or
171      *         the currently selected tab if there is no child component at
172      *         this location
173      */
174     public Accessible getAccessibleAt(Point p)
175     {
176       int tabIndex = indexAtLocation(p.x, p.y);
177       if (tabIndex >= 0)
178         return getAccessibleChild(tabIndex);
179       else
180         return getAccessibleSelection(0);
181     }
182
183     /**
184      * Returns the number of selected child components of the
185      * <code>JTabbedPane</code>. The reference implementation appears
186      * to return <code>1</code> always and we do the same. 
187      *
188      * @return <code>1</code>
189      */
190     public int getAccessibleSelectionCount()
191     {
192       return 1;
193     }
194
195     /**
196      * Returns the selected tab, or <code>null</code> if there is no 
197      * selection.
198      *
199      * @param i  the selection index (ignored here).
200      *
201      * @return The selected tab, or <code>null</code>.
202      */
203     public Accessible getAccessibleSelection(int i)
204     {
205       Accessible result = null;
206       int selected = getSelectedIndex();
207       if (selected >= 0)
208         result = (Page) tabs.get(selected);
209       return result;
210     }
211
212     /**
213      * Returns <code>true</code> if the specified child is selected,
214      * and <code>false</code> otherwise.
215      *
216      * @param i the child index.
217      *
218      * @return A boolean.
219      */
220     public boolean isAccessibleChildSelected(int i)
221     {
222       return i == getSelectedIndex();
223     }
224
225     /**
226      * Selects the specified tab.
227      *
228      * @param i  the index of the item to select.
229      */
230     public void addAccessibleSelection(int i)
231     {
232       setSelectedIndex(i);
233     }
234
235     /**
236      * Does nothing - it makes no sense to remove a selection for a
237      * tabbed pane, since one tab must always be selected.
238      *
239      * @param i  the item index.
240      * 
241      * @see #addAccessibleSelection(int)
242      */
243     public void removeAccessibleSelection(int i)
244     {
245       // do nothing
246     }
247
248     /**
249      * Does nothing - it makes no sense to clear the selection for
250      * a tabbed pane, since one tab must always be selected.
251      * 
252      * @see #addAccessibleSelection(int)
253      */
254     public void clearAccessibleSelection()
255     {
256       // do nothing
257     }
258
259     /**
260      * Does nothing - it makes no sense to select all for a tabbed
261      * pane, since only one tab can be selected at a time.
262      * 
263      * @see #addAccessibleSelection(int)
264      */
265     public void selectAllAccessibleSelection()
266     {
267       // do nothing
268     }
269   }
270
271   /**
272    * A helper class that listens for changes to the model.
273    */
274   protected class ModelListener implements ChangeListener, Serializable
275   {
276     private static final long serialVersionUID = 497359819958114132L;
277
278     /**
279      * Creates a new ModelListener object.
280      */
281     protected ModelListener()
282     {
283       // Nothing to do here.
284     }
285
286     /**
287      * This method is called whenever the model  is changed.
288      *
289      * @param e The ChangeEvent that is passed from the model.
290      */
291     public void stateChanged(ChangeEvent e)
292     {
293       // Propagate to our listeners.
294       fireStateChanged();
295     }
296   }
297
298   /**
299    * A private class that holds all the information  for each tab.
300    */
301   private class Page
302     extends AccessibleContext
303     implements Accessible
304   {
305     /** The tooltip string. */
306     private String tip;
307
308     /** The component associated with the tab. */
309     private Component component;
310
311     /** The active icon associated with the tab. */
312     private transient Icon icon;
313
314     /** The disabled icon associated with the tab. */
315     private transient Icon disabledIcon;
316
317     /** The tab's enabled status. */
318     private transient boolean enabled = true;
319
320     /** The string painted on the tab. */
321     private transient String title;
322
323     /** The background color of the tab. */
324     private transient Color bg;
325
326     /** The foreground color of the tab. */
327     private transient Color fg;
328
329     /** The mnemonic associated with the tab. */
330     private transient int mnemonicKey;
331
332     /** The index of the underlined character in the string. */
333     private transient int underlinedChar = -1;
334
335     /**
336      * Creates a new data storage for the tab.
337      *
338      * @param title The string displayed on the tab.
339      * @param icon The active icon displayed on the tab.
340      * @param component The component associated with the tab.
341      * @param tip The tooltip associated with the tab.
342      */
343     protected Page(String title, Icon icon, Component component, String tip)
344     {
345       this.title = title;
346       this.icon = icon;
347       this.component = component;
348       this.tip = tip;
349     }
350
351     /**
352      * This method returns the component associated with the tab.
353      *
354      * @return The component associated with the tab.
355      */
356     public Component getComponent()
357     {
358       return component;
359     }
360
361     /**
362      * This method sets the component associated with the tab.
363      *
364      * @param c The component associated with the tab.
365      */
366     public void setComponent(Component c)
367     {
368       int i = indexOfComponent(component);
369       insertTab(title, icon, c, tip, i);
370       component = c;
371       removeTabAt(i);
372     }
373
374     /**
375      * This method returns the tooltip string.
376      *
377      * @return The tooltip string.
378      */
379     public String getTip()
380     {
381       return tip;
382     }
383
384     /**
385      * This method sets the tooltip string.
386      *
387      * @param tip The tooltip string.
388      */
389     public void setTip(String tip)
390     {
391       this.tip = tip;
392     }
393
394     /**
395      * This method returns the background color.
396      *
397      * @return The background color.
398      */
399     public Color getBackground()
400     {
401       Color background;
402       if (bg == null)
403         background = JTabbedPane.this.getBackground();
404       else
405         background = bg;
406       return background;
407     }
408
409     /**
410      * This method sets the background color.
411      *
412      * @param background The background color.
413      */
414     public void setBackground(Color background)
415     {
416       bg = background;
417     }
418
419     /**
420      * This method returns the foreground color.
421      *
422      * @return The foreground color.
423      */
424     public Color getForeground()
425     {
426       Color foreground;
427       if (fg == null)
428         foreground = JTabbedPane.this.getForeground();
429       else
430         foreground = fg;
431       return foreground;
432     }
433
434     /**
435      * This method sets the foreground color.
436      *
437      * @param foreground The foreground color.
438      */
439     public void setForeground(Color foreground)
440     {
441       fg = foreground;
442     }
443
444     /**
445      * This method returns the title associated with the tab.
446      *
447      * @return The title of the tab.
448      */
449     public String getTitle()
450     {
451       return title;
452     }
453
454     private static final long serialVersionUID = 1614381073220130939L;
455
456     /**
457      * This method sets the title of the tab.
458      *
459      * @param text The title of the tab.
460      */
461     public void setTitle(String text)
462     {
463       title = text;
464       if (title != null && title.length() <= underlinedChar)
465         setDisplayedMnemonicIndex(title.length() - 1);
466     }
467
468     /**
469      * This method returns the active icon.
470      *
471      * @return The active icon.
472      */
473     public Icon getIcon()
474     {
475       return icon;
476     }
477
478     /**
479      * This method sets the active icon.
480      *
481      * @param icon The active icon.
482      */
483     public void setIcon(Icon icon)
484     {
485       this.icon = icon;
486     }
487
488     /**
489      * This method returns the disabled icon.
490      *
491      * @return The disabled icon.
492      */
493     public Icon getDisabledIcon()
494     {
495       if (disabledIcon == null && icon instanceof ImageIcon)
496         setDisabledIcon(icon);
497       return disabledIcon;
498     }
499
500     /**
501      * This method sets the disabled icon.
502      *
503      * @param disabledIcon The disabled icon.
504      */
505     public void setDisabledIcon(Icon disabledIcon)
506     {
507       this.disabledIcon = disabledIcon;
508     }
509
510     /**
511      * This method returns whether the tab is enabled.
512      *
513      * @return Whether the tab is enabled.
514      */
515     public boolean isEnabled()
516     {
517       return enabled;
518     }
519
520     /**
521      * This method sets whether the tab is enabled.
522      *
523      * @param enabled Whether this tab is enabled.
524      */
525     public void setEnabled(boolean enabled)
526     {
527       this.enabled = enabled;
528     }
529
530     /**
531      * This method returns the mnemonic.
532      *
533      * @return The mnemonic.
534      */
535     public int getMnemonic()
536     {
537       return mnemonicKey;
538     }
539
540     /**
541      * This method sets the mnemonic. If the title is set, it will update the
542      * mnemonicIndex.
543      *
544      * @param key The mnemonic.
545      */
546     public void setMnemonic(int key)
547     {
548       setMnemonic((char) key);
549     }
550
551     /**
552      * This method sets the mnemonic. If the title is set, it will update the
553      * mnemonicIndex.
554      *
555      * @param aChar The mnemonic.
556      */
557     public void setMnemonic(char aChar)
558     {
559       mnemonicKey = aChar;
560       if (title != null)
561         setDisplayedMnemonicIndex(title.indexOf(mnemonicKey));
562     }
563
564     /**
565      * This method returns the mnemonicIndex.
566      *
567      * @return The mnemonicIndex.
568      */
569     public int getDisplayedMnemonicIndex()
570     {
571       return underlinedChar;
572     }
573
574     /**
575      * This method sets the mnemonicIndex.
576      *
577      * @param index The mnemonicIndex.
578      *
579      * @throws IllegalArgumentException If index less than -1 || index greater
580      *         or equal to title.length.
581      */
582     public void setDisplayedMnemonicIndex(int index)
583       throws IllegalArgumentException
584     {
585       if (index < -1 || title != null && index >= title.length())
586         throw new IllegalArgumentException();
587
588       if (title == null || mnemonicKey == 0 || (index > -1 && title.charAt(index) != mnemonicKey))
589         index = -1;
590
591       underlinedChar = index;
592     }
593
594     /**
595      * Returns the accessible context, which is this object itself.
596      *
597      * @return the accessible context, which is this object itself
598      */
599     public AccessibleContext getAccessibleContext()
600     {
601       return this;
602     }
603
604     /**
605      * Returns the accessible name for this tab.
606      * 
607      * @return The accessible name.
608      */
609     public String getAccessibleName()
610     {
611       if (accessibleName != null)
612         return accessibleName;
613       else
614         return title;
615     }
616     
617     /**
618      * Returns the accessible role of this tab, which is always
619      * {@link AccessibleRole#PAGE_TAB}.
620      *
621      * @return the accessible role of this tab
622      */
623     public AccessibleRole getAccessibleRole()
624     {
625       return AccessibleRole.PAGE_TAB;
626     }
627
628     /**
629      * Returns the accessible state set of this object.
630      *
631      * @return the accessible state set of this object
632      */
633     public AccessibleStateSet getAccessibleStateSet()
634     {
635       AccessibleContext parentCtx = JTabbedPane.this.getAccessibleContext(); 
636       AccessibleStateSet state = parentCtx.getAccessibleStateSet();
637       state.add(AccessibleState.SELECTABLE);
638       if (component == getSelectedComponent())
639         state.add(AccessibleState.SELECTED);
640       return state;
641     }
642
643     /**
644      * Returns the index of this tab inside its parent.
645      *
646      * @return the index of this tab inside its parent
647      */
648     public int getAccessibleIndexInParent()
649     {
650       // TODO: Not sure if the title is unambiguous, but I can't figure
651       // another way of doing this.
652       return indexOfTab(title);
653     }
654
655     /**
656      * Returns the number of accessible children, which is always one (the
657      * component of this tab).
658      *
659      * @return the number of accessible children
660      */
661     public int getAccessibleChildrenCount()
662     {
663       return 1;
664     }
665
666     /**
667      * Returns the accessible child of this tab, which is the component
668      * displayed by the tab.
669      *
670      * @return the accessible child of this tab
671      */
672     public Accessible getAccessibleChild(int i)
673     {
674       // A quick test shows that this method always returns the component
675       // displayed by the tab, regardless of the index.
676       return (Accessible) component;
677     }
678
679     /**
680      * Returns the locale of this accessible object.
681      *
682      * @return the locale of this accessible object
683      */
684     public Locale getLocale()
685     {
686       // TODO: Is this ok?
687       return Locale.getDefault();
688     }
689   }
690
691   private static final long serialVersionUID = 1614381073220130939L;
692
693   /** The changeEvent used to fire changes to listeners. */
694   protected ChangeEvent changeEvent;
695
696   /** The listener that listens to the model. */
697   protected ChangeListener changeListener;
698
699   /** The model that describes this JTabbedPane. */
700   protected SingleSelectionModel model;
701
702   /** Indicates that the TabbedPane is in scrolling mode. */
703   public static final int SCROLL_TAB_LAYOUT = 1;
704
705   /** Indicates that the TabbedPane is in wrap mode. */
706   public static final int WRAP_TAB_LAYOUT = 0;
707
708   /** The current tabPlacement of the TabbedPane. */
709   protected int tabPlacement = SwingConstants.TOP;
710
711   /** The current tabLayoutPolicy of the TabbedPane. */
712   private transient int layoutPolicy;
713
714   /** The list of tabs associated with the TabbedPane. */
715   transient Vector tabs = new Vector();
716
717   /**
718    * Creates a new JTabbedPane object with tabs on top and using wrap tab
719    * layout.
720    */
721   public JTabbedPane()
722   {
723     this(SwingConstants.TOP, WRAP_TAB_LAYOUT);
724   }
725
726   /**
727    * Creates a new JTabbedPane object using wrap tab layout  and the given
728    * <code>tabPlacement</code>, where <code>tabPlacement</code> can be one
729    * of the following values: {@link #TOP}, {@link #BOTTOM}, {@link #LEFT} or
730    * {@link #RIGHT}.
731    *
732    * @param tabPlacement where the tabs will be placed
733    */
734   public JTabbedPane(int tabPlacement)
735   {
736     this(tabPlacement, WRAP_TAB_LAYOUT);
737   }
738
739   /**
740    * Creates a new JTabbedPane object with the given <code>tabPlacement</code>
741    * and <code>tabLayoutPolicy</code>. The <code>tabPlacement</code> can be one
742    * of the following values: {@link #TOP}, {@link #BOTTOM}, {@link #LEFT} or
743    * {@link #RIGHT}. The <code>tabLayoutPolicy</code> can be either
744    * {@link #SCROLL_TAB_LAYOUT} or {@link #WRAP_TAB_LAYOUT}.
745    *
746    * @param tabPlacement where the tabs will be placed
747    * @param tabLayoutPolicy the way tabs will be placed
748    *
749    * @throws IllegalArgumentException If tabLayoutPolicy or tabPlacement are
750    *         not valid.
751    */
752   public JTabbedPane(int tabPlacement, int tabLayoutPolicy)
753   {
754     if (tabPlacement != TOP && tabPlacement != BOTTOM && tabPlacement != RIGHT
755         && tabPlacement != LEFT)
756       throw new IllegalArgumentException("tabPlacement is not valid.");
757     if (tabLayoutPolicy != SCROLL_TAB_LAYOUT
758         && tabLayoutPolicy != WRAP_TAB_LAYOUT)
759       throw new IllegalArgumentException("tabLayoutPolicy is not valid.");
760     this.tabPlacement = tabPlacement;
761     layoutPolicy = tabLayoutPolicy;
762     
763     changeEvent = new ChangeEvent(this);
764     changeListener = createChangeListener();
765
766     model = new DefaultSingleSelectionModel();
767     model.addChangeListener(changeListener);
768
769     updateUI();
770   }
771
772   /**
773    * This method returns the UI used to display the JTabbedPane.
774    *
775    * @return The UI used to display the JTabbedPane.
776    */
777   public TabbedPaneUI getUI()
778   {
779     return (TabbedPaneUI) ui;
780   }
781
782   /**
783    * This method sets the UI used to display the JTabbedPane.
784    *
785    * @param ui The UI used to display the JTabbedPane.
786    */
787   public void setUI(TabbedPaneUI ui)
788   {
789     super.setUI(ui);
790   }
791
792   /**
793    * This method restores the UI to the defaults given by the UIManager.
794    */
795   public void updateUI()
796   {
797     setUI((TabbedPaneUI) UIManager.getUI(this));
798   }
799
800   /**
801    * This method returns a string identifier that  is used to determine which
802    * UI will be used with  the JTabbedPane.
803    *
804    * @return A string identifier for the UI.
805    */
806   public String getUIClassID()
807   {
808     return "TabbedPaneUI";
809   }
810
811   /**
812    * This method creates a ChangeListener that is used to  listen to the model
813    * for events.
814    *
815    * @return A ChangeListener to listen to the model.
816    */
817   protected ChangeListener createChangeListener()
818   {
819     return new ModelListener();
820   }
821
822   /**
823    * This method adds a ChangeListener to the JTabbedPane.
824    *
825    * @param l The ChangeListener to add.
826    */
827   public void addChangeListener(ChangeListener l)
828   {
829     listenerList.add(ChangeListener.class, l);
830   }
831
832   /**
833    * This method removes a ChangeListener to the JTabbedPane.
834    *
835    * @param l The ChangeListener to remove.
836    */
837   public void removeChangeListener(ChangeListener l)
838   {
839     listenerList.remove(ChangeListener.class, l);
840   }
841
842   /**
843    * This method fires a ChangeEvent to all the JTabbedPane's ChangeListeners.
844    */
845   protected void fireStateChanged()
846   {
847     Object[] changeListeners = listenerList.getListenerList();
848     if (changeEvent == null)
849       changeEvent = new ChangeEvent(this);
850     for (int i = changeListeners.length - 2; i >= 0; i -= 2)
851       {
852         if (changeListeners[i] == ChangeListener.class)
853           ((ChangeListener) changeListeners[i + 1]).stateChanged(changeEvent);
854       }
855   }
856
857   /**
858    * This method returns all ChangeListeners registered with the JTabbedPane.
859    *
860    * @return The ChangeListeners registered with the JTabbedPane.
861    */
862   public ChangeListener[] getChangeListeners()
863   {
864     return (ChangeListener[]) super.getListeners(ChangeListener.class);
865   }
866
867   /**
868    * This method returns the model used with the JTabbedPane.
869    *
870    * @return The JTabbedPane's model.
871    */
872   public SingleSelectionModel getModel()
873   {
874     return model;
875   }
876
877   /**
878    * This method changes the model property of the JTabbedPane.
879    *
880    * @param model The new model to use with the JTabbedPane.
881    */
882   public void setModel(SingleSelectionModel model)
883   {
884     if (model != this.model)
885       {
886         SingleSelectionModel oldModel = this.model;
887         this.model.removeChangeListener(changeListener);
888         this.model = model;
889         this.model.addChangeListener(changeListener);
890         firePropertyChange("model", oldModel, this.model);
891       }
892   }
893
894   /**
895    * This method returns the tabPlacement.
896    *
897    * @return The tabPlacement used with the JTabbedPane.
898    */
899   public int getTabPlacement()
900   {
901     return tabPlacement;
902   }
903
904   /**
905    * This method changes the tabPlacement property of the JTabbedPane.
906    *
907    * @param tabPlacement The tabPlacement to use.
908    *
909    * @throws IllegalArgumentException If tabPlacement is not one of TOP,
910    *         BOTTOM, LEFT, or RIGHT.
911    */
912   public void setTabPlacement(int tabPlacement)
913   {
914     if (tabPlacement != TOP && tabPlacement != BOTTOM && tabPlacement != RIGHT
915         && tabPlacement != LEFT)
916       throw new IllegalArgumentException("tabPlacement is not valid.");
917     if (tabPlacement != this.tabPlacement)
918       {
919         int oldPlacement = this.tabPlacement;
920         this.tabPlacement = tabPlacement;
921         firePropertyChange("tabPlacement", oldPlacement, this.tabPlacement);
922       }
923   }
924
925   /**
926    * This method returns the tabLayoutPolicy.
927    *
928    * @return The tabLayoutPolicy.
929    */
930   public int getTabLayoutPolicy()
931   {
932     return layoutPolicy;
933   }
934
935   /**
936    * This method changes the tabLayoutPolicy property of the JTabbedPane.
937    *
938    * @param tabLayoutPolicy The tabLayoutPolicy to use.
939    *
940    * @throws IllegalArgumentException If tabLayoutPolicy is not one of
941    *         SCROLL_TAB_LAYOUT or WRAP_TAB_LAYOUT.
942    */
943   public void setTabLayoutPolicy(int tabLayoutPolicy)
944   {
945     if (tabLayoutPolicy != SCROLL_TAB_LAYOUT
946         && tabLayoutPolicy != WRAP_TAB_LAYOUT)
947       throw new IllegalArgumentException("tabLayoutPolicy is not valid.");
948     if (tabLayoutPolicy != layoutPolicy)
949       {
950         int oldPolicy = layoutPolicy;
951         layoutPolicy = tabLayoutPolicy;
952         firePropertyChange("tabLayoutPolicy", oldPolicy, layoutPolicy);
953       }
954   }
955
956   /**
957    * This method returns the index of the tab that is currently selected.
958    *
959    * @return The index of the selected tab.
960    */
961   public int getSelectedIndex()
962   {
963     return model.getSelectedIndex();
964   }
965
966   /**
967    * This method checks the index.
968    *
969    * @param index The index to check.
970    * @param start DOCUMENT ME!
971    * @param end DOCUMENT ME!
972    *
973    * @throws IndexOutOfBoundsException DOCUMENT ME!
974    */
975   private void checkIndex(int index, int start, int end)
976   {
977     if (index < start || index >= end)
978       throw new IndexOutOfBoundsException("Index < " + start + " || Index >= "
979                                           + end);
980   }
981
982   /**
983    * This method sets the selected index. This method will hide the old
984    * component and show the new component.
985    *
986    * @param index The index to set it at.
987    */
988   public void setSelectedIndex(int index)
989   {
990     checkIndex(index, -1, tabs.size());
991     if (index != getSelectedIndex())
992       {
993         model.setSelectedIndex(index);
994       }
995   }
996
997   /**
998    * This method returns the component at the selected index.
999    *
1000    * @return The component at the selected index.
1001    */
1002   public Component getSelectedComponent()
1003   {
1004     int selectedIndex = getSelectedIndex();
1005     Component selected = null;
1006     if (selectedIndex >= 0)
1007       selected = getComponentAt(selectedIndex);
1008     return selected;
1009   }
1010
1011   /**
1012    * This method sets the component at the selected index.
1013    *
1014    * @param c The component associated with the selected index.
1015    */
1016   public void setSelectedComponent(Component c)
1017   {
1018     if (c.getParent() == this)
1019       setSelectedIndex(indexOfComponent(c));
1020     else
1021       setComponentAt(getSelectedIndex(), c);
1022   }
1023
1024   /**
1025    * This method inserts tabs into JTabbedPane. This includes adding the
1026    * component to the JTabbedPane and hiding it.
1027    *
1028    * @param title the title of the tab; may be <code>null</code>
1029    * @param icon the tab's icon; may be <code>null</code>
1030    * @param component the component associated with the tab
1031    * @param tip the tooltip for the tab
1032    * @param index the index to insert the tab at
1033    */
1034   public void insertTab(String title, Icon icon, Component component,
1035                         String tip, int index)
1036   {
1037     if (title == null)
1038       title = "";
1039     Page p = new Page(title, icon, component, tip);
1040     tabs.insertElementAt(p, index);
1041
1042     // Hide the component so we don't see it. Do it before we parent it
1043     // so we don't trigger a repaint.
1044     if (component != null)
1045       {
1046         component.hide();
1047         super.add(component);
1048       }
1049
1050     if (getSelectedIndex() == -1)
1051       setSelectedIndex(0);
1052
1053     revalidate();
1054     repaint();
1055   }
1056
1057   /**
1058    * This method adds a tab to the JTabbedPane.
1059    *
1060    * @param title the title of the tab; may be <code>null</code>
1061    * @param icon the icon for the tab; may be <code>null</code>
1062    * @param component the associated component
1063    * @param tip the associated tooltip
1064    */
1065   public void addTab(String title, Icon icon, Component component, String tip)
1066   {
1067     insertTab(title, icon, component, tip, tabs.size());
1068   }
1069
1070   /**
1071    * This method adds a tab to the JTabbedPane.
1072    *
1073    * @param title the title of the tab; may be <code>null</code>
1074    * @param icon the icon for the tab; may be <code>null</code>
1075    * @param component the associated component
1076    */
1077   public void addTab(String title, Icon icon, Component component)
1078   {
1079     insertTab(title, icon, component, null, tabs.size());
1080   }
1081
1082   /**
1083    * This method adds a tab to the JTabbedPane.
1084    *
1085    * @param title the title of the tab; may be <code>null</code>
1086    * @param component the associated component
1087    */
1088   public void addTab(String title, Component component)
1089   {
1090     insertTab(title, null, component, null, tabs.size());
1091   }
1092
1093   /**
1094    * This method adds a tab to the JTabbedPane. The title of the tab is the
1095    * Component's name. If the Component is an instance of UIResource, it
1096    * doesn't add the tab and instead add the component directly to the
1097    * JTabbedPane.
1098    *
1099    * @param component The associated component.
1100    *
1101    * @return The Component that was added.
1102    */
1103   public Component add(Component component)
1104   {
1105     if (component instanceof UIResource)
1106       super.add(component);
1107     else
1108       insertTab(component.getName(), null, component, null, tabs.size());
1109     
1110     return component;
1111   }
1112
1113   /**
1114    * This method adds a tab to the JTabbedPane. If the Component is an
1115    * instance of UIResource, it doesn't add the tab and instead add the
1116    * component directly to the JTabbedPane.
1117    *
1118    * @param title the title of the tab; may be <code>null</code>
1119    * @param component the associated component
1120    *
1121    * @return The Component that was added.
1122    */
1123   public Component add(String title, Component component)
1124   {
1125     if (component instanceof UIResource)
1126       super.add(component);
1127     else
1128       insertTab(title, null, component, null, tabs.size());
1129     return component;
1130   }
1131
1132   /**
1133    * This method adds a tab to the JTabbedPane. If the Component is an
1134    * instance of UIResource, it doesn't add the tab and instead add the
1135    * component directly to the JTabbedPane.
1136    *
1137    * @param component The associated component.
1138    * @param index The index to insert the tab at.
1139    *
1140    * @return The Component that was added.
1141    */
1142   public Component add(Component component, int index)
1143   {
1144     if (component instanceof UIResource)
1145       super.add(component);
1146     else
1147       insertTab(component.getName(), null, component, null, index);
1148     return component;
1149   }
1150
1151   /**
1152    * This method adds a tab to the JTabbedPane. If the Component is an
1153    * instance of UIResource, it doesn't add the tab and instead add the
1154    * component directly to the JTabbedPane. If the constraints object is an
1155    * icon, it will be used as the tab's icon. If the constraints object is a
1156    * string, we will use it as the title.
1157    *
1158    * @param component The associated component.
1159    * @param constraints The constraints object.
1160    */
1161   public void add(Component component, Object constraints)
1162   {
1163     add(component, constraints, tabs.size());
1164   }
1165
1166   /**
1167    * This method adds a tab to the JTabbedPane. If the Component is an
1168    * instance of UIResource, it doesn't add the tab and instead add the
1169    * component directly to the JTabbedPane. If the constraints object is an
1170    * icon, it will be used as the tab's icon. If the constraints object is a
1171    * string, we will use it as the title.
1172    *
1173    * @param component The associated component.
1174    * @param constraints The constraints object.
1175    * @param index The index to insert the tab at.
1176    */
1177   public void add(Component component, Object constraints, int index)
1178   {
1179     if (component instanceof UIResource)
1180       super.add(component);
1181     else
1182       {
1183         if (constraints instanceof String)
1184           insertTab((String) constraints, null, component, null, index);
1185         else
1186           insertTab(component.getName(),
1187                     (constraints instanceof Icon) ? (Icon) constraints : null,
1188                     component, null, index);
1189       }
1190   }
1191
1192   /**
1193    * Removes the tab at index. After the component associated with 
1194    * index is removed, its visibility is reset to true to ensure it 
1195    * will be visible if added to other containers.
1196    *
1197    * @param index The index of the tab to remove.
1198    */
1199   public void removeTabAt(int index)
1200   {
1201     checkIndex(index, 0, tabs.size());
1202
1203     // We need to adjust the selection if we remove a tab that comes
1204     // before the selected tab or if the selected tab is removed.
1205     // This decrements the selected index by 1 if any of this is the case.
1206     // Note that this covers all cases:
1207     // - When the selected tab comes after the removed tab, this simply
1208     //   adjusts the selection so that after the removal the selected tab
1209     //   is still the same.
1210     // - When we remove the currently selected tab, then the tab before the
1211     //   selected tab gets selected.
1212     // - When the last tab is removed, then we have an index==0, which gets
1213     //   decremented to -1, which means no selection, which is 100% perfect.
1214     int selectedIndex = getSelectedIndex();
1215     if (selectedIndex >= index)
1216       setSelectedIndex(selectedIndex - 1);
1217
1218     Component comp = getComponentAt(index);
1219
1220     // Remove the tab object.
1221     tabs.remove(index);
1222
1223     // Remove the component. I think we cannot assume that the tab order
1224     // is equal to the component order, so we iterate over the children
1225     // here to find the and remove the correct component.
1226     if (comp != null)
1227       {
1228         Component[] children = getComponents();
1229         for (int i = children.length - 1; i >= 0; --i)
1230           {
1231             if (children[i] == comp)
1232               {
1233                 super.remove(i);
1234                 comp.setVisible(true);
1235                 break;
1236               }
1237           }
1238       }
1239     revalidate();
1240     repaint();
1241   }
1242
1243   /**
1244    * Removes the specified Component from the JTabbedPane.
1245    *
1246    * @param component The Component to remove.
1247    */
1248   public void remove(Component component)
1249   {
1250     super.remove(component);
1251   }
1252
1253   /**
1254    * Removes the tab and component which corresponds to the specified index.
1255    *
1256    * @param index The index of the tab to remove.
1257    */
1258   public void remove(int index)
1259   {
1260     super.remove(index);
1261     removeTabAt(index);
1262   }
1263
1264   /**
1265    * This method removes all tabs and associated components from the
1266    * JTabbedPane.
1267    */
1268   public void removeAll()
1269   {
1270     setSelectedIndex(-1);
1271     for (int i = getTabCount() - 1; i >= 0; i--)
1272       removeTabAt(i);
1273   }
1274
1275   /**
1276    * This method returns how many tabs are in the JTabbedPane.
1277    *
1278    * @return The number of tabs in the JTabbedPane.
1279    */
1280   public int getTabCount()
1281   {
1282     return tabs.size();
1283   }
1284
1285   /**
1286    * This method returns the number of runs used  to paint the JTabbedPane.
1287    *
1288    * @return The number of runs.
1289    */
1290   public int getTabRunCount()
1291   {
1292     return ((TabbedPaneUI) ui).getTabRunCount(this);
1293   }
1294
1295   /**
1296    * This method returns the tab title given the index.
1297    *
1298    * @param index The index of the tab.
1299    *
1300    * @return The title for the tab.
1301    */
1302   public String getTitleAt(int index)
1303   {
1304     checkIndex(index, 0, tabs.size());
1305     return ((Page) tabs.elementAt(index)).getTitle();
1306   }
1307
1308   /**
1309    * This method returns the active icon given the index.
1310    *
1311    * @param index The index of the tab.
1312    *
1313    * @return The active icon for the tab.
1314    */
1315   public Icon getIconAt(int index)
1316   {
1317     checkIndex(index, 0, tabs.size());
1318     return ((Page) tabs.elementAt(index)).getIcon();
1319   }
1320
1321   /**
1322    * This method returns the disabled icon given the index.
1323    *
1324    * @param index The index of the tab.
1325    *
1326    * @return The disabled icon for the tab.
1327    */
1328   public Icon getDisabledIconAt(int index)
1329   {
1330     checkIndex(index, 0, tabs.size());
1331     return ((Page) tabs.elementAt(index)).getDisabledIcon();
1332   }
1333
1334   /**
1335    * This method returns the tooltip string for the tab.
1336    *
1337    * @param index The index of the tab.
1338    *
1339    * @return The tooltip string for the tab.
1340    */
1341   public String getToolTipTextAt(int index)
1342   {
1343     checkIndex(index, 0, tabs.size());
1344     return ((Page) tabs.elementAt(index)).getTip();
1345   }
1346
1347   /**
1348    * This method returns the foreground color for the tab.
1349    *
1350    * @param index The index of the tab.
1351    *
1352    * @return The foreground color for the tab.
1353    */
1354   public Color getForegroundAt(int index)
1355   {
1356     checkIndex(index, 0, tabs.size());
1357     return ((Page) tabs.elementAt(index)).getForeground();
1358   }
1359
1360   /**
1361    * This method returns the background color for the tab.
1362    *
1363    * @param index The index of the tab.
1364    *
1365    * @return The background color for the tab.
1366    */
1367   public Color getBackgroundAt(int index)
1368   {
1369     checkIndex(index, 0, tabs.size());
1370     return ((Page) tabs.elementAt(index)).getBackground();
1371   }
1372
1373   /**
1374    * This method returns the component associated with the tab.
1375    *
1376    * @param index The index of the tab.
1377    *
1378    * @return The component associated with the tab.
1379    */
1380   public Component getComponentAt(int index)
1381   {
1382     checkIndex(index, 0, tabs.size());
1383     return ((Page) tabs.elementAt(index)).getComponent();
1384   }
1385
1386   /**
1387    * This method returns whether this tab is enabled. Disabled tabs cannot be
1388    * selected.
1389    *
1390    * @param index The index of the tab.
1391    *
1392    * @return Whether the tab is enabled.
1393    */
1394   public boolean isEnabledAt(int index)
1395   {
1396     checkIndex(index, 0, tabs.size());
1397     return ((Page) tabs.elementAt(index)).isEnabled();
1398   }
1399
1400   /**
1401    * This method returns the mnemonic for the tab.
1402    *
1403    * @param tabIndex The index of the tab.
1404    *
1405    * @return The mnemonic for the tab.
1406    */
1407   public int getMnemonicAt(int tabIndex)
1408   {
1409     checkIndex(tabIndex, 0, tabs.size());
1410     return ((Page) tabs.elementAt(tabIndex)).getMnemonic();
1411   }
1412
1413   /**
1414    * This method returns the mnemonic index for the tab.
1415    *
1416    * @param tabIndex The index of the tab.
1417    *
1418    * @return The mnemonic index for the tab.
1419    */
1420   public int getDisplayedMnemonicIndexAt(int tabIndex)
1421   {
1422     checkIndex(tabIndex, 0, tabs.size());
1423     return ((Page) tabs.elementAt(tabIndex)).getDisplayedMnemonicIndex();
1424   }
1425
1426   /**
1427    * This method returns the bounds of the tab given the index.
1428    *
1429    * @param index The index of the tab.
1430    *
1431    * @return A rectangle describing the bounds of the tab.
1432    */
1433   public Rectangle getBoundsAt(int index)
1434   {
1435     checkIndex(index, 0, tabs.size());
1436     return ((TabbedPaneUI) ui).getTabBounds(this, index);
1437   }
1438
1439   /**
1440    * This method sets the title of the tab.
1441    *
1442    * @param index The index of the tab.
1443    * @param title The new title.
1444    */
1445   public void setTitleAt(int index, String title)
1446   {
1447     checkIndex(index, 0, tabs.size());
1448     ((Page) tabs.elementAt(index)).setTitle(title);
1449   }
1450
1451   /**
1452    * This method sets the icon of the tab.
1453    *
1454    * @param index The index of the tab.
1455    * @param icon The new icon.
1456    */
1457   public void setIconAt(int index, Icon icon)
1458   {
1459     checkIndex(index, 0, tabs.size());
1460     ((Page) tabs.elementAt(index)).setIcon(icon);
1461   }
1462
1463   /**
1464    * This method sets the disabled icon of the tab.
1465    *
1466    * @param index The index of the tab.
1467    * @param disabledIcon The new disabled icon.
1468    */
1469   public void setDisabledIconAt(int index, Icon disabledIcon)
1470   {
1471     checkIndex(index, 0, tabs.size());
1472     ((Page) tabs.elementAt(index)).setDisabledIcon(disabledIcon);
1473   }
1474
1475   /**
1476    * This method sets the tooltip text of the tab.
1477    *
1478    * @param index The index of the tab.
1479    * @param toolTipText The tooltip text.
1480    */
1481   public void setToolTipTextAt(int index, String toolTipText)
1482   {
1483     checkIndex(index, 0, tabs.size());
1484     ((Page) tabs.elementAt(index)).setTip(toolTipText);
1485   }
1486
1487   /**
1488    * This method sets the background color of the tab.
1489    *
1490    * @param index The index of the tab.
1491    * @param background The background color of the tab.
1492    */
1493   public void setBackgroundAt(int index, Color background)
1494   {
1495     checkIndex(index, 0, tabs.size());
1496     ((Page) tabs.elementAt(index)).setBackground(background);
1497   }
1498
1499   /**
1500    * This method sets the foreground color of the tab.
1501    *
1502    * @param index The index of the tab.
1503    * @param foreground The foreground color of the tab.
1504    */
1505   public void setForegroundAt(int index, Color foreground)
1506   {
1507     checkIndex(index, 0, tabs.size());
1508     ((Page) tabs.elementAt(index)).setForeground(foreground);
1509   }
1510
1511   /**
1512    * This method sets whether the tab is enabled.
1513    *
1514    * @param index The index of the tab.
1515    * @param enabled Whether the tab is enabled.
1516    */
1517   public void setEnabledAt(int index, boolean enabled)
1518   {
1519     checkIndex(index, 0, tabs.size());
1520     ((Page) tabs.elementAt(index)).setEnabled(enabled);
1521   }
1522
1523   /**
1524    * This method sets the component associated with the tab.
1525    *
1526    * @param index The index of the tab.
1527    * @param component The component associated with the tab.
1528    */
1529   public void setComponentAt(int index, Component component)
1530   {
1531     checkIndex(index, 0, tabs.size());
1532     ((Page) tabs.elementAt(index)).setComponent(component);
1533   }
1534
1535   /**
1536    * This method sets the displayed mnemonic index of the tab.
1537    *
1538    * @param tabIndex The index of the tab.
1539    * @param mnemonicIndex The mnemonic index.
1540    */
1541   public void setDisplayedMnemonicIndexAt(int tabIndex, int mnemonicIndex)
1542   {
1543     checkIndex(tabIndex, 0, tabs.size());
1544     ((Page) tabs.elementAt(tabIndex)).setDisplayedMnemonicIndex(mnemonicIndex);
1545   }
1546
1547   /**
1548    * This method sets the mnemonic for the tab.
1549    *
1550    * @param tabIndex The index of the tab.
1551    * @param mnemonic The mnemonic.
1552    */
1553   public void setMnemonicAt(int tabIndex, int mnemonic)
1554   {
1555     checkIndex(tabIndex, 0, tabs.size());
1556     ((Page) tabs.elementAt(tabIndex)).setMnemonic(mnemonic);
1557   }
1558
1559   /**
1560    * This method finds the index of a tab given the title.
1561    *
1562    * @param title The title that belongs to a tab.
1563    *
1564    * @return The index of the tab that has the title or -1 if not found.
1565    */
1566   public int indexOfTab(String title)
1567   {
1568     int index = -1;
1569     for (int i = 0; i < tabs.size(); i++)
1570       {
1571         if (((Page) tabs.elementAt(i)).getTitle().equals(title))
1572           {
1573             index = i;
1574             break;
1575           }
1576       }
1577     return index;
1578   }
1579
1580   /**
1581    * This method finds the index of a tab given the icon.
1582    *
1583    * @param icon The icon that belongs to a tab.
1584    *
1585    * @return The index of the tab that has the icon or -1 if not found.
1586    */
1587   public int indexOfTab(Icon icon)
1588   {
1589     int index = -1;
1590     for (int i = 0; i < tabs.size(); i++)
1591       {
1592         if (((Page) tabs.elementAt(i)).getIcon() == icon)
1593           {
1594             index = i;
1595             break;
1596           }
1597       }
1598     return index;
1599   }
1600
1601   /**
1602    * This method finds the index of a tab given the component.
1603    *
1604    * @param component A component associated with a tab.
1605    *
1606    * @return The index of the tab that has this component or -1 if not found.
1607    */
1608   public int indexOfComponent(Component component)
1609   {
1610     int index = -1;
1611     for (int i = 0; i < tabs.size(); i++)
1612       {
1613         if (((Page) tabs.elementAt(i)).getComponent() == component)
1614           {
1615             index = i;
1616             break;
1617           }
1618       }
1619     return index;
1620   }
1621
1622   /**
1623    * This method returns a tab index given an (x,y) location. The origin of
1624    * the (x,y) pair will be the JTabbedPane's top left position. The  tab
1625    * returned will be the one that contains the point. This method is
1626    * delegated to the UI.
1627    *
1628    * @param x The x coordinate of the point.
1629    * @param y The y coordinate of the point.
1630    *
1631    * @return The index of the tab that contains the point.
1632    */
1633   public int indexAtLocation(int x, int y)
1634   {
1635     return ((TabbedPaneUI) ui).tabForCoordinate(this, x, y);
1636   }
1637
1638   /**
1639    * This method returns the tooltip text given a mouse event.
1640    *
1641    * @param event The mouse event.
1642    *
1643    * @return The tool tip text that is associated with this mouse event.
1644    */
1645   public String getToolTipText(MouseEvent event)
1646   {
1647     int index = indexAtLocation(event.getX(), event.getY());
1648     return ((Page) tabs.elementAt(index)).getTip();
1649   }
1650
1651   /**
1652    * Returns a string describing the attributes for the 
1653    * <code>JTabbedPane</code> component, for use in debugging.  The return 
1654    * value is guaranteed to be non-<code>null</code>, but the format of the 
1655    * string may vary between implementations.
1656    *
1657    * @return A string describing the attributes of the 
1658    *     <code>JTabbedPane</code>.
1659    */
1660   protected String paramString()
1661   {
1662     StringBuffer sb = new StringBuffer(super.paramString());
1663     sb.append(",tabPlacement=");
1664     if (tabPlacement == TOP)
1665       sb.append("TOP");
1666     if (tabPlacement == BOTTOM)
1667       sb.append("BOTTOM");
1668     if (tabPlacement == LEFT)
1669       sb.append("LEFT");
1670     if (tabPlacement == RIGHT)
1671       sb.append("RIGHT");
1672     return sb.toString();
1673   }
1674
1675   /**
1676    * Returns the object that provides accessibility features for this
1677    * <code>JTabbedPane</code> component.
1678    *
1679    * @return The accessible context (an instance of 
1680    *         {@link AccessibleJTabbedPane}).
1681    */
1682   public AccessibleContext getAccessibleContext()
1683   {
1684     if (accessibleContext == null)
1685       {
1686         AccessibleJTabbedPane ctx = new AccessibleJTabbedPane();
1687         addChangeListener(ctx);
1688         accessibleContext = ctx;
1689       }
1690
1691     return accessibleContext;
1692   }
1693 }