Save This Page
Home » openjdk-7 » javax » swing » [javadoc | source]
    1   /*
    2    * Copyright 1997-2008 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   
   26   package javax.swing;
   27   
   28   import java.awt;
   29   import java.awt.event;
   30   import java.beans.Transient;
   31   import java.util;
   32   import javax.swing.event;
   33   import javax.swing.plaf;
   34   import javax.accessibility;
   35   import sun.swing.SwingUtilities2;
   36   
   37   import java.io.Serializable;
   38   import java.io.ObjectOutputStream;
   39   import java.io.ObjectInputStream;
   40   import java.io.IOException;
   41   
   42   /**
   43    * A component that lets the user switch between a group of components by
   44    * clicking on a tab with a given title and/or icon.
   45    * For examples and information on using tabbed panes see
   46    * <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/tabbedpane.html">How to Use Tabbed Panes</a>,
   47    * a section in <em>The Java Tutorial</em>.
   48    * <p>
   49    * Tabs/components are added to a <code>TabbedPane</code> object by using the
   50    * <code>addTab</code> and <code>insertTab</code> methods.
   51    * A tab is represented by an index corresponding
   52    * to the position it was added in, where the first tab has an index equal to 0
   53    * and the last tab has an index equal to the tab count minus 1.
   54    * <p>
   55    * The <code>TabbedPane</code> uses a <code>SingleSelectionModel</code>
   56    * to represent the set
   57    * of tab indices and the currently selected index.  If the tab count
   58    * is greater than 0, then there will always be a selected index, which
   59    * by default will be initialized to the first tab.  If the tab count is
   60    * 0, then the selected index will be -1.
   61    * <p>
   62    * The tab title can be rendered by a <code>Component</code>.
   63    * For example, the following produce similar results:
   64    * <pre>
   65    * // In this case the look and feel renders the title for the tab.
   66    * tabbedPane.addTab("Tab", myComponent);
   67    * // In this case the custom component is responsible for rendering the
   68    * // title of the tab.
   69    * tabbedPane.addTab(null, myComponent);
   70    * tabbedPane.setTabComponentAt(0, new JLabel("Tab"));
   71    * </pre>
   72    * The latter is typically used when you want a more complex user interaction
   73    * that requires custom components on the tab.  For example, you could
   74    * provide a custom component that animates or one that has widgets for
   75    * closing the tab.
   76    * <p>
   77    * If you specify a component for a tab, the <code>JTabbedPane</code>
   78    * will not render any text or icon you have specified for the tab.
   79    * <p>
   80    * <strong>Note:</strong>
   81    * Do not use <code>setVisible</code> directly on a tab component to make it visible,
   82    * use <code>setSelectedComponent</code> or <code>setSelectedIndex</code> methods instead.
   83    * <p>
   84    * <strong>Warning:</strong> Swing is not thread safe. For more
   85    * information see <a
   86    * href="package-summary.html#threading">Swing's Threading
   87    * Policy</a>.
   88    * <p>
   89    * <strong>Warning:</strong>
   90    * Serialized objects of this class will not be compatible with
   91    * future Swing releases. The current serialization support is
   92    * appropriate for short term storage or RMI between applications running
   93    * the same version of Swing.  As of 1.4, support for long term storage
   94    * of all JavaBeans<sup><font size="-2">TM</font></sup>
   95    * has been added to the <code>java.beans</code> package.
   96    * Please see {@link java.beans.XMLEncoder}.
   97    *
   98    * @beaninfo
   99    *      attribute: isContainer true
  100    *    description: A component which provides a tab folder metaphor for
  101    *                 displaying one component from a set of components.
  102    *
  103    * @author Dave Moore
  104    * @author Philip Milne
  105    * @author Amy Fowler
  106    *
  107    * @see SingleSelectionModel
  108    */
  109   public class JTabbedPane extends JComponent
  110          implements Serializable, Accessible, SwingConstants {
  111   
  112      /**
  113       * The tab layout policy for wrapping tabs in multiple runs when all
  114       * tabs will not fit within a single run.
  115       */
  116       public static final int WRAP_TAB_LAYOUT = 0;
  117   
  118      /**
  119       * Tab layout policy for providing a subset of available tabs when all
  120       * the tabs will not fit within a single run.  If all the tabs do
  121       * not fit within a single run the look and feel will provide a way
  122       * to navigate to hidden tabs.
  123       */
  124       public static final int SCROLL_TAB_LAYOUT = 1;
  125   
  126   
  127       /**
  128        * @see #getUIClassID
  129        * @see #readObject
  130        */
  131       private static final String uiClassID = "TabbedPaneUI";
  132   
  133       /**
  134        * Where the tabs are placed.
  135        * @see #setTabPlacement
  136        */
  137       protected int tabPlacement = TOP;
  138   
  139       private int tabLayoutPolicy;
  140   
  141       /** The default selection model */
  142       protected SingleSelectionModel model;
  143   
  144       private boolean haveRegistered;
  145   
  146       /**
  147        * The <code>changeListener</code> is the listener we add to the
  148        * model.
  149        */
  150       protected ChangeListener changeListener = null;
  151   
  152       private final java.util.List<Page> pages;
  153   
  154       /* The component that is currently visible */
  155       private Component visComp = null;
  156   
  157       /**
  158        * Only one <code>ChangeEvent</code> is needed per <code>TabPane</code>
  159        * instance since the
  160        * event's only (read-only) state is the source property.  The source
  161        * of events generated here is always "this".
  162        */
  163       protected transient ChangeEvent changeEvent = null;
  164   
  165       /**
  166        * Creates an empty <code>TabbedPane</code> with a default
  167        * tab placement of <code>JTabbedPane.TOP</code>.
  168        * @see #addTab
  169        */
  170       public JTabbedPane() {
  171           this(TOP, WRAP_TAB_LAYOUT);
  172       }
  173   
  174       /**
  175        * Creates an empty <code>TabbedPane</code> with the specified tab placement
  176        * of either: <code>JTabbedPane.TOP</code>, <code>JTabbedPane.BOTTOM</code>,
  177        * <code>JTabbedPane.LEFT</code>, or <code>JTabbedPane.RIGHT</code>.
  178        *
  179        * @param tabPlacement the placement for the tabs relative to the content
  180        * @see #addTab
  181        */
  182       public JTabbedPane(int tabPlacement) {
  183           this(tabPlacement, WRAP_TAB_LAYOUT);
  184       }
  185   
  186       /**
  187        * Creates an empty <code>TabbedPane</code> with the specified tab placement
  188        * and tab layout policy.  Tab placement may be either:
  189        * <code>JTabbedPane.TOP</code>, <code>JTabbedPane.BOTTOM</code>,
  190        * <code>JTabbedPane.LEFT</code>, or <code>JTabbedPane.RIGHT</code>.
  191        * Tab layout policy may be either: <code>JTabbedPane.WRAP_TAB_LAYOUT</code>
  192        * or <code>JTabbedPane.SCROLL_TAB_LAYOUT</code>.
  193        *
  194        * @param tabPlacement the placement for the tabs relative to the content
  195        * @param tabLayoutPolicy the policy for laying out tabs when all tabs will not fit on one run
  196        * @exception IllegalArgumentException if tab placement or tab layout policy are not
  197        *            one of the above supported values
  198        * @see #addTab
  199        * @since 1.4
  200        */
  201       public JTabbedPane(int tabPlacement, int tabLayoutPolicy) {
  202           setTabPlacement(tabPlacement);
  203           setTabLayoutPolicy(tabLayoutPolicy);
  204           pages = new ArrayList<Page>(1);
  205           setModel(new DefaultSingleSelectionModel());
  206           updateUI();
  207       }
  208   
  209       /**
  210        * Returns the UI object which implements the L&F for this component.
  211        *
  212        * @return a <code>TabbedPaneUI</code> object
  213        * @see #setUI
  214        */
  215       public TabbedPaneUI getUI() {
  216           return (TabbedPaneUI)ui;
  217       }
  218   
  219       /**
  220        * Sets the UI object which implements the L&F for this component.
  221        *
  222        * @param ui the new UI object
  223        * @see UIDefaults#getUI
  224        * @beaninfo
  225        *        bound: true
  226        *       hidden: true
  227        *    attribute: visualUpdate true
  228        *  description: The UI object that implements the tabbedpane's LookAndFeel
  229        */
  230       public void setUI(TabbedPaneUI ui) {
  231           super.setUI(ui);
  232           // disabled icons are generated by LF so they should be unset here
  233           for (int i = 0; i < getTabCount(); i++) {
  234               Icon icon = pages.get(i).disabledIcon;
  235               if (icon instanceof UIResource) {
  236                   setDisabledIconAt(i, null);
  237               }
  238           }
  239       }
  240   
  241       /**
  242        * Resets the UI property to a value from the current look and feel.
  243        *
  244        * @see JComponent#updateUI
  245        */
  246       public void updateUI() {
  247           setUI((TabbedPaneUI)UIManager.getUI(this));
  248       }
  249   
  250   
  251       /**
  252        * Returns the name of the UI class that implements the
  253        * L&F for this component.
  254        *
  255        * @return the string "TabbedPaneUI"
  256        * @see JComponent#getUIClassID
  257        * @see UIDefaults#getUI
  258        */
  259       public String getUIClassID() {
  260           return uiClassID;
  261       }
  262   
  263   
  264       /**
  265        * We pass <code>ModelChanged</code> events along to the listeners with
  266        * the tabbedpane (instead of the model itself) as the event source.
  267        */
  268       protected class ModelListener implements ChangeListener, Serializable {
  269           public void stateChanged(ChangeEvent e) {
  270               fireStateChanged();
  271           }
  272       }
  273   
  274       /**
  275        * Subclasses that want to handle <code>ChangeEvents</code> differently
  276        * can override this to return a subclass of <code>ModelListener</code> or
  277        * another <code>ChangeListener</code> implementation.
  278        *
  279        * @see #fireStateChanged
  280        */
  281       protected ChangeListener createChangeListener() {
  282           return new ModelListener();
  283       }
  284   
  285       /**
  286        * Adds a <code>ChangeListener</code> to this tabbedpane.
  287        *
  288        * @param l the <code>ChangeListener</code> to add
  289        * @see #fireStateChanged
  290        * @see #removeChangeListener
  291        */
  292       public void addChangeListener(ChangeListener l) {
  293           listenerList.add(ChangeListener.class, l);
  294       }
  295   
  296       /**
  297        * Removes a <code>ChangeListener</code> from this tabbedpane.
  298        *
  299        * @param l the <code>ChangeListener</code> to remove
  300        * @see #fireStateChanged
  301        * @see #addChangeListener
  302        */
  303       public void removeChangeListener(ChangeListener l) {
  304           listenerList.remove(ChangeListener.class, l);
  305       }
  306   
  307      /**
  308        * Returns an array of all the <code>ChangeListener</code>s added
  309        * to this <code>JTabbedPane</code> with <code>addChangeListener</code>.
  310        *
  311        * @return all of the <code>ChangeListener</code>s added or an empty
  312        *         array if no listeners have been added
  313        * @since 1.4
  314        */
  315       public ChangeListener[] getChangeListeners() {
  316           return (ChangeListener[])listenerList.getListeners(
  317                   ChangeListener.class);
  318       }
  319   
  320       /**
  321        * Sends a {@code ChangeEvent}, with this {@code JTabbedPane} as the source,
  322        * to each registered listener. This method is called each time there is
  323        * a change to either the selected index or the selected tab in the
  324        * {@code JTabbedPane}. Usually, the selected index and selected tab change
  325        * together. However, there are some cases, such as tab addition, where the
  326        * selected index changes and the same tab remains selected. There are other
  327        * cases, such as deleting the selected tab, where the index remains the
  328        * same, but a new tab moves to that index. Events are fired for all of
  329        * these cases.
  330        *
  331        * @see #addChangeListener
  332        * @see EventListenerList
  333        */
  334       protected void fireStateChanged() {
  335           /* --- Begin code to deal with visibility --- */
  336   
  337           /* This code deals with changing the visibility of components to
  338            * hide and show the contents for the selected tab. It duplicates
  339            * logic already present in BasicTabbedPaneUI, logic that is
  340            * processed during the layout pass. This code exists to allow
  341            * developers to do things that are quite difficult to accomplish
  342            * with the previous model of waiting for the layout pass to process
  343            * visibility changes; such as requesting focus on the new visible
  344            * component.
  345            *
  346            * For the average code, using the typical JTabbedPane methods,
  347            * all visibility changes will now be processed here. However,
  348            * the code in BasicTabbedPaneUI still exists, for the purposes
  349            * of backward compatibility. Therefore, when making changes to
  350            * this code, ensure that the BasicTabbedPaneUI code is kept in
  351            * synch.
  352            */
  353   
  354           int selIndex = getSelectedIndex();
  355   
  356           /* if the selection is now nothing */
  357           if (selIndex < 0) {
  358               /* if there was a previous visible component */
  359               if (visComp != null && visComp.isVisible()) {
  360                   /* make it invisible */
  361                   visComp.setVisible(false);
  362               }
  363   
  364               /* now there's no visible component */
  365               visComp = null;
  366   
  367           /* else - the selection is now something */
  368           } else {
  369               /* Fetch the component for the new selection */
  370               Component newComp = getComponentAt(selIndex);
  371   
  372               /* if the new component is non-null and different */
  373               if (newComp != null && newComp != visComp) {
  374                   boolean shouldChangeFocus = false;
  375   
  376                   /* Note: the following (clearing of the old visible component)
  377                    * is inside this if-statement for good reason: Tabbed pane
  378                    * should continue to show the previously visible component
  379                    * if there is no component for the chosen tab.
  380                    */
  381   
  382                   /* if there was a previous visible component */
  383                   if (visComp != null) {
  384                       shouldChangeFocus =
  385                           (SwingUtilities.findFocusOwner(visComp) != null);
  386   
  387                       /* if it's still visible */
  388                       if (visComp.isVisible()) {
  389                           /* make it invisible */
  390                           visComp.setVisible(false);
  391                       }
  392                   }
  393   
  394                   if (!newComp.isVisible()) {
  395                       newComp.setVisible(true);
  396                   }
  397   
  398                   if (shouldChangeFocus) {
  399                       SwingUtilities2.tabbedPaneChangeFocusTo(newComp);
  400                   }
  401   
  402                   visComp = newComp;
  403               } /* else - the visible component shouldn't changed */
  404           }
  405   
  406           /* --- End code to deal with visibility --- */
  407   
  408           // Guaranteed to return a non-null array
  409           Object[] listeners = listenerList.getListenerList();
  410           // Process the listeners last to first, notifying
  411           // those that are interested in this event
  412           for (int i = listeners.length-2; i>=0; i-=2) {
  413               if (listeners[i]==ChangeListener.class) {
  414                   // Lazily create the event:
  415                   if (changeEvent == null)
  416                       changeEvent = new ChangeEvent(this);
  417                   ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
  418               }
  419           }
  420       }
  421   
  422       /**
  423        * Returns the model associated with this tabbedpane.
  424        *
  425        * @see #setModel
  426        */
  427       public SingleSelectionModel getModel() {
  428           return model;
  429       }
  430   
  431       /**
  432        * Sets the model to be used with this tabbedpane.
  433        *
  434        * @param model the model to be used
  435        * @see #getModel
  436        * @beaninfo
  437        *       bound: true
  438        * description: The tabbedpane's SingleSelectionModel.
  439        */
  440       public void setModel(SingleSelectionModel model) {
  441           SingleSelectionModel oldModel = getModel();
  442   
  443           if (oldModel != null) {
  444               oldModel.removeChangeListener(changeListener);
  445               changeListener = null;
  446           }
  447   
  448           this.model = model;
  449   
  450           if (model != null) {
  451               changeListener = createChangeListener();
  452               model.addChangeListener(changeListener);
  453           }
  454   
  455           firePropertyChange("model", oldModel, model);
  456           repaint();
  457       }
  458   
  459       /**
  460        * Returns the placement of the tabs for this tabbedpane.
  461        * @see #setTabPlacement
  462        */
  463       public int getTabPlacement() {
  464           return tabPlacement;
  465       }
  466   
  467       /**
  468        * Sets the tab placement for this tabbedpane.
  469        * Possible values are:<ul>
  470        * <li><code>JTabbedPane.TOP</code>
  471        * <li><code>JTabbedPane.BOTTOM</code>
  472        * <li><code>JTabbedPane.LEFT</code>
  473        * <li><code>JTabbedPane.RIGHT</code>
  474        * </ul>
  475        * The default value, if not set, is <code>SwingConstants.TOP</code>.
  476        *
  477        * @param tabPlacement the placement for the tabs relative to the content
  478        * @exception IllegalArgumentException if tab placement value isn't one
  479        *                          of the above valid values
  480        *
  481        * @beaninfo
  482        *    preferred: true
  483        *        bound: true
  484        *    attribute: visualUpdate true
  485        *         enum: TOP JTabbedPane.TOP
  486        *               LEFT JTabbedPane.LEFT
  487        *               BOTTOM JTabbedPane.BOTTOM
  488        *               RIGHT JTabbedPane.RIGHT
  489        *  description: The tabbedpane's tab placement.
  490        *
  491        */
  492       public void setTabPlacement(int tabPlacement) {
  493           if (tabPlacement != TOP && tabPlacement != LEFT &&
  494               tabPlacement != BOTTOM && tabPlacement != RIGHT) {
  495               throw new IllegalArgumentException("illegal tab placement: must be TOP, BOTTOM, LEFT, or RIGHT");
  496           }
  497           if (this.tabPlacement != tabPlacement) {
  498               int oldValue = this.tabPlacement;
  499               this.tabPlacement = tabPlacement;
  500               firePropertyChange("tabPlacement", oldValue, tabPlacement);
  501               revalidate();
  502               repaint();
  503           }
  504       }
  505   
  506       /**
  507        * Returns the policy used by the tabbedpane to layout the tabs when all the
  508        * tabs will not fit within a single run.
  509        * @see #setTabLayoutPolicy
  510        * @since 1.4
  511        */
  512       public int getTabLayoutPolicy() {
  513           return tabLayoutPolicy;
  514       }
  515   
  516      /**
  517        * Sets the policy which the tabbedpane will use in laying out the tabs
  518        * when all the tabs will not fit within a single run.
  519        * Possible values are:
  520        * <ul>
  521        * <li><code>JTabbedPane.WRAP_TAB_LAYOUT</code>
  522        * <li><code>JTabbedPane.SCROLL_TAB_LAYOUT</code>
  523        * </ul>
  524        *
  525        * The default value, if not set by the UI, is <code>JTabbedPane.WRAP_TAB_LAYOUT</code>.
  526        * <p>
  527        * Some look and feels might only support a subset of the possible
  528        * layout policies, in which case the value of this property may be
  529        * ignored.
  530        *
  531        * @param tabLayoutPolicy the policy used to layout the tabs
  532        * @exception IllegalArgumentException if layoutPolicy value isn't one
  533        *                          of the above valid values
  534        * @see #getTabLayoutPolicy
  535        * @since 1.4
  536        *
  537        * @beaninfo
  538        *    preferred: true
  539        *        bound: true
  540        *    attribute: visualUpdate true
  541        *         enum: WRAP_TAB_LAYOUT JTabbedPane.WRAP_TAB_LAYOUT
  542        *               SCROLL_TAB_LAYOUT JTabbedPane.SCROLL_TAB_LAYOUT
  543        *  description: The tabbedpane's policy for laying out the tabs
  544        *
  545        */
  546       public void setTabLayoutPolicy(int tabLayoutPolicy) {
  547           if (tabLayoutPolicy != WRAP_TAB_LAYOUT && tabLayoutPolicy != SCROLL_TAB_LAYOUT) {
  548               throw new IllegalArgumentException("illegal tab layout policy: must be WRAP_TAB_LAYOUT or SCROLL_TAB_LAYOUT");
  549           }
  550           if (this.tabLayoutPolicy != tabLayoutPolicy) {
  551               int oldValue = this.tabLayoutPolicy;
  552               this.tabLayoutPolicy = tabLayoutPolicy;
  553               firePropertyChange("tabLayoutPolicy", oldValue, tabLayoutPolicy);
  554               revalidate();
  555               repaint();
  556           }
  557       }
  558   
  559       /**
  560        * Returns the currently selected index for this tabbedpane.
  561        * Returns -1 if there is no currently selected tab.
  562        *
  563        * @return the index of the selected tab
  564        * @see #setSelectedIndex
  565        */
  566       @Transient
  567       public int getSelectedIndex() {
  568           return model.getSelectedIndex();
  569       }
  570   
  571       /**
  572        * Sets the selected index for this tabbedpane. The index must be
  573        * a valid tab index or -1, which indicates that no tab should be selected
  574        * (can also be used when there are no tabs in the tabbedpane).  If a -1
  575        * value is specified when the tabbedpane contains one or more tabs, then
  576        * the results will be implementation defined.
  577        *
  578        * @param index  the index to be selected
  579        * @exception IndexOutOfBoundsException if index is out of range
  580        *            (index < -1 || index >= tab count)
  581        *
  582        * @see #getSelectedIndex
  583        * @see SingleSelectionModel#setSelectedIndex
  584        * @beaninfo
  585        *   preferred: true
  586        * description: The tabbedpane's selected tab index.
  587        */
  588       public void setSelectedIndex(int index) {
  589           if (index != -1) {
  590               checkIndex(index);
  591           }
  592           setSelectedIndexImpl(index, true);
  593       }
  594   
  595   
  596       private void setSelectedIndexImpl(int index, boolean doAccessibleChanges) {
  597           int oldIndex = model.getSelectedIndex();
  598           Page oldPage = null, newPage = null;
  599           String oldName = null;
  600   
  601           doAccessibleChanges = doAccessibleChanges && (oldIndex != index);
  602   
  603           if (doAccessibleChanges) {
  604               if (accessibleContext != null) {
  605                   oldName = accessibleContext.getAccessibleName();
  606               }
  607   
  608               if (oldIndex >= 0) {
  609                   oldPage = pages.get(oldIndex);
  610               }
  611   
  612               if (index >= 0) {
  613                   newPage = pages.get(index);
  614               }
  615           }
  616   
  617           model.setSelectedIndex(index);
  618   
  619           if (doAccessibleChanges) {
  620               changeAccessibleSelection(oldPage, oldName, newPage);
  621           }
  622       }
  623   
  624       private void changeAccessibleSelection(Page oldPage, String oldName, Page newPage) {
  625           if (accessibleContext == null) {
  626               return;
  627           }
  628   
  629           if (oldPage != null) {
  630               oldPage.firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  631                                          AccessibleState.SELECTED, null);
  632           }
  633   
  634           if (newPage != null) {
  635               newPage.firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  636                                          null, AccessibleState.SELECTED);
  637           }
  638   
  639           accessibleContext.firePropertyChange(
  640               AccessibleContext.ACCESSIBLE_NAME_PROPERTY,
  641               oldName,
  642               accessibleContext.getAccessibleName());
  643       }
  644   
  645       /**
  646        * Returns the currently selected component for this tabbedpane.
  647        * Returns <code>null</code> if there is no currently selected tab.
  648        *
  649        * @return the component corresponding to the selected tab
  650        * @see #setSelectedComponent
  651        */
  652       @Transient
  653       public Component getSelectedComponent() {
  654           int index = getSelectedIndex();
  655           if (index == -1) {
  656               return null;
  657           }
  658           return getComponentAt(index);
  659       }
  660   
  661       /**
  662        * Sets the selected component for this tabbedpane.  This
  663        * will automatically set the <code>selectedIndex</code> to the index
  664        * corresponding to the specified component.
  665        *
  666        * @exception IllegalArgumentException if component not found in tabbed
  667        *          pane
  668        * @see #getSelectedComponent
  669        * @beaninfo
  670        *   preferred: true
  671        * description: The tabbedpane's selected component.
  672        */
  673       public void setSelectedComponent(Component c) {
  674           int index = indexOfComponent(c);
  675           if (index != -1) {
  676               setSelectedIndex(index);
  677           } else {
  678               throw new IllegalArgumentException("component not found in tabbed pane");
  679           }
  680       }
  681   
  682       /**
  683        * Inserts a new tab for the given component, at the given index,
  684        * represented by the given title and/or icon, either of which may
  685        * be {@code null}.
  686        *
  687        * @param title the title to be displayed on the tab
  688        * @param icon the icon to be displayed on the tab
  689        * @param component the component to be displayed when this tab is clicked.
  690        * @param tip the tooltip to be displayed for this tab
  691        * @param index the position to insert this new tab
  692        *       ({@code > 0 and <= getTabCount()})
  693        *
  694        * @throws IndexOutOfBoundsException if the index is out of range
  695        *         ({@code < 0 or > getTabCount()})
  696        *
  697        * @see #addTab
  698        * @see #removeTabAt
  699        */
  700       public void insertTab(String title, Icon icon, Component component, String tip, int index) {
  701           int newIndex = index;
  702   
  703           // If component already exists, remove corresponding
  704           // tab so that new tab gets added correctly
  705           // Note: we are allowing component=null because of compatibility,
  706           // but we really should throw an exception because much of the
  707           // rest of the JTabbedPane implementation isn't designed to deal
  708           // with null components for tabs.
  709           int removeIndex = indexOfComponent(component);
  710           if (component != null && removeIndex != -1) {
  711               removeTabAt(removeIndex);
  712               if (newIndex > removeIndex) {
  713                   newIndex--;
  714               }
  715           }
  716   
  717           int selectedIndex = getSelectedIndex();
  718   
  719           pages.add(
  720               newIndex,
  721               new Page(this, title != null? title : "", icon, null, component, tip));
  722   
  723   
  724           if (component != null) {
  725               addImpl(component, null, -1);
  726               component.setVisible(false);
  727           } else {
  728               firePropertyChange("indexForNullComponent", -1, index);
  729           }
  730   
  731           if (pages.size() == 1) {
  732               setSelectedIndex(0);
  733           }
  734   
  735           if (selectedIndex >= newIndex) {
  736               setSelectedIndexImpl(selectedIndex + 1, false);
  737           }
  738   
  739           if (!haveRegistered && tip != null) {
  740               ToolTipManager.sharedInstance().registerComponent(this);
  741               haveRegistered = true;
  742           }
  743   
  744           if (accessibleContext != null) {
  745               accessibleContext.firePropertyChange(
  746                       AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
  747                       null, component);
  748           }
  749           revalidate();
  750           repaint();
  751       }
  752   
  753       /**
  754        * Adds a <code>component</code> and <code>tip</code>
  755        * represented by a <code>title</code> and/or <code>icon</code>,
  756        * either of which can be <code>null</code>.
  757        * Cover method for <code>insertTab</code>.
  758        *
  759        * @param title the title to be displayed in this tab
  760        * @param icon the icon to be displayed in this tab
  761        * @param component the component to be displayed when this tab is clicked
  762        * @param tip the tooltip to be displayed for this tab
  763        *
  764        * @see #insertTab
  765        * @see #removeTabAt
  766        */
  767       public void addTab(String title, Icon icon, Component component, String tip) {
  768           insertTab(title, icon, component, tip, pages.size());
  769       }
  770   
  771       /**
  772        * Adds a <code>component</code> represented by a <code>title</code>
  773        * and/or <code>icon</code>, either of which can be <code>null</code>.
  774        * Cover method for <code>insertTab</code>.
  775        *
  776        * @param title the title to be displayed in this tab
  777        * @param icon the icon to be displayed in this tab
  778        * @param component the component to be displayed when this tab is clicked
  779        *
  780        * @see #insertTab
  781        * @see #removeTabAt
  782        */
  783       public void addTab(String title, Icon icon, Component component) {
  784           insertTab(title, icon, component, null, pages.size());
  785       }
  786   
  787       /**
  788        * Adds a <code>component</code> represented by a <code>title</code>
  789        * and no icon.
  790        * Cover method for <code>insertTab</code>.
  791        *
  792        * @param title the title to be displayed in this tab
  793        * @param component the component to be displayed when this tab is clicked
  794        *
  795        * @see #insertTab
  796        * @see #removeTabAt
  797        */
  798       public void addTab(String title, Component component) {
  799           insertTab(title, null, component, null, pages.size());
  800       }
  801   
  802       /**
  803        * Adds a <code>component</code> with a tab title defaulting to
  804        * the name of the component which is the result of calling
  805        * <code>component.getName</code>.
  806        * Cover method for <code>insertTab</code>.
  807        *
  808        * @param component the component to be displayed when this tab is clicked
  809        * @return the component
  810        *
  811        * @see #insertTab
  812        * @see #removeTabAt
  813        */
  814       public Component add(Component component) {
  815           if (!(component instanceof UIResource)) {
  816               addTab(component.getName(), component);
  817           } else {
  818               super.add(component);
  819           }
  820           return component;
  821       }
  822   
  823       /**
  824        * Adds a <code>component</code> with the specified tab title.
  825        * Cover method for <code>insertTab</code>.
  826        *
  827        * @param title the title to be displayed in this tab
  828        * @param component the component to be displayed when this tab is clicked
  829        * @return the component
  830        *
  831        * @see #insertTab
  832        * @see #removeTabAt
  833        */
  834       public Component add(String title, Component component) {
  835           if (!(component instanceof UIResource)) {
  836               addTab(title, component);
  837           } else {
  838               super.add(title, component);
  839           }
  840           return component;
  841       }
  842   
  843       /**
  844        * Adds a <code>component</code> at the specified tab index with a tab
  845        * title defaulting to the name of the component.
  846        * Cover method for <code>insertTab</code>.
  847        *
  848        * @param component the component to be displayed when this tab is clicked
  849        * @param index the position to insert this new tab
  850        * @return the component
  851        *
  852        * @see #insertTab
  853        * @see #removeTabAt
  854        */
  855       public Component add(Component component, int index) {
  856           if (!(component instanceof UIResource)) {
  857               // Container.add() interprets -1 as "append", so convert
  858               // the index appropriately to be handled by the vector
  859               insertTab(component.getName(), null, component, null,
  860                         index == -1? getTabCount() : index);
  861           } else {
  862               super.add(component, index);
  863           }
  864           return component;
  865       }
  866   
  867       /**
  868        * Adds a <code>component</code> to the tabbed pane.
  869        * If <code>constraints</code> is a <code>String</code> or an
  870        * <code>Icon</code>, it will be used for the tab title,
  871        * otherwise the component's name will be used as the tab title.
  872        * Cover method for <code>insertTab</code>.
  873        *
  874        * @param component the component to be displayed when this tab is clicked
  875        * @param constraints the object to be displayed in the tab
  876        *
  877        * @see #insertTab
  878        * @see #removeTabAt
  879        */
  880       public void add(Component component, Object constraints) {
  881           if (!(component instanceof UIResource)) {
  882               if (constraints instanceof String) {
  883                   addTab((String)constraints, component);
  884               } else if (constraints instanceof Icon) {
  885                   addTab(null, (Icon)constraints, component);
  886               } else {
  887                   add(component);
  888               }
  889           } else {
  890               super.add(component, constraints);
  891           }
  892       }
  893   
  894       /**
  895        * Adds a <code>component</code> at the specified tab index.
  896        * If <code>constraints</code> is a <code>String</code> or an
  897        * <code>Icon</code>, it will be used for the tab title,
  898        * otherwise the component's name will be used as the tab title.
  899        * Cover method for <code>insertTab</code>.
  900        *
  901        * @param component the component to be displayed when this tab is clicked
  902        * @param constraints the object to be displayed in the tab
  903        * @param index the position to insert this new tab
  904        *
  905        * @see #insertTab
  906        * @see #removeTabAt
  907        */
  908       public void add(Component component, Object constraints, int index) {
  909           if (!(component instanceof UIResource)) {
  910   
  911               Icon icon = constraints instanceof Icon? (Icon)constraints : null;
  912               String title = constraints instanceof String? (String)constraints : null;
  913               // Container.add() interprets -1 as "append", so convert
  914               // the index appropriately to be handled by the vector
  915               insertTab(title, icon, component, null, index == -1? getTabCount() : index);
  916           } else {
  917               super.add(component, constraints, index);
  918           }
  919       }
  920   
  921       /**
  922        * Removes the tab at <code>index</code>.
  923        * After the component associated with <code>index</code> is removed,
  924        * its visibility is reset to true to ensure it will be visible
  925        * if added to other containers.
  926        * @param index the index of the tab to be removed
  927        * @exception IndexOutOfBoundsException if index is out of range
  928        *            (index < 0 || index >= tab count)
  929        *
  930        * @see #addTab
  931        * @see #insertTab
  932        */
  933       public void removeTabAt(int index) {
  934           checkIndex(index);
  935   
  936           Component component = getComponentAt(index);
  937           boolean shouldChangeFocus = false;
  938           int selected = getSelectedIndex();
  939           String oldName = null;
  940   
  941           /* if we're about to remove the visible component */
  942           if (component == visComp) {
  943               shouldChangeFocus = (SwingUtilities.findFocusOwner(visComp) != null);
  944               visComp = null;
  945           }
  946   
  947           if (accessibleContext != null) {
  948               /* if we're removing the selected page */
  949               if (index == selected) {
  950                   /* fire an accessible notification that it's unselected */
  951                   pages.get(index).firePropertyChange(
  952                       AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  953                       AccessibleState.SELECTED, null);
  954   
  955                   oldName = accessibleContext.getAccessibleName();
  956               }
  957   
  958               accessibleContext.firePropertyChange(
  959                       AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
  960                       component, null);
  961           }
  962   
  963           // Force the tabComponent to be cleaned up.
  964           setTabComponentAt(index, null);
  965           pages.remove(index);
  966   
  967           // NOTE 4/15/2002 (joutwate):
  968           // This fix is implemented using client properties since there is
  969           // currently no IndexPropertyChangeEvent.  Once
  970           // IndexPropertyChangeEvents have been added this code should be
  971           // modified to use it.
  972           putClientProperty("__index_to_remove__", Integer.valueOf(index));
  973   
  974           /* if the selected tab is after the removal */
  975           if (selected > index) {
  976               setSelectedIndexImpl(selected - 1, false);
  977   
  978           /* if the selected tab is the last tab */
  979           } else if (selected >= getTabCount()) {
  980               setSelectedIndexImpl(selected - 1, false);
  981               Page newSelected = (selected != 0)
  982                   ? pages.get(selected - 1)
  983                   : null;
  984   
  985               changeAccessibleSelection(null, oldName, newSelected);
  986   
  987           /* selected index hasn't changed, but the associated tab has */
  988           } else if (index == selected) {
  989               fireStateChanged();
  990               changeAccessibleSelection(null, oldName, pages.get(index));
  991           }
  992   
  993           // We can't assume the tab indices correspond to the
  994           // container's children array indices, so make sure we
  995           // remove the correct child!
  996           if (component != null) {
  997               Component components[] = getComponents();
  998               for (int i = components.length; --i >= 0; ) {
  999                   if (components[i] == component) {
 1000                       super.remove(i);
 1001                       component.setVisible(true);
 1002                       break;
 1003                   }
 1004               }
 1005           }
 1006   
 1007           if (shouldChangeFocus) {
 1008               SwingUtilities2.tabbedPaneChangeFocusTo(getSelectedComponent());
 1009           }
 1010   
 1011           revalidate();
 1012           repaint();
 1013       }
 1014   
 1015       /**
 1016        * Removes the specified <code>Component</code> from the
 1017        * <code>JTabbedPane</code>. The method does nothing
 1018        * if the <code>component</code> is null.
 1019        *
 1020        * @param component the component to remove from the tabbedpane
 1021        * @see #addTab
 1022        * @see #removeTabAt
 1023        */
 1024       public void remove(Component component) {
 1025           int index = indexOfComponent(component);
 1026           if (index != -1) {
 1027               removeTabAt(index);
 1028           } else {
 1029               // Container#remove(comp) invokes Container#remove(int)
 1030               // so make sure JTabbedPane#remove(int) isn't called here
 1031               Component children[] = getComponents();
 1032               for (int i=0; i < children.length; i++) {
 1033                   if (component == children[i]) {
 1034                       super.remove(i);
 1035                       break;
 1036                   }
 1037               }
 1038           }
 1039       }
 1040   
 1041       /**
 1042        * Removes the tab and component which corresponds to the specified index.
 1043        *
 1044        * @param index the index of the component to remove from the
 1045        *          <code>tabbedpane</code>
 1046        * @exception IndexOutOfBoundsException if index is out of range
 1047        *            (index < 0 || index >= tab count)
 1048        * @see #addTab
 1049        * @see #removeTabAt
 1050        */
 1051       public void remove(int index) {
 1052           removeTabAt(index);
 1053       }
 1054   
 1055       /**
 1056        * Removes all the tabs and their corresponding components
 1057        * from the <code>tabbedpane</code>.
 1058        *
 1059        * @see #addTab
 1060        * @see #removeTabAt
 1061        */
 1062       public void removeAll() {
 1063           setSelectedIndexImpl(-1, true);
 1064   
 1065           int tabCount = getTabCount();
 1066           // We invoke removeTabAt for each tab, otherwise we may end up
 1067           // removing Components added by the UI.
 1068           while (tabCount-- > 0) {
 1069               removeTabAt(tabCount);
 1070           }
 1071       }
 1072   
 1073       /**
 1074        * Returns the number of tabs in this <code>tabbedpane</code>.
 1075        *
 1076        * @return an integer specifying the number of tabbed pages
 1077        */
 1078       public int getTabCount() {
 1079           return pages.size();
 1080       }
 1081   
 1082       /**
 1083        * Returns the number of tab runs currently used to display
 1084        * the tabs.
 1085        * @return an integer giving the number of rows if the
 1086        *          <code>tabPlacement</code>
 1087        *          is <code>TOP</code> or <code>BOTTOM</code>
 1088        *          and the number of columns if
 1089        *          <code>tabPlacement</code>
 1090        *          is <code>LEFT</code> or <code>RIGHT</code>,
 1091        *          or 0 if there is no UI set on this <code>tabbedpane</code>
 1092        */
 1093       public int getTabRunCount() {
 1094           if (ui != null) {
 1095               return ((TabbedPaneUI)ui).getTabRunCount(this);
 1096           }
 1097           return 0;
 1098       }
 1099   
 1100   
 1101   // Getters for the Pages
 1102   
 1103       /**
 1104        * Returns the tab title at <code>index</code>.
 1105        *
 1106        * @param index  the index of the item being queried
 1107        * @return the title at <code>index</code>
 1108        * @exception IndexOutOfBoundsException if index is out of range
 1109        *            (index < 0 || index >= tab count)
 1110        * @see #setTitleAt
 1111        */
 1112       public String getTitleAt(int index) {
 1113           return pages.get(index).title;
 1114       }
 1115   
 1116       /**
 1117        * Returns the tab icon at <code>index</code>.
 1118        *
 1119        * @param index  the index of the item being queried
 1120        * @return the icon at <code>index</code>
 1121        * @exception IndexOutOfBoundsException if index is out of range
 1122        *            (index < 0 || index >= tab count)
 1123        *
 1124        * @see #setIconAt
 1125        */
 1126       public Icon getIconAt(int index) {
 1127           return pages.get(index).icon;
 1128       }
 1129   
 1130       /**
 1131        * Returns the tab disabled icon at <code>index</code>.
 1132        * If the tab disabled icon doesn't exist at <code>index</code>
 1133        * this will forward the call to the look and feel to construct
 1134        * an appropriate disabled Icon from the corresponding enabled
 1135        * Icon. Some look and feels might not render the disabled Icon,
 1136        * in which case it won't be created.
 1137        *
 1138        * @param index  the index of the item being queried
 1139        * @return the icon at <code>index</code>
 1140        * @exception IndexOutOfBoundsException if index is out of range
 1141        *            (index < 0 || index >= tab count)
 1142        *
 1143        * @see #setDisabledIconAt
 1144        */
 1145       public Icon getDisabledIconAt(int index) {
 1146           Page page = pages.get(index);
 1147           if (page.disabledIcon == null) {
 1148               page.disabledIcon = UIManager.getLookAndFeel().getDisabledIcon(this, page.icon);
 1149           }
 1150           return page.disabledIcon;
 1151       }
 1152   
 1153       /**
 1154        * Returns the tab tooltip text at <code>index</code>.
 1155        *
 1156        * @param index  the index of the item being queried
 1157        * @return a string containing the tool tip text at <code>index</code>
 1158        * @exception IndexOutOfBoundsException if index is out of range
 1159        *            (index < 0 || index >= tab count)
 1160        *
 1161        * @see #setToolTipTextAt
 1162        * @since 1.3
 1163        */
 1164       public String getToolTipTextAt(int index) {
 1165           return pages.get(index).tip;
 1166       }
 1167   
 1168       /**
 1169        * Returns the tab background color at <code>index</code>.
 1170        *
 1171        * @param index  the index of the item being queried
 1172        * @return the <code>Color</code> of the tab background at
 1173        *          <code>index</code>
 1174        * @exception IndexOutOfBoundsException if index is out of range
 1175        *            (index < 0 || index >= tab count)
 1176        *
 1177        * @see #setBackgroundAt
 1178        */
 1179       public Color getBackgroundAt(int index) {
 1180           return pages.get(index).getBackground();
 1181       }
 1182   
 1183       /**
 1184        * Returns the tab foreground color at <code>index</code>.
 1185        *
 1186        * @param index  the index of the item being queried
 1187        * @return the <code>Color</code> of the tab foreground at
 1188        *          <code>index</code>
 1189        * @exception IndexOutOfBoundsException if index is out of range
 1190        *            (index < 0 || index >= tab count)
 1191        *
 1192        * @see #setForegroundAt
 1193        */
 1194       public Color getForegroundAt(int index) {
 1195           return pages.get(index).getForeground();
 1196       }
 1197   
 1198       /**
 1199        * Returns whether or not the tab at <code>index</code> is
 1200        * currently enabled.
 1201        *
 1202        * @param index  the index of the item being queried
 1203        * @return true if the tab at <code>index</code> is enabled;
 1204        *          false otherwise
 1205        * @exception IndexOutOfBoundsException if index is out of range
 1206        *            (index < 0 || index >= tab count)
 1207        *
 1208        * @see #setEnabledAt
 1209        */
 1210       public boolean isEnabledAt(int index) {
 1211           return pages.get(index).isEnabled();
 1212       }
 1213   
 1214       /**
 1215        * Returns the component at <code>index</code>.
 1216        *
 1217        * @param index  the index of the item being queried
 1218        * @return the <code>Component</code> at <code>index</code>
 1219        * @exception IndexOutOfBoundsException if index is out of range
 1220        *            (index < 0 || index >= tab count)
 1221        *
 1222        * @see #setComponentAt
 1223        */
 1224       public Component getComponentAt(int index) {
 1225           return pages.get(index).component;
 1226       }
 1227   
 1228       /**
 1229        * Returns the keyboard mnemonic for accessing the specified tab.
 1230        * The mnemonic is the key which when combined with the look and feel's
 1231        * mouseless modifier (usually Alt) will activate the specified
 1232        * tab.
 1233        *
 1234        * @since 1.4
 1235        * @param tabIndex the index of the tab that the mnemonic refers to
 1236        * @return the key code which represents the mnemonic;
 1237        *         -1 if a mnemonic is not specified for the tab
 1238        * @exception IndexOutOfBoundsException if index is out of range
 1239        *            (<code>tabIndex</code> &lt; 0 ||
 1240        *              <code>tabIndex</code> &gt;= tab count)
 1241        * @see #setDisplayedMnemonicIndexAt(int,int)
 1242        * @see #setMnemonicAt(int,int)
 1243        */
 1244       public int getMnemonicAt(int tabIndex) {
 1245           checkIndex(tabIndex);
 1246   
 1247           Page page = pages.get(tabIndex);
 1248           return page.getMnemonic();
 1249       }
 1250   
 1251       /**
 1252        * Returns the character, as an index, that the look and feel should
 1253        * provide decoration for as representing the mnemonic character.
 1254        *
 1255        * @since 1.4
 1256        * @param tabIndex the index of the tab that the mnemonic refers to
 1257        * @return index representing mnemonic character if one exists;
 1258        *    otherwise returns -1
 1259        * @exception IndexOutOfBoundsException if index is out of range
 1260        *            (<code>tabIndex</code> &lt; 0 ||
 1261        *              <code>tabIndex</code> &gt;= tab count)
 1262        * @see #setDisplayedMnemonicIndexAt(int,int)
 1263        * @see #setMnemonicAt(int,int)
 1264        */
 1265       public int getDisplayedMnemonicIndexAt(int tabIndex) {
 1266           checkIndex(tabIndex);
 1267   
 1268           Page page = pages.get(tabIndex);
 1269           return page.getDisplayedMnemonicIndex();
 1270       }
 1271   
 1272       /**
 1273        * Returns the tab bounds at <code>index</code>.  If the tab at
 1274        * this index is not currently visible in the UI, then returns
 1275        * <code>null</code>.
 1276        * If there is no UI set on this <code>tabbedpane</code>,
 1277        * then returns <code>null</code>.
 1278        *
 1279        * @param index the index to be queried
 1280        * @return a <code>Rectangle</code> containing the tab bounds at
 1281        *          <code>index</code>, or <code>null</code> if tab at
 1282        *          <code>index</code> is not currently visible in the UI,
 1283        *          or if there is no UI set on this <code>tabbedpane</code>
 1284        * @exception IndexOutOfBoundsException if index is out of range
 1285        *            (index &lt; 0 || index &gt;= tab count)
 1286        */
 1287       public Rectangle getBoundsAt(int index) {
 1288           checkIndex(index);
 1289           if (ui != null) {
 1290               return ((TabbedPaneUI)ui).getTabBounds(this, index);
 1291           }
 1292           return null;
 1293       }
 1294   
 1295   
 1296   // Setters for the Pages
 1297   
 1298       /**
 1299        * Sets the title at <code>index</code> to <code>title</code> which
 1300        * can be <code>null</code>.
 1301        * The title is not shown if a tab component for this tab was specified.
 1302        * An internal exception is raised if there is no tab at that index.
 1303        *
 1304        * @param index the tab index where the title should be set
 1305        * @param title the title to be displayed in the tab
 1306        * @exception IndexOutOfBoundsException if index is out of range
 1307        *            (index &lt; 0 || index &gt;= tab count)
 1308        *
 1309        * @see #getTitleAt
 1310        * @see #setTabComponentAt
 1311        * @beaninfo
 1312        *    preferred: true
 1313        *    attribute: visualUpdate true
 1314        *  description: The title at the specified tab index.
 1315        */
 1316       public void setTitleAt(int index, String title) {
 1317           Page page = pages.get(index);
 1318           String oldTitle =page.title;
 1319           page.title = title;
 1320   
 1321           if (oldTitle != title) {
 1322               firePropertyChange("indexForTitle", -1, index);
 1323           }
 1324           page.updateDisplayedMnemonicIndex();
 1325           if ((oldTitle != title) && (accessibleContext != null)) {
 1326               accessibleContext.firePropertyChange(
 1327                       AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
 1328                       oldTitle, title);
 1329           }
 1330           if (title == null || oldTitle == null ||
 1331               !title.equals(oldTitle)) {
 1332               revalidate();
 1333               repaint();
 1334           }
 1335       }
 1336   
 1337       /**
 1338        * Sets the icon at <code>index</code> to <code>icon</code> which can be
 1339        * <code>null</code>. This does not set disabled icon at <code>icon</code>.
 1340        * If the new Icon is different than the current Icon and disabled icon
 1341        * is not explicitly set, the LookAndFeel will be asked to generate a disabled
 1342        * Icon. To explicitly set disabled icon, use <code>setDisableIconAt()</code>.
 1343        * The icon is not shown if a tab component for this tab was specified.
 1344        * An internal exception is raised if there is no tab at that index.
 1345        *
 1346        * @param index the tab index where the icon should be set
 1347        * @param icon the icon to be displayed in the tab
 1348        * @exception IndexOutOfBoundsException if index is out of range
 1349        *            (index < 0 || index >= tab count)
 1350        *
 1351        * @see #setDisabledIconAt
 1352        * @see #getIconAt
 1353        * @see #getDisabledIconAt
 1354        * @see #setTabComponentAt
 1355        * @beaninfo
 1356        *    preferred: true
 1357        *    attribute: visualUpdate true
 1358        *  description: The icon at the specified tab index.
 1359        */
 1360       public void setIconAt(int index, Icon icon) {
 1361           Page page = pages.get(index);
 1362           Icon oldIcon = page.icon;
 1363           if (icon != oldIcon) {
 1364               page.icon = icon;
 1365   
 1366               /* If the default icon has really changed and we had
 1367                * generated the disabled icon for this page, then
 1368                * clear the disabledIcon field of the page.
 1369                */
 1370               if (page.disabledIcon instanceof UIResource) {
 1371                   page.disabledIcon = null;
 1372               }
 1373   
 1374               // Fire the accessibility Visible data change
 1375               if (accessibleContext != null) {
 1376                   accessibleContext.firePropertyChange(
 1377                           AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
 1378                           oldIcon, icon);
 1379               }
 1380               revalidate();
 1381               repaint();
 1382           }
 1383       }
 1384   
 1385       /**
 1386        * Sets the disabled icon at <code>index</code> to <code>icon</code>
 1387        * which can be <code>null</code>.
 1388        * An internal exception is raised if there is no tab at that index.
 1389        *
 1390        * @param index the tab index where the disabled icon should be set
 1391        * @param disabledIcon the icon to be displayed in the tab when disabled
 1392        * @exception IndexOutOfBoundsException if index is out of range
 1393        *            (index &lt; 0 || index &gt;= tab count)
 1394        *
 1395        * @see #getDisabledIconAt
 1396        * @beaninfo
 1397        *    preferred: true
 1398        *    attribute: visualUpdate true
 1399        *  description: The disabled icon at the specified tab index.
 1400        */
 1401       public void setDisabledIconAt(int index, Icon disabledIcon) {
 1402           Icon oldIcon = pages.get(index).disabledIcon;
 1403           pages.get(index).disabledIcon = disabledIcon;
 1404           if (disabledIcon != oldIcon && !isEnabledAt(index)) {
 1405               revalidate();
 1406               repaint();
 1407           }
 1408       }
 1409   
 1410       /**
 1411        * Sets the tooltip text at <code>index</code> to <code>toolTipText</code>
 1412        * which can be <code>null</code>.
 1413        * An internal exception is raised if there is no tab at that index.
 1414        *
 1415        * @param index the tab index where the tooltip text should be set
 1416        * @param toolTipText the tooltip text to be displayed for the tab
 1417        * @exception IndexOutOfBoundsException if index is out of range
 1418        *            (index &lt; 0 || index &gt;= tab count)
 1419        *
 1420        * @see #getToolTipTextAt
 1421        * @beaninfo
 1422        *    preferred: true
 1423        *  description: The tooltip text at the specified tab index.
 1424        * @since 1.3
 1425        */
 1426       public void setToolTipTextAt(int index, String toolTipText) {
 1427           String oldToolTipText = pages.get(index).tip;
 1428           pages.get(index).tip = toolTipText;
 1429   
 1430           if ((oldToolTipText != toolTipText) && (accessibleContext != null)) {
 1431               accessibleContext.firePropertyChange(
 1432                       AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
 1433                       oldToolTipText, toolTipText);
 1434           }
 1435           if (!haveRegistered && toolTipText != null) {
 1436               ToolTipManager.sharedInstance().registerComponent(this);
 1437               haveRegistered = true;
 1438           }
 1439       }
 1440   
 1441       /**
 1442        * Sets the background color at <code>index</code> to
 1443        * <code>background</code>
 1444        * which can be <code>null</code>, in which case the tab's background color
 1445        * will default to the background color of the <code>tabbedpane</code>.
 1446        * An internal exception is raised if there is no tab at that index.
 1447        * @param index the tab index where the background should be set
 1448        * @param background the color to be displayed in the tab's background
 1449        * @exception IndexOutOfBoundsException if index is out of range
 1450        *            (index &lt; 0 || index &gt;= tab count)
 1451        *
 1452        * @see #getBackgroundAt
 1453        * @beaninfo
 1454        *    preferred: true
 1455        *    attribute: visualUpdate true
 1456        *  description: The background color at the specified tab index.
 1457        */
 1458       public void setBackgroundAt(int index, Color background) {
 1459           Color oldBg = pages.get(index).background;
 1460           pages.get(index).setBackground(background);
 1461           if (background == null || oldBg == null ||
 1462               !background.equals(oldBg)) {
 1463               Rectangle tabBounds = getBoundsAt(index);
 1464               if (tabBounds != null) {
 1465                   repaint(tabBounds);
 1466               }
 1467           }
 1468       }
 1469   
 1470       /**
 1471        * Sets the foreground color at <code>index</code> to
 1472        * <code>foreground</code> which can be
 1473        * <code>null</code>, in which case the tab's foreground color
 1474        * will default to the foreground color of this <code>tabbedpane</code>.
 1475        * An internal exception is raised if there is no tab at that index.
 1476        *
 1477        * @param index the tab index where the foreground should be set
 1478        * @param foreground the color to be displayed as the tab's foreground
 1479        * @exception IndexOutOfBoundsException if index is out of range
 1480        *            (index &lt; 0 || index &gt;= tab count)
 1481        *
 1482        * @see #getForegroundAt
 1483        * @beaninfo
 1484        *    preferred: true
 1485        *    attribute: visualUpdate true
 1486        *  description: The foreground color at the specified tab index.
 1487        */
 1488       public void setForegroundAt(int index, Color foreground) {
 1489           Color oldFg = pages.get(index).foreground;
 1490           pages.get(index).setForeground(foreground);
 1491           if (foreground == null || oldFg == null ||
 1492               !foreground.equals(oldFg)) {
 1493               Rectangle tabBounds = getBoundsAt(index);
 1494               if (tabBounds != null) {
 1495                   repaint(tabBounds);
 1496               }
 1497           }
 1498       }
 1499   
 1500       /**
 1501        * Sets whether or not the tab at <code>index</code> is enabled.
 1502        * An internal exception is raised if there is no tab at that index.
 1503        *
 1504        * @param index the tab index which should be enabled/disabled
 1505        * @param enabled whether or not the tab should be enabled
 1506        * @exception IndexOutOfBoundsException if index is out of range
 1507        *            (index &lt; 0 || index &gt;= tab count)
 1508        *
 1509        * @see #isEnabledAt
 1510        */
 1511       public void setEnabledAt(int index, boolean enabled) {
 1512           boolean oldEnabled = pages.get(index).isEnabled();
 1513           pages.get(index).setEnabled(enabled);
 1514           if (enabled != oldEnabled) {
 1515               revalidate();
 1516               repaint();
 1517           }
 1518       }
 1519   
 1520       /**
 1521        * Sets the component at <code>index</code> to <code>component</code>.
 1522        * An internal exception is raised if there is no tab at that index.
 1523        *
 1524        * @param index the tab index where this component is being placed
 1525        * @param component the component for the tab
 1526        * @exception IndexOutOfBoundsException if index is out of range
 1527        *            (index &lt; 0 || index &gt;= tab count)
 1528        *
 1529        * @see #getComponentAt
 1530        * @beaninfo
 1531        *    attribute: visualUpdate true
 1532        *  description: The component at the specified tab index.
 1533        */
 1534       public void setComponentAt(int index, Component component) {
 1535           Page page = pages.get(index);
 1536           if (component != page.component) {
 1537               boolean shouldChangeFocus = false;
 1538   
 1539               if (page.component != null) {
 1540                   shouldChangeFocus =
 1541                       (SwingUtilities.findFocusOwner(page.component) != null);
 1542   
 1543                   // REMIND(aim): this is really silly;
 1544                   // why not if (page.component.getParent() == this) remove(component)
 1545                   synchronized(getTreeLock()) {
 1546                       int count = getComponentCount();
 1547                       Component children[] = getComponents();
 1548                       for (int i = 0; i < count; i++) {
 1549                           if (children[i] == page.component) {
 1550                               super.remove(i);
 1551                           }
 1552                       }
 1553                   }
 1554               }
 1555   
 1556               page.component = component;
 1557               boolean selectedPage = (getSelectedIndex() == index);
 1558   
 1559               if (selectedPage) {
 1560                   this.visComp = component;
 1561               }
 1562   
 1563               if (component != null) {
 1564                   component.setVisible(selectedPage);
 1565                   addImpl(component, null, -1);
 1566   
 1567                   if (shouldChangeFocus) {
 1568                       SwingUtilities2.tabbedPaneChangeFocusTo(component);
 1569                   }
 1570               } else {
 1571                   repaint();
 1572               }
 1573   
 1574               revalidate();
 1575           }
 1576       }
 1577   
 1578       /**
 1579        * Provides a hint to the look and feel as to which character in the
 1580        * text should be decorated to represent the mnemonic. Not all look and
 1581        * feels may support this. A value of -1 indicates either there is
 1582        * no mnemonic for this tab, or you do not wish the mnemonic to be
 1583        * displayed for this tab.
 1584        * <p>
 1585        * The value of this is updated as the properties relating to the
 1586        * mnemonic change (such as the mnemonic itself, the text...).
 1587        * You should only ever have to call this if
 1588        * you do not wish the default character to be underlined. For example, if
 1589        * the text at tab index 3 was 'Apple Price', with a mnemonic of 'p',
 1590        * and you wanted the 'P'
 1591        * to be decorated, as 'Apple <u>P</u>rice', you would have to invoke
 1592        * <code>setDisplayedMnemonicIndex(3, 6)</code> after invoking
 1593        * <code>setMnemonicAt(3, KeyEvent.VK_P)</code>.
 1594        * <p>Note that it is the programmer's responsibility to ensure
 1595        * that each tab has a unique mnemonic or unpredictable results may
 1596        * occur.
 1597        *
 1598        * @since 1.4
 1599        * @param tabIndex the index of the tab that the mnemonic refers to
 1600        * @param mnemonicIndex index into the <code>String</code> to underline
 1601        * @exception IndexOutOfBoundsException if <code>tabIndex</code> is
 1602        *            out of range (<code>tabIndex < 0 || tabIndex >= tab
 1603        *            count</code>)
 1604        * @exception IllegalArgumentException will be thrown if
 1605        *            <code>mnemonicIndex</code> is &gt;= length of the tab
 1606        *            title , or &lt; -1
 1607        * @see #setMnemonicAt(int,int)
 1608        * @see #getDisplayedMnemonicIndexAt(int)
 1609        *
 1610        * @beaninfo
 1611        *        bound: true
 1612        *    attribute: visualUpdate true
 1613        *  description: the index into the String to draw the keyboard character
 1614        *               mnemonic at
 1615        */
 1616       public void setDisplayedMnemonicIndexAt(int tabIndex, int mnemonicIndex) {
 1617           checkIndex(tabIndex);
 1618   
 1619           Page page = pages.get(tabIndex);
 1620   
 1621           page.setDisplayedMnemonicIndex(mnemonicIndex);
 1622       }
 1623   
 1624       /**
 1625        * Sets the keyboard mnemonic for accessing the specified tab.
 1626        * The mnemonic is the key which when combined with the look and feel's
 1627        * mouseless modifier (usually Alt) will activate the specified
 1628        * tab.
 1629        * <p>
 1630        * A mnemonic must correspond to a single key on the keyboard
 1631        * and should be specified using one of the <code>VK_XXX</code>
 1632        * keycodes defined in <code>java.awt.event.KeyEvent</code>.
 1633        * Mnemonics are case-insensitive, therefore a key event
 1634        * with the corresponding keycode would cause the button to be
 1635        * activated whether or not the Shift modifier was pressed.
 1636        * <p>
 1637        * This will update the displayed mnemonic property for the specified
 1638        * tab.
 1639        *
 1640        * @since 1.4
 1641        * @param tabIndex the index of the tab that the mnemonic refers to
 1642        * @param mnemonic the key code which represents the mnemonic
 1643        * @exception IndexOutOfBoundsException if <code>tabIndex</code> is out
 1644        *            of range (<code>tabIndex < 0 || tabIndex >= tab count</code>)
 1645        * @see #getMnemonicAt(int)
 1646        * @see #setDisplayedMnemonicIndexAt(int,int)
 1647        *
 1648        * @beaninfo
 1649        *        bound: true
 1650        *    attribute: visualUpdate true
 1651        *  description: The keyboard mnenmonic, as a KeyEvent VK constant,
 1652        *               for the specified tab
 1653        */
 1654       public void setMnemonicAt(int tabIndex, int mnemonic) {
 1655           checkIndex(tabIndex);
 1656   
 1657           Page page = pages.get(tabIndex);
 1658           page.setMnemonic(mnemonic);
 1659   
 1660           firePropertyChange("mnemonicAt", null, null);
 1661       }
 1662   
 1663   // end of Page setters
 1664   
 1665       /**
 1666        * Returns the first tab index with a given <code>title</code>,  or
 1667        * -1 if no tab has this title.
 1668        *
 1669        * @param title the title for the tab
 1670        * @return the first tab index which matches <code>title</code>, or
 1671        *          -1 if no tab has this title
 1672        */
 1673       public int indexOfTab(String title) {
 1674           for(int i = 0; i < getTabCount(); i++) {
 1675               if (getTitleAt(i).equals(title == null? "" : title)) {
 1676                   return i;
 1677               }
 1678           }
 1679           return -1;
 1680       }
 1681   
 1682       /**
 1683        * Returns the first tab index with a given <code>icon</code>,
 1684        * or -1 if no tab has this icon.
 1685        *
 1686        * @param icon the icon for the tab
 1687        * @return the first tab index which matches <code>icon</code>,
 1688        *          or -1 if no tab has this icon
 1689        */
 1690       public int indexOfTab(Icon icon) {
 1691           for(int i = 0; i < getTabCount(); i++) {
 1692               Icon tabIcon = getIconAt(i);
 1693               if ((tabIcon != null && tabIcon.equals(icon)) ||
 1694                   (tabIcon == null && tabIcon == icon)) {
 1695                   return i;
 1696               }
 1697           }
 1698           return -1;
 1699       }
 1700   
 1701       /**
 1702        * Returns the index of the tab for the specified component.
 1703        * Returns -1 if there is no tab for this component.
 1704        *
 1705        * @param component the component for the tab
 1706        * @return the first tab which matches this component, or -1
 1707        *          if there is no tab for this component
 1708        */
 1709       public int indexOfComponent(Component component) {
 1710           for(int i = 0; i < getTabCount(); i++) {
 1711               Component c = getComponentAt(i);
 1712               if ((c != null && c.equals(component)) ||
 1713                   (c == null && c == component)) {
 1714                   return i;
 1715               }
 1716           }
 1717           return -1;
 1718       }
 1719   
 1720       /**
 1721        * Returns the tab index corresponding to the tab whose bounds
 1722        * intersect the specified location.  Returns -1 if no tab
 1723        * intersects the location.
 1724        *
 1725        * @param x the x location relative to this tabbedpane
 1726        * @param y the y location relative to this tabbedpane
 1727        * @return the tab index which intersects the location, or
 1728        *         -1 if no tab intersects the location
 1729        * @since 1.4
 1730        */
 1731       public int indexAtLocation(int x, int y) {
 1732           if (ui != null) {
 1733               return ((TabbedPaneUI)ui).tabForCoordinate(this, x, y);
 1734           }
 1735           return -1;
 1736       }
 1737   
 1738   
 1739       /**
 1740        * Returns the tooltip text for the component determined by the
 1741        * mouse event location.
 1742        *
 1743        * @param event  the <code>MouseEvent</code> that tells where the
 1744        *          cursor is lingering
 1745        * @return the <code>String</code> containing the tooltip text
 1746        */
 1747       public String getToolTipText(MouseEvent event) {
 1748           if (ui != null) {
 1749               int index = ((TabbedPaneUI)ui).tabForCoordinate(this, event.getX(), event.getY());
 1750   
 1751               if (index != -1) {
 1752                   return pages.get(index).tip;
 1753               }
 1754           }
 1755           return super.getToolTipText(event);
 1756       }
 1757   
 1758       private void checkIndex(int index) {
 1759           if (index < 0 || index >= pages.size()) {
 1760               throw new IndexOutOfBoundsException("Index: "+index+", Tab count: "+pages.size());
 1761           }
 1762       }
 1763   
 1764   
 1765       /**
 1766        * See <code>readObject</code> and <code>writeObject</code> in
 1767        * <code>JComponent</code> for more
 1768        * information about serialization in Swing.
 1769        */
 1770       private void writeObject(ObjectOutputStream s) throws IOException {
 1771           s.defaultWriteObject();
 1772           if (getUIClassID().equals(uiClassID)) {
 1773               byte count = JComponent.getWriteObjCounter(this);
 1774               JComponent.setWriteObjCounter(this, --count);
 1775               if (count == 0 && ui != null) {
 1776                   ui.installUI(this);
 1777               }
 1778           }
 1779       }
 1780   
 1781       /* Called from the <code>JComponent</code>'s
 1782        * <code>EnableSerializationFocusListener</code> to
 1783        * do any Swing-specific pre-serialization configuration.
 1784        */
 1785       void compWriteObjectNotify() {
 1786           super.compWriteObjectNotify();
 1787           // If ToolTipText != null, then the tooltip has already been
 1788           // unregistered by JComponent.compWriteObjectNotify()
 1789           if (getToolTipText() == null && haveRegistered) {
 1790               ToolTipManager.sharedInstance().unregisterComponent(this);
 1791           }
 1792       }
 1793   
 1794       /**
 1795        * See <code>readObject</code> and <code>writeObject</code> in
 1796        * <code>JComponent</code> for more
 1797        * information about serialization in Swing.
 1798        */
 1799       private void readObject(ObjectInputStream s)
 1800           throws IOException, ClassNotFoundException
 1801       {
 1802           s.defaultReadObject();
 1803           if ((ui != null) && (getUIClassID().equals(uiClassID))) {
 1804               ui.installUI(this);
 1805           }
 1806           // If ToolTipText != null, then the tooltip has already been
 1807           // registered by JComponent.readObject()
 1808           if (getToolTipText() == null && haveRegistered) {
 1809               ToolTipManager.sharedInstance().registerComponent(this);
 1810           }
 1811       }
 1812   
 1813   
 1814       /**
 1815        * Returns a string representation of this <code>JTabbedPane</code>.
 1816        * This method
 1817        * is intended to be used only for debugging purposes, and the
 1818        * content and format of the returned string may vary between
 1819        * implementations. The returned string may be empty but may not
 1820        * be <code>null</code>.
 1821        *
 1822        * @return  a string representation of this JTabbedPane.
 1823        */
 1824       protected String paramString() {
 1825           String tabPlacementString;
 1826           if (tabPlacement == TOP) {
 1827               tabPlacementString = "TOP";
 1828           } else if (tabPlacement == BOTTOM) {
 1829               tabPlacementString = "BOTTOM";
 1830           } else if (tabPlacement == LEFT) {
 1831               tabPlacementString = "LEFT";
 1832           } else if (tabPlacement == RIGHT) {
 1833               tabPlacementString = "RIGHT";
 1834           } else tabPlacementString = "";
 1835           String haveRegisteredString = (haveRegistered ?
 1836                                          "true" : "false");
 1837   
 1838           return super.paramString() +
 1839           ",haveRegistered=" + haveRegisteredString +
 1840           ",tabPlacement=" + tabPlacementString;
 1841       }
 1842   
 1843   /////////////////
 1844   // Accessibility support
 1845   ////////////////
 1846   
 1847       /**
 1848        * Gets the AccessibleContext associated with this JTabbedPane.
 1849        * For tabbed panes, the AccessibleContext takes the form of an
 1850        * AccessibleJTabbedPane.
 1851        * A new AccessibleJTabbedPane instance is created if necessary.
 1852        *
 1853        * @return an AccessibleJTabbedPane that serves as the
 1854        *         AccessibleContext of this JTabbedPane
 1855        */
 1856       public AccessibleContext getAccessibleContext() {
 1857           if (accessibleContext == null) {
 1858               accessibleContext = new AccessibleJTabbedPane();
 1859   
 1860               // initialize AccessibleContext for the existing pages
 1861               int count = getTabCount();
 1862               for (int i = 0; i < count; i++) {
 1863                   pages.get(i).initAccessibleContext();
 1864               }
 1865           }
 1866           return accessibleContext;
 1867       }
 1868   
 1869       /**
 1870        * This class implements accessibility support for the
 1871        * <code>JTabbedPane</code> class.  It provides an implementation of the
 1872        * Java Accessibility API appropriate to tabbed pane user-interface
 1873        * elements.
 1874        * <p>
 1875        * <strong>Warning:</strong>
 1876        * Serialized objects of this class will not be compatible with
 1877        * future Swing releases. The current serialization support is
 1878        * appropriate for short term storage or RMI between applications running
 1879        * the same version of Swing.  As of 1.4, support for long term storage
 1880        * of all JavaBeans<sup><font size="-2">TM</font></sup>
 1881        * has been added to the <code>java.beans</code> package.
 1882        * Please see {@link java.beans.XMLEncoder}.
 1883        */
 1884       protected class AccessibleJTabbedPane extends AccessibleJComponent
 1885           implements AccessibleSelection, ChangeListener {
 1886   
 1887           /**
 1888            * Returns the accessible name of this object, or {@code null} if
 1889            * there is no accessible name.
 1890            *
 1891            * @return the accessible name of this object, nor {@code null}.
 1892            * @since 1.6
 1893            */
 1894           public String getAccessibleName() {
 1895               if (accessibleName != null) {
 1896                   return accessibleName;
 1897               }
 1898   
 1899               String cp = (String)getClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY);
 1900   
 1901               if (cp != null) {
 1902                   return cp;
 1903               }
 1904   
 1905               int index = getSelectedIndex();
 1906   
 1907               if (index >= 0) {
 1908                   return pages.get(index).getAccessibleName();
 1909               }
 1910   
 1911               return super.getAccessibleName();
 1912           }
 1913   
 1914           /**
 1915            *  Constructs an AccessibleJTabbedPane
 1916            */
 1917           public AccessibleJTabbedPane() {
 1918               super();
 1919               JTabbedPane.this.model.addChangeListener(this);
 1920           }
 1921   
 1922           public void stateChanged(ChangeEvent e) {
 1923               Object o = e.getSource();
 1924               firePropertyChange(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
 1925                                  null, o);
 1926           }
 1927   
 1928           /**
 1929            * Get the role of this object.
 1930            *
 1931            * @return an instance of AccessibleRole describing the role of
 1932            *          the object
 1933            */
 1934           public AccessibleRole getAccessibleRole() {
 1935               return AccessibleRole.PAGE_TAB_LIST;
 1936           }
 1937   
 1938           /**
 1939            * Returns the number of accessible children in the object.
 1940            *
 1941            * @return the number of accessible children in the object.
 1942            */
 1943           public int getAccessibleChildrenCount() {
 1944               return getTabCount();
 1945           }
 1946   
 1947           /**
 1948            * Return the specified Accessible child of the object.
 1949            *
 1950            * @param i zero-based index of child
 1951            * @return the Accessible child of the object
 1952            * @exception IllegalArgumentException if index is out of bounds
 1953            */
 1954           public Accessible getAccessibleChild(int i) {
 1955               if (i < 0 || i >= getTabCount()) {
 1956                   return null;
 1957               }
 1958               return pages.get(i);
 1959           }
 1960   
 1961           /**
 1962            * Gets the <code>AccessibleSelection</code> associated with
 1963            * this object.  In the implementation of the Java
 1964            * Accessibility API for this class,
 1965            * returns this object, which is responsible for implementing the
 1966            * <code>AccessibleSelection</code> interface on behalf of itself.
 1967            *
 1968            * @return this object
 1969            */
 1970           public AccessibleSelection getAccessibleSelection() {
 1971              return this;
 1972           }
 1973   
 1974           /**
 1975            * Returns the <code>Accessible</code> child contained at
 1976            * the local coordinate <code>Point</code>, if one exists.
 1977            * Otherwise returns the currently selected tab.
 1978            *
 1979            * @return the <code>Accessible</code> at the specified
 1980            *    location, if it exists
 1981            */
 1982           public Accessible getAccessibleAt(Point p) {
 1983               int tab = ((TabbedPaneUI) ui).tabForCoordinate(JTabbedPane.this,
 1984                                                              p.x, p.y);
 1985               if (tab == -1) {
 1986                   tab = getSelectedIndex();
 1987               }
 1988               return getAccessibleChild(tab);
 1989           }
 1990   
 1991           public int getAccessibleSelectionCount() {
 1992               return 1;
 1993           }
 1994   
 1995           public Accessible getAccessibleSelection(int i) {
 1996               int index = getSelectedIndex();
 1997               if (index == -1) {
 1998                   return null;
 1999               }
 2000               return pages.get(index);
 2001           }
 2002   
 2003           public boolean isAccessibleChildSelected(int i) {
 2004               return (i == getSelectedIndex());
 2005           }
 2006   
 2007           public void addAccessibleSelection(int i) {
 2008              setSelectedIndex(i);
 2009           }
 2010   
 2011           public void removeAccessibleSelection(int i) {
 2012              // can't do
 2013           }
 2014   
 2015           public void clearAccessibleSelection() {
 2016              // can't do
 2017           }
 2018   
 2019           public void selectAllAccessibleSelection() {
 2020              // can't do
 2021           }
 2022       }
 2023   
 2024       private class Page extends AccessibleContext
 2025           implements Serializable, Accessible, AccessibleComponent {
 2026           String title;
 2027           Color background;
 2028           Color foreground;
 2029           Icon icon;
 2030           Icon disabledIcon;
 2031           JTabbedPane parent;
 2032           Component component;
 2033           String tip;
 2034           boolean enabled = true;
 2035           boolean needsUIUpdate;
 2036           int mnemonic = -1;
 2037           int mnemonicIndex = -1;
 2038           Component tabComponent;
 2039   
 2040           Page(JTabbedPane parent,
 2041                String title, Icon icon, Icon disabledIcon, Component component, String tip) {
 2042               this.title = title;
 2043               this.icon = icon;
 2044               this.disabledIcon = disabledIcon;
 2045               this.parent = parent;
 2046               this.setAccessibleParent(parent);
 2047               this.component = component;
 2048               this.tip = tip;
 2049   
 2050               initAccessibleContext();
 2051           }
 2052   
 2053           /*
 2054            * initializes the AccessibleContext for the page
 2055            */
 2056           void initAccessibleContext() {
 2057               if (JTabbedPane.this.accessibleContext != null &&
 2058                   component instanceof Accessible) {
 2059                   /*
 2060                    * Do initialization if the AccessibleJTabbedPane
 2061                    * has been instantiated. We do not want to load
 2062                    * Accessibility classes unnecessarily.
 2063                    */
 2064                   AccessibleContext ac;
 2065                   ac = ((Accessible) component).getAccessibleContext();
 2066                   if (ac != null) {
 2067                       ac.setAccessibleParent(this);
 2068                   }
 2069               }
 2070           }
 2071   
 2072           void setMnemonic(int mnemonic) {
 2073               this.mnemonic = mnemonic;
 2074               updateDisplayedMnemonicIndex();
 2075           }
 2076   
 2077           int getMnemonic() {
 2078               return mnemonic;
 2079           }
 2080   
 2081           /*
 2082            * Sets the page displayed mnemonic index
 2083            */
 2084           void setDisplayedMnemonicIndex(int mnemonicIndex) {
 2085               if (this.mnemonicIndex != mnemonicIndex) {
 2086                   if (mnemonicIndex != -1 && (title == null ||
 2087                           mnemonicIndex < 0 ||
 2088                           mnemonicIndex >= title.length())) {
 2089                       throw new IllegalArgumentException(
 2090                                   "Invalid mnemonic index: " + mnemonicIndex);
 2091                   }
 2092                   this.mnemonicIndex = mnemonicIndex;
 2093                   JTabbedPane.this.firePropertyChange("displayedMnemonicIndexAt",
 2094                                                       null, null);
 2095               }
 2096           }
 2097   
 2098           /*
 2099            * Returns the page displayed mnemonic index
 2100            */
 2101           int getDisplayedMnemonicIndex() {
 2102               return this.mnemonicIndex;
 2103           }
 2104   
 2105           void updateDisplayedMnemonicIndex() {
 2106               setDisplayedMnemonicIndex(
 2107                   SwingUtilities.findDisplayedMnemonicIndex(title, mnemonic));
 2108           }
 2109   
 2110           /////////////////
 2111           // Accessibility support
 2112           ////////////////
 2113   
 2114           public AccessibleContext getAccessibleContext() {
 2115               return this;
 2116           }
 2117   
 2118   
 2119           // AccessibleContext methods
 2120   
 2121           public String getAccessibleName() {
 2122               if (accessibleName != null) {
 2123                   return accessibleName;
 2124               } else if (title != null) {
 2125                   return title;
 2126               }
 2127               return null;
 2128           }
 2129   
 2130           public String getAccessibleDescription() {
 2131               if (accessibleDescription != null) {
 2132                   return accessibleDescription;
 2133               } else if (tip != null) {
 2134                   return tip;
 2135               }
 2136               return null;
 2137           }
 2138   
 2139           public AccessibleRole getAccessibleRole() {
 2140               return AccessibleRole.PAGE_TAB;
 2141           }
 2142   
 2143           public AccessibleStateSet getAccessibleStateSet() {
 2144               AccessibleStateSet states;
 2145               states = parent.getAccessibleContext().getAccessibleStateSet();
 2146               states.add(AccessibleState.SELECTABLE);
 2147               int i = parent.indexOfTab(title);
 2148               if (i == parent.getSelectedIndex()) {
 2149                   states.add(AccessibleState.SELECTED);
 2150               }
 2151               return states;
 2152           }
 2153   
 2154           public int getAccessibleIndexInParent() {
 2155               return parent.indexOfTab(title);
 2156           }
 2157   
 2158           public int getAccessibleChildrenCount() {
 2159               if (component instanceof Accessible) {
 2160                   return 1;
 2161               } else {
 2162                   return 0;
 2163               }
 2164           }
 2165   
 2166           public Accessible getAccessibleChild(int i) {
 2167               if (component instanceof Accessible) {
 2168                   return (Accessible) component;
 2169               } else {
 2170                   return null;
 2171               }
 2172           }
 2173   
 2174           public Locale getLocale() {
 2175               return parent.getLocale();
 2176           }
 2177   
 2178           public AccessibleComponent getAccessibleComponent() {
 2179               return this;
 2180           }
 2181   
 2182   
 2183           // AccessibleComponent methods
 2184   
 2185           public Color getBackground() {
 2186               return background != null? background : parent.getBackground();
 2187           }
 2188   
 2189           public void setBackground(Color c) {
 2190               background = c;
 2191           }
 2192   
 2193           public Color getForeground() {
 2194               return foreground != null? foreground : parent.getForeground();
 2195           }
 2196   
 2197           public void setForeground(Color c) {
 2198               foreground = c;
 2199           }
 2200   
 2201           public Cursor getCursor() {
 2202               return parent.getCursor();
 2203           }
 2204   
 2205           public void setCursor(Cursor c) {
 2206               parent.setCursor(c);
 2207           }
 2208   
 2209           public Font getFont() {
 2210               return parent.getFont();
 2211           }
 2212   
 2213           public void setFont(Font f) {
 2214               parent.setFont(f);
 2215           }
 2216   
 2217           public FontMetrics getFontMetrics(Font f) {
 2218               return parent.getFontMetrics(f);
 2219           }
 2220   
 2221           public boolean isEnabled() {
 2222               return enabled;
 2223           }
 2224   
 2225           public void setEnabled(boolean b) {
 2226               enabled = b;
 2227           }
 2228   
 2229           public boolean isVisible() {
 2230               return parent.isVisible();
 2231           }
 2232   
 2233           public void setVisible(boolean b) {
 2234               parent.setVisible(b);
 2235           }
 2236   
 2237           public boolean isShowing() {
 2238               return parent.isShowing();
 2239           }
 2240   
 2241           public boolean contains(Point p) {
 2242               Rectangle r = getBounds();
 2243               return r.contains(p);
 2244           }
 2245   
 2246           public Point getLocationOnScreen() {
 2247                Point parentLocation = parent.getLocationOnScreen();
 2248                Point componentLocation = getLocation();
 2249                componentLocation.translate(parentLocation.x, parentLocation.y);
 2250                return componentLocation;
 2251           }
 2252   
 2253           public Point getLocation() {
 2254                Rectangle r = getBounds();
 2255                return new Point(r.x, r.y);
 2256           }
 2257   
 2258           public void setLocation(Point p) {
 2259               // do nothing
 2260           }
 2261   
 2262           public Rectangle getBounds() {
 2263               return parent.getUI().getTabBounds(parent,
 2264                                                  parent.indexOfTab(title));
 2265           }
 2266   
 2267           public void setBounds(Rectangle r) {
 2268               // do nothing
 2269           }
 2270   
 2271           public Dimension getSize() {
 2272               Rectangle r = getBounds();
 2273               return new Dimension(r.width, r.height);
 2274           }
 2275   
 2276           public void setSize(Dimension d) {
 2277               // do nothing
 2278           }
 2279   
 2280           public Accessible getAccessibleAt(Point p) {
 2281               if (component instanceof Accessible) {
 2282                   return (Accessible) component;
 2283               } else {
 2284                   return null;
 2285               }
 2286           }
 2287   
 2288           public boolean isFocusTraversable() {
 2289               return false;
 2290           }
 2291   
 2292           public void requestFocus() {
 2293               // do nothing
 2294           }
 2295   
 2296           public void addFocusListener(FocusListener l) {
 2297               // do nothing
 2298           }
 2299   
 2300           public void removeFocusListener(FocusListener l) {
 2301               // do nothing
 2302           }
 2303   
 2304           // TIGER - 4732339
 2305           /**
 2306            * Returns an AccessibleIcon
 2307            *
 2308            * @return the enabled icon if one exists and the page
 2309            * is enabled. Otherwise, returns the disabled icon if
 2310            * one exists and the page is disabled.  Otherwise, null
 2311            * is returned.
 2312            */
 2313           public AccessibleIcon [] getAccessibleIcon() {
 2314               AccessibleIcon accessibleIcon = null;
 2315               if (enabled && icon instanceof ImageIcon) {
 2316                   AccessibleContext ac =
 2317                       ((ImageIcon)icon).getAccessibleContext();
 2318                   accessibleIcon = (AccessibleIcon)ac;
 2319               } else if (!enabled && disabledIcon instanceof ImageIcon) {
 2320                   AccessibleContext ac =
 2321                       ((ImageIcon)disabledIcon).getAccessibleContext();
 2322                   accessibleIcon = (AccessibleIcon)ac;
 2323               }
 2324               if (accessibleIcon != null) {
 2325                   AccessibleIcon [] returnIcons = new AccessibleIcon[1];
 2326                   returnIcons[0] = accessibleIcon;
 2327                   return returnIcons;
 2328               } else {
 2329                   return null;
 2330               }
 2331           }
 2332       }
 2333   
 2334       /**
 2335       * Sets the component that is responsible for rendering the
 2336       * title for the specified tab.  A null value means
 2337       * <code>JTabbedPane</code> will render the title and/or icon for
 2338       * the specified tab.  A non-null value means the component will
 2339       * render the title and <code>JTabbedPane</code> will not render
 2340       * the title and/or icon.
 2341       * <p>
 2342       * Note: The component must not be one that the developer has
 2343       *       already added to the tabbed pane.
 2344       *
 2345       * @param index the tab index where the component should be set
 2346       * @param component the component to render the title for the
 2347       *                  specified tab
 2348       * @exception IndexOutOfBoundsException if index is out of range
 2349       *            (index < 0 || index >= tab count)
 2350       * @exception IllegalArgumentException if component has already been
 2351       *            added to this <code>JTabbedPane</code>
 2352       *
 2353       * @see #getTabComponentAt
 2354       * @beaninfo
 2355       *    preferred: true
 2356       *    attribute: visualUpdate true
 2357       *  description: The tab component at the specified tab index.
 2358       * @since 1.6
 2359       */
 2360       public void setTabComponentAt(int index, Component component) {
 2361           if (component != null && indexOfComponent(component) != -1) {
 2362               throw new IllegalArgumentException("Component is already added to this JTabbedPane");
 2363           }
 2364           Component oldValue = getTabComponentAt(index);
 2365           if (component != oldValue) {
 2366               int tabComponentIndex = indexOfTabComponent(component);
 2367               if (tabComponentIndex != -1) {
 2368                   setTabComponentAt(tabComponentIndex, null);
 2369               }
 2370               pages.get(index).tabComponent = component;
 2371               firePropertyChange("indexForTabComponent", -1, index);
 2372           }
 2373       }
 2374   
 2375       /**
 2376        * Returns the tab component at <code>index</code>.
 2377        *
 2378        * @param index  the index of the item being queried
 2379        * @return the tab component at <code>index</code>
 2380        * @exception IndexOutOfBoundsException if index is out of range
 2381        *            (index < 0 || index >= tab count)
 2382        *
 2383        * @see #setTabComponentAt
 2384        * @since 1.6
 2385        */
 2386       public Component getTabComponentAt(int index) {
 2387           return pages.get(index).tabComponent;
 2388       }
 2389   
 2390       /**
 2391        * Returns the index of the tab for the specified tab component.
 2392        * Returns -1 if there is no tab for this tab component.
 2393        *
 2394        * @param tabComponent the tab component for the tab
 2395        * @return the first tab which matches this tab component, or -1
 2396        *          if there is no tab for this tab component
 2397        * @see #setTabComponentAt
 2398        * @see #getTabComponentAt
 2399        * @since 1.6
 2400        */
 2401        public int indexOfTabComponent(Component tabComponent) {
 2402           for(int i = 0; i < getTabCount(); i++) {
 2403               Component c = getTabComponentAt(i);
 2404               if (c == tabComponent) {
 2405                   return i;
 2406               }
 2407           }
 2408           return -1;
 2409       }
 2410   }

Save This Page
Home » openjdk-7 » javax » swing » [javadoc | source]