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