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