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

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

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