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 }