Save This Page
Home » openjdk-7 » javax » swing » plaf » basic » [javadoc | source]
    1   /*
    2    * Copyright 1997-2007 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   
   26   package javax.swing.plaf.basic;
   27   
   28   import sun.swing.SwingUtilities2;
   29   
   30   import javax.swing;
   31   import javax.swing.event;
   32   import javax.swing.plaf;
   33   import javax.swing.text.View;
   34   
   35   import java.awt;
   36   import java.awt.event;
   37   import java.beans.PropertyChangeListener;
   38   import java.beans.PropertyChangeEvent;
   39   import java.util.Vector;
   40   import java.util.Hashtable;
   41   
   42   import sun.swing.DefaultLookup;
   43   import sun.swing.UIAction;
   44   
   45   /**
   46    * A Basic L&F implementation of TabbedPaneUI.
   47    *
   48    * @author Amy Fowler
   49    * @author Philip Milne
   50    * @author Steve Wilson
   51    * @author Tom Santos
   52    * @author Dave Moore
   53    */
   54   public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants {
   55   
   56   
   57   // Instance variables initialized at installation
   58   
   59       protected JTabbedPane tabPane;
   60   
   61       protected Color highlight;
   62       protected Color lightHighlight;
   63       protected Color shadow;
   64       protected Color darkShadow;
   65       protected Color focus;
   66       private   Color selectedColor;
   67   
   68       protected int textIconGap;
   69   
   70       protected int tabRunOverlay;
   71   
   72       protected Insets tabInsets;
   73       protected Insets selectedTabPadInsets;
   74       protected Insets tabAreaInsets;
   75       protected Insets contentBorderInsets;
   76       private boolean tabsOverlapBorder;
   77       private boolean tabsOpaque = true;
   78       private boolean contentOpaque = true;
   79   
   80       /**
   81        * As of Java 2 platform v1.3 this previously undocumented field is no
   82        * longer used.
   83        * Key bindings are now defined by the LookAndFeel, please refer to
   84        * the key bindings specification for further details.
   85        *
   86        * @deprecated As of Java 2 platform v1.3.
   87        */
   88       @Deprecated
   89       protected KeyStroke upKey;
   90       /**
   91        * As of Java 2 platform v1.3 this previously undocumented field is no
   92        * longer used.
   93        * Key bindings are now defined by the LookAndFeel, please refer to
   94        * the key bindings specification for further details.
   95        *
   96        * @deprecated As of Java 2 platform v1.3.
   97        */
   98       @Deprecated
   99       protected KeyStroke downKey;
  100       /**
  101        * As of Java 2 platform v1.3 this previously undocumented field is no
  102        * longer used.
  103        * Key bindings are now defined by the LookAndFeel, please refer to
  104        * the key bindings specification for further details.
  105        *
  106        * @deprecated As of Java 2 platform v1.3.
  107        */
  108       @Deprecated
  109       protected KeyStroke leftKey;
  110       /**
  111        * As of Java 2 platform v1.3 this previously undocumented field is no
  112        * longer used.
  113        * Key bindings are now defined by the LookAndFeel, please refer to
  114        * the key bindings specification for further details.
  115        *
  116        * @deprecated As of Java 2 platform v1.3.
  117        */
  118       @Deprecated
  119       protected KeyStroke rightKey;
  120   
  121   
  122   // Transient variables (recalculated each time TabbedPane is layed out)
  123   
  124       protected int tabRuns[] = new int[10];
  125       protected int runCount = 0;
  126       protected int selectedRun = -1;
  127       protected Rectangle rects[] = new Rectangle[0];
  128       protected int maxTabHeight;
  129       protected int maxTabWidth;
  130   
  131   // Listeners
  132   
  133       protected ChangeListener tabChangeListener;
  134       protected PropertyChangeListener propertyChangeListener;
  135       protected MouseListener mouseListener;
  136       protected FocusListener focusListener;
  137   
  138   // Private instance data
  139   
  140       private Insets currentPadInsets = new Insets(0,0,0,0);
  141       private Insets currentTabAreaInsets = new Insets(0,0,0,0);
  142   
  143       private Component visibleComponent;
  144       // PENDING(api): See comment for ContainerHandler
  145       private Vector htmlViews;
  146   
  147       private Hashtable mnemonicToIndexMap;
  148   
  149       /**
  150        * InputMap used for mnemonics. Only non-null if the JTabbedPane has
  151        * mnemonics associated with it. Lazily created in initMnemonics.
  152        */
  153       private InputMap mnemonicInputMap;
  154   
  155       // For use when tabLayoutPolicy = SCROLL_TAB_LAYOUT
  156       private ScrollableTabSupport tabScroller;
  157   
  158       private TabContainer tabContainer;
  159   
  160       /**
  161        * A rectangle used for general layout calculations in order
  162        * to avoid constructing many new Rectangles on the fly.
  163        */
  164       protected transient Rectangle calcRect = new Rectangle(0,0,0,0);
  165   
  166       /**
  167        * Tab that has focus.
  168        */
  169       private int focusIndex;
  170   
  171       /**
  172        * Combined listeners.
  173        */
  174       private Handler handler;
  175   
  176       /**
  177        * Index of the tab the mouse is over.
  178        */
  179       private int rolloverTabIndex;
  180   
  181       /**
  182        * This is set to true when a component is added/removed from the tab
  183        * pane and set to false when layout happens.  If true it indicates that
  184        * tabRuns is not valid and shouldn't be used.
  185        */
  186       private boolean isRunsDirty;
  187   
  188       private boolean calculatedBaseline;
  189       private int baseline;
  190   
  191   // UI creation
  192   
  193       public static ComponentUI createUI(JComponent c) {
  194           return new BasicTabbedPaneUI();
  195       }
  196   
  197       static void loadActionMap(LazyActionMap map) {
  198           map.put(new Actions(Actions.NEXT));
  199           map.put(new Actions(Actions.PREVIOUS));
  200           map.put(new Actions(Actions.RIGHT));
  201           map.put(new Actions(Actions.LEFT));
  202           map.put(new Actions(Actions.UP));
  203           map.put(new Actions(Actions.DOWN));
  204           map.put(new Actions(Actions.PAGE_UP));
  205           map.put(new Actions(Actions.PAGE_DOWN));
  206           map.put(new Actions(Actions.REQUEST_FOCUS));
  207           map.put(new Actions(Actions.REQUEST_FOCUS_FOR_VISIBLE));
  208           map.put(new Actions(Actions.SET_SELECTED));
  209           map.put(new Actions(Actions.SELECT_FOCUSED));
  210           map.put(new Actions(Actions.SCROLL_FORWARD));
  211           map.put(new Actions(Actions.SCROLL_BACKWARD));
  212       }
  213   
  214   // UI Installation/De-installation
  215   
  216       public void installUI(JComponent c) {
  217           this.tabPane = (JTabbedPane)c;
  218   
  219           calculatedBaseline = false;
  220           rolloverTabIndex = -1;
  221           focusIndex = -1;
  222           c.setLayout(createLayoutManager());
  223           installComponents();
  224           installDefaults();
  225           installListeners();
  226           installKeyboardActions();
  227       }
  228   
  229       public void uninstallUI(JComponent c) {
  230           uninstallKeyboardActions();
  231           uninstallListeners();
  232           uninstallDefaults();
  233           uninstallComponents();
  234           c.setLayout(null);
  235   
  236           this.tabPane = null;
  237       }
  238   
  239       /**
  240        * Invoked by <code>installUI</code> to create
  241        * a layout manager object to manage
  242        * the <code>JTabbedPane</code>.
  243        *
  244        * @return a layout manager object
  245        *
  246        * @see TabbedPaneLayout
  247        * @see javax.swing.JTabbedPane#getTabLayoutPolicy
  248        */
  249       protected LayoutManager createLayoutManager() {
  250           if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
  251               return new TabbedPaneScrollLayout();
  252           } else { /* WRAP_TAB_LAYOUT */
  253               return new TabbedPaneLayout();
  254           }
  255       }
  256   
  257       /* In an attempt to preserve backward compatibility for programs
  258        * which have extended BasicTabbedPaneUI to do their own layout, the
  259        * UI uses the installed layoutManager (and not tabLayoutPolicy) to
  260        * determine if scrollTabLayout is enabled.
  261        */
  262       private boolean scrollableTabLayoutEnabled() {
  263           return (tabPane.getLayout() instanceof TabbedPaneScrollLayout);
  264       }
  265   
  266       /**
  267        * Creates and installs any required subcomponents for the JTabbedPane.
  268        * Invoked by installUI.
  269        *
  270        * @since 1.4
  271        */
  272       protected void installComponents() {
  273           if (scrollableTabLayoutEnabled()) {
  274               if (tabScroller == null) {
  275                   tabScroller = new ScrollableTabSupport(tabPane.getTabPlacement());
  276                   tabPane.add(tabScroller.viewport);
  277               }
  278           }
  279           installTabContainer();
  280       }
  281   
  282       private void installTabContainer() {
  283            for (int i = 0; i < tabPane.getTabCount(); i++) {
  284                Component tabComponent = tabPane.getTabComponentAt(i);
  285                if (tabComponent != null) {
  286                    if(tabContainer == null) {
  287                        tabContainer = new TabContainer();
  288                    }
  289                    tabContainer.add(tabComponent);
  290                }
  291            }
  292            if(tabContainer == null) {
  293                return;
  294            }
  295            if (scrollableTabLayoutEnabled()) {
  296                tabScroller.tabPanel.add(tabContainer);
  297            } else {
  298                tabPane.add(tabContainer);
  299            }
  300       }
  301   
  302       /**
  303        * Creates and returns a JButton that will provide the user
  304        * with a way to scroll the tabs in a particular direction. The
  305        * returned JButton must be instance of UIResource.
  306        *
  307        * @param direction One of the SwingConstants constants:
  308        * SOUTH, NORTH, EAST or WEST
  309        * @return Widget for user to
  310        * @see javax.swing.JTabbedPane#setTabPlacement
  311        * @see javax.swing.SwingConstants
  312        * @throws IllegalArgumentException if direction is not one of
  313        *         NORTH, SOUTH, EAST or WEST
  314        * @since 1.5
  315        */
  316       protected JButton createScrollButton(int direction) {
  317           if (direction != SOUTH && direction != NORTH && direction != EAST &&
  318                                     direction != WEST) {
  319               throw new IllegalArgumentException("Direction must be one of: " +
  320                                                  "SOUTH, NORTH, EAST or WEST");
  321           }
  322           return new ScrollableTabButton(direction);
  323       }
  324   
  325       /**
  326        * Removes any installed subcomponents from the JTabbedPane.
  327        * Invoked by uninstallUI.
  328        *
  329        * @since 1.4
  330        */
  331       protected void uninstallComponents() {
  332           uninstallTabContainer();
  333           if (scrollableTabLayoutEnabled()) {
  334               tabPane.remove(tabScroller.viewport);
  335               tabPane.remove(tabScroller.scrollForwardButton);
  336               tabPane.remove(tabScroller.scrollBackwardButton);
  337               tabScroller = null;
  338           }
  339       }
  340   
  341       private void uninstallTabContainer() {
  342            if(tabContainer == null) {
  343                return;
  344            }
  345            // Remove all the tabComponents, making sure not to notify
  346            // the tabbedpane.
  347            tabContainer.notifyTabbedPane = false;
  348            tabContainer.removeAll();
  349            if(scrollableTabLayoutEnabled()) {
  350                tabContainer.remove(tabScroller.croppedEdge);
  351                tabScroller.tabPanel.remove(tabContainer);
  352            } else {
  353              tabPane.remove(tabContainer);
  354            }
  355            tabContainer = null;
  356       }
  357   
  358       protected void installDefaults() {
  359           LookAndFeel.installColorsAndFont(tabPane, "TabbedPane.background",
  360                                       "TabbedPane.foreground", "TabbedPane.font");
  361           highlight = UIManager.getColor("TabbedPane.light");
  362           lightHighlight = UIManager.getColor("TabbedPane.highlight");
  363           shadow = UIManager.getColor("TabbedPane.shadow");
  364           darkShadow = UIManager.getColor("TabbedPane.darkShadow");
  365           focus = UIManager.getColor("TabbedPane.focus");
  366           selectedColor = UIManager.getColor("TabbedPane.selected");
  367   
  368           textIconGap = UIManager.getInt("TabbedPane.textIconGap");
  369           tabInsets = UIManager.getInsets("TabbedPane.tabInsets");
  370           selectedTabPadInsets = UIManager.getInsets("TabbedPane.selectedTabPadInsets");
  371           tabAreaInsets = UIManager.getInsets("TabbedPane.tabAreaInsets");
  372           tabsOverlapBorder = UIManager.getBoolean("TabbedPane.tabsOverlapBorder");
  373           contentBorderInsets = UIManager.getInsets("TabbedPane.contentBorderInsets");
  374           tabRunOverlay = UIManager.getInt("TabbedPane.tabRunOverlay");
  375           tabsOpaque = UIManager.getBoolean("TabbedPane.tabsOpaque");
  376           contentOpaque = UIManager.getBoolean("TabbedPane.contentOpaque");
  377           Object opaque = UIManager.get("TabbedPane.opaque");
  378           if (opaque == null) {
  379               opaque = Boolean.FALSE;
  380           }
  381           LookAndFeel.installProperty(tabPane, "opaque", opaque);
  382       }
  383   
  384       protected void uninstallDefaults() {
  385           highlight = null;
  386           lightHighlight = null;
  387           shadow = null;
  388           darkShadow = null;
  389           focus = null;
  390           tabInsets = null;
  391           selectedTabPadInsets = null;
  392           tabAreaInsets = null;
  393           contentBorderInsets = null;
  394       }
  395   
  396       protected void installListeners() {
  397           if ((propertyChangeListener = createPropertyChangeListener()) != null) {
  398               tabPane.addPropertyChangeListener(propertyChangeListener);
  399           }
  400           if ((tabChangeListener = createChangeListener()) != null) {
  401               tabPane.addChangeListener(tabChangeListener);
  402           }
  403           if ((mouseListener = createMouseListener()) != null) {
  404               tabPane.addMouseListener(mouseListener);
  405           }
  406           tabPane.addMouseMotionListener(getHandler());
  407           if ((focusListener = createFocusListener()) != null) {
  408               tabPane.addFocusListener(focusListener);
  409           }
  410           tabPane.addContainerListener(getHandler());
  411           if (tabPane.getTabCount()>0) {
  412               htmlViews = createHTMLVector();
  413           }
  414       }
  415   
  416       protected void uninstallListeners() {
  417           if (mouseListener != null) {
  418               tabPane.removeMouseListener(mouseListener);
  419               mouseListener = null;
  420           }
  421           tabPane.removeMouseMotionListener(getHandler());
  422           if (focusListener != null) {
  423               tabPane.removeFocusListener(focusListener);
  424               focusListener = null;
  425           }
  426   
  427           tabPane.removeContainerListener(getHandler());
  428           if (htmlViews!=null) {
  429               htmlViews.removeAllElements();
  430               htmlViews = null;
  431           }
  432           if (tabChangeListener != null) {
  433               tabPane.removeChangeListener(tabChangeListener);
  434               tabChangeListener = null;
  435           }
  436           if (propertyChangeListener != null) {
  437               tabPane.removePropertyChangeListener(propertyChangeListener);
  438               propertyChangeListener = null;
  439           }
  440           handler = null;
  441       }
  442   
  443       protected MouseListener createMouseListener() {
  444           return getHandler();
  445       }
  446   
  447       protected FocusListener createFocusListener() {
  448           return getHandler();
  449       }
  450   
  451       protected ChangeListener createChangeListener() {
  452           return getHandler();
  453       }
  454   
  455       protected PropertyChangeListener createPropertyChangeListener() {
  456           return getHandler();
  457       }
  458   
  459       private Handler getHandler() {
  460           if (handler == null) {
  461               handler = new Handler();
  462           }
  463           return handler;
  464       }
  465   
  466       protected void installKeyboardActions() {
  467           InputMap km = getInputMap(JComponent.
  468                                     WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  469   
  470           SwingUtilities.replaceUIInputMap(tabPane, JComponent.
  471                                            WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
  472                                            km);
  473           km = getInputMap(JComponent.WHEN_FOCUSED);
  474           SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, km);
  475   
  476           LazyActionMap.installLazyActionMap(tabPane, BasicTabbedPaneUI.class,
  477                                              "TabbedPane.actionMap");
  478           updateMnemonics();
  479       }
  480   
  481       InputMap getInputMap(int condition) {
  482           if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
  483               return (InputMap)DefaultLookup.get(tabPane, this,
  484                                                  "TabbedPane.ancestorInputMap");
  485           }
  486           else if (condition == JComponent.WHEN_FOCUSED) {
  487               return (InputMap)DefaultLookup.get(tabPane, this,
  488                                                  "TabbedPane.focusInputMap");
  489           }
  490           return null;
  491       }
  492   
  493       protected void uninstallKeyboardActions() {
  494           SwingUtilities.replaceUIActionMap(tabPane, null);
  495           SwingUtilities.replaceUIInputMap(tabPane, JComponent.
  496                                            WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
  497                                            null);
  498           SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED,
  499                                            null);
  500           SwingUtilities.replaceUIInputMap(tabPane,
  501                                            JComponent.WHEN_IN_FOCUSED_WINDOW,
  502                                            null);
  503           mnemonicToIndexMap = null;
  504           mnemonicInputMap = null;
  505       }
  506   
  507       /**
  508        * Reloads the mnemonics. This should be invoked when a memonic changes,
  509        * when the title of a mnemonic changes, or when tabs are added/removed.
  510        */
  511       private void updateMnemonics() {
  512           resetMnemonics();
  513           for (int counter = tabPane.getTabCount() - 1; counter >= 0;
  514                counter--) {
  515               int mnemonic = tabPane.getMnemonicAt(counter);
  516   
  517               if (mnemonic > 0) {
  518                   addMnemonic(counter, mnemonic);
  519               }
  520           }
  521       }
  522   
  523       /**
  524        * Resets the mnemonics bindings to an empty state.
  525        */
  526       private void resetMnemonics() {
  527           if (mnemonicToIndexMap != null) {
  528               mnemonicToIndexMap.clear();
  529               mnemonicInputMap.clear();
  530           }
  531       }
  532   
  533       /**
  534        * Adds the specified mnemonic at the specified index.
  535        */
  536       private void addMnemonic(int index, int mnemonic) {
  537           if (mnemonicToIndexMap == null) {
  538               initMnemonics();
  539           }
  540           mnemonicInputMap.put(KeyStroke.getKeyStroke(mnemonic, Event.ALT_MASK),
  541                                "setSelectedIndex");
  542           mnemonicToIndexMap.put(Integer.valueOf(mnemonic), Integer.valueOf(index));
  543       }
  544   
  545       /**
  546        * Installs the state needed for mnemonics.
  547        */
  548       private void initMnemonics() {
  549           mnemonicToIndexMap = new Hashtable();
  550           mnemonicInputMap = new ComponentInputMapUIResource(tabPane);
  551           mnemonicInputMap.setParent(SwingUtilities.getUIInputMap(tabPane,
  552                                 JComponent.WHEN_IN_FOCUSED_WINDOW));
  553           SwingUtilities.replaceUIInputMap(tabPane,
  554                                 JComponent.WHEN_IN_FOCUSED_WINDOW,
  555                                            mnemonicInputMap);
  556       }
  557   
  558       /**
  559        * Sets the tab the mouse is over by location. This is a cover method
  560        * for <code>setRolloverTab(tabForCoordinate(x, y, false))</code>.
  561        */
  562       private void setRolloverTab(int x, int y) {
  563           // NOTE:
  564           // This calls in with false otherwise it could trigger a validate,
  565           // which should NOT happen if the user is only dragging the
  566           // mouse around.
  567           setRolloverTab(tabForCoordinate(tabPane, x, y, false));
  568       }
  569   
  570       /**
  571        * Sets the tab the mouse is currently over to <code>index</code>.
  572        * <code>index</code> will be -1 if the mouse is no longer over any
  573        * tab. No checking is done to ensure the passed in index identifies a
  574        * valid tab.
  575        *
  576        * @param index Index of the tab the mouse is over.
  577        * @since 1.5
  578        */
  579       protected void setRolloverTab(int index) {
  580           rolloverTabIndex = index;
  581       }
  582   
  583       /**
  584        * Returns the tab the mouse is currently over, or {@code -1} if the mouse is no
  585        * longer over any tab.
  586        *
  587        * @return the tab the mouse is currently over, or {@code -1} if the mouse is no
  588        * longer over any tab
  589        * @since 1.5
  590        */
  591       protected int getRolloverTab() {
  592           return rolloverTabIndex;
  593       }
  594   
  595       public Dimension getMinimumSize(JComponent c) {
  596           // Default to LayoutManager's minimumLayoutSize
  597           return null;
  598       }
  599   
  600       public Dimension getMaximumSize(JComponent c) {
  601           // Default to LayoutManager's maximumLayoutSize
  602           return null;
  603       }
  604   
  605       /**
  606        * Returns the baseline.
  607        *
  608        * @throws NullPointerException {@inheritDoc}
  609        * @throws IllegalArgumentException {@inheritDoc}
  610        * @see javax.swing.JComponent#getBaseline(int, int)
  611        * @since 1.6
  612        */
  613       public int getBaseline(JComponent c, int width, int height) {
  614           super.getBaseline(c, width, height);
  615           int baseline = calculateBaselineIfNecessary();
  616           if (baseline != -1) {
  617               int placement = tabPane.getTabPlacement();
  618               Insets insets = tabPane.getInsets();
  619               Insets tabAreaInsets = getTabAreaInsets(placement);
  620               switch(placement) {
  621               case JTabbedPane.TOP:
  622                   baseline += insets.top + tabAreaInsets.top;
  623                   return baseline;
  624               case JTabbedPane.BOTTOM:
  625                   baseline = height - insets.bottom -
  626                       tabAreaInsets.bottom - maxTabHeight + baseline;
  627                   return baseline;
  628               case JTabbedPane.LEFT:
  629               case JTabbedPane.RIGHT:
  630                   baseline += insets.top + tabAreaInsets.top;
  631                   return baseline;
  632               }
  633           }
  634           return -1;
  635       }
  636   
  637       /**
  638        * Returns an enum indicating how the baseline of the component
  639        * changes as the size changes.
  640        *
  641        * @throws NullPointerException {@inheritDoc}
  642        * @see javax.swing.JComponent#getBaseline(int, int)
  643        * @since 1.6
  644        */
  645       public Component.BaselineResizeBehavior getBaselineResizeBehavior(
  646               JComponent c) {
  647           super.getBaselineResizeBehavior(c);
  648           switch(tabPane.getTabPlacement()) {
  649           case JTabbedPane.LEFT:
  650           case JTabbedPane.RIGHT:
  651           case JTabbedPane.TOP:
  652               return Component.BaselineResizeBehavior.CONSTANT_ASCENT;
  653           case JTabbedPane.BOTTOM:
  654               return Component.BaselineResizeBehavior.CONSTANT_DESCENT;
  655           }
  656           return Component.BaselineResizeBehavior.OTHER;
  657       }
  658   
  659       /**
  660        * Returns the baseline for the specified tab.
  661        *
  662        * @param tab index of tab to get baseline for
  663        * @exception IndexOutOfBoundsException if index is out of range
  664        *            (index < 0 || index >= tab count)
  665        * @return baseline or a value &lt; 0 indicating there is no reasonable
  666        *                  baseline
  667        * @since 1.6
  668        */
  669       protected int getBaseline(int tab) {
  670           if (tabPane.getTabComponentAt(tab) != null) {
  671               int offset = getBaselineOffset();
  672               if (offset != 0) {
  673                   // The offset is not applied to the tab component, and so
  674                   // in general we can't get good alignment like with components
  675                   // in the tab.
  676                   return -1;
  677               }
  678               Component c = tabPane.getTabComponentAt(tab);
  679               Dimension pref = c.getPreferredSize();
  680               Insets tabInsets = getTabInsets(tabPane.getTabPlacement(), tab);
  681               int cellHeight = maxTabHeight - tabInsets.top - tabInsets.bottom;
  682               return c.getBaseline(pref.width, pref.height) +
  683                       (cellHeight - pref.height) / 2 + tabInsets.top;
  684           }
  685           else {
  686               View view = getTextViewForTab(tab);
  687               if (view != null) {
  688                   int viewHeight = (int)view.getPreferredSpan(View.Y_AXIS);
  689                   int baseline = BasicHTML.getHTMLBaseline(
  690                       view, (int)view.getPreferredSpan(View.X_AXIS), viewHeight);
  691                   if (baseline >= 0) {
  692                       return maxTabHeight / 2 - viewHeight / 2 + baseline +
  693                           getBaselineOffset();
  694                   }
  695                   return -1;
  696               }
  697           }
  698           FontMetrics metrics = getFontMetrics();
  699           int fontHeight = metrics.getHeight();
  700           int fontBaseline = metrics.getAscent();
  701           return maxTabHeight / 2 - fontHeight / 2 + fontBaseline +
  702                   getBaselineOffset();
  703       }
  704   
  705       /**
  706        * Returns the amount the baseline is offset by.  This is typically
  707        * the same as <code>getTabLabelShiftY</code>.
  708        *
  709        * @return amount to offset the baseline by
  710        * @since 1.6
  711        */
  712       protected int getBaselineOffset() {
  713           switch(tabPane.getTabPlacement()) {
  714           case JTabbedPane.TOP:
  715               if (tabPane.getTabCount() > 1) {
  716                   return 1;
  717               }
  718               else {
  719                   return -1;
  720               }
  721           case JTabbedPane.BOTTOM:
  722               if (tabPane.getTabCount() > 1) {
  723                   return -1;
  724               }
  725               else {
  726                   return 1;
  727               }
  728           default: // RIGHT|LEFT
  729               return (maxTabHeight % 2);
  730           }
  731       }
  732   
  733       private int calculateBaselineIfNecessary() {
  734           if (!calculatedBaseline) {
  735               calculatedBaseline = true;
  736               baseline = -1;
  737               if (tabPane.getTabCount() > 0) {
  738                   calculateBaseline();
  739               }
  740           }
  741           return baseline;
  742       }
  743   
  744       private void calculateBaseline() {
  745           int tabCount = tabPane.getTabCount();
  746           int tabPlacement = tabPane.getTabPlacement();
  747           maxTabHeight = calculateMaxTabHeight(tabPlacement);
  748           baseline = getBaseline(0);
  749           if (isHorizontalTabPlacement()) {
  750               for(int i = 1; i < tabCount; i++) {
  751                   if (getBaseline(i) != baseline) {
  752                       baseline = -1;
  753                       break;
  754                   }
  755               }
  756           }
  757           else {
  758               // left/right, tabs may be different sizes.
  759               FontMetrics fontMetrics = getFontMetrics();
  760               int fontHeight = fontMetrics.getHeight();
  761               int height = calculateTabHeight(tabPlacement, 0, fontHeight);
  762               for(int i = 1; i < tabCount; i++) {
  763                   int newHeight = calculateTabHeight(tabPlacement, i,fontHeight);
  764                   if (height != newHeight) {
  765                       // assume different baseline
  766                       baseline = -1;
  767                       break;
  768                   }
  769               }
  770           }
  771       }
  772   
  773   // UI Rendering
  774   
  775       public void paint(Graphics g, JComponent c) {
  776           int selectedIndex = tabPane.getSelectedIndex();
  777           int tabPlacement = tabPane.getTabPlacement();
  778   
  779           ensureCurrentLayout();
  780   
  781           // Paint content border and tab area
  782           if (tabsOverlapBorder) {
  783               paintContentBorder(g, tabPlacement, selectedIndex);
  784           }
  785           // If scrollable tabs are enabled, the tab area will be
  786           // painted by the scrollable tab panel instead.
  787           //
  788           if (!scrollableTabLayoutEnabled()) { // WRAP_TAB_LAYOUT
  789               paintTabArea(g, tabPlacement, selectedIndex);
  790           }
  791           if (!tabsOverlapBorder) {
  792               paintContentBorder(g, tabPlacement, selectedIndex);
  793           }
  794       }
  795   
  796       /**
  797        * Paints the tabs in the tab area.
  798        * Invoked by paint().
  799        * The graphics parameter must be a valid <code>Graphics</code>
  800        * object.  Tab placement may be either:
  801        * <code>JTabbedPane.TOP</code>, <code>JTabbedPane.BOTTOM</code>,
  802        * <code>JTabbedPane.LEFT</code>, or <code>JTabbedPane.RIGHT</code>.
  803        * The selected index must be a valid tabbed pane tab index (0 to
  804        * tab count - 1, inclusive) or -1 if no tab is currently selected.
  805        * The handling of invalid parameters is unspecified.
  806        *
  807        * @param g the graphics object to use for rendering
  808        * @param tabPlacement the placement for the tabs within the JTabbedPane
  809        * @param selectedIndex the tab index of the selected component
  810        *
  811        * @since 1.4
  812        */
  813       protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex) {
  814           int tabCount = tabPane.getTabCount();
  815   
  816           Rectangle iconRect = new Rectangle(),
  817                     textRect = new Rectangle();
  818           Rectangle clipRect = g.getClipBounds();
  819   
  820           // Paint tabRuns of tabs from back to front
  821           for (int i = runCount - 1; i >= 0; i--) {
  822               int start = tabRuns[i];
  823               int next = tabRuns[(i == runCount - 1)? 0 : i + 1];
  824               int end = (next != 0? next - 1: tabCount - 1);
  825               for (int j = start; j <= end; j++) {
  826                   if (j != selectedIndex && rects[j].intersects(clipRect)) {
  827                       paintTab(g, tabPlacement, rects, j, iconRect, textRect);
  828                   }
  829               }
  830           }
  831   
  832           // Paint selected tab if its in the front run
  833           // since it may overlap other tabs
  834           if (selectedIndex >= 0 && rects[selectedIndex].intersects(clipRect)) {
  835               paintTab(g, tabPlacement, rects, selectedIndex, iconRect, textRect);
  836           }
  837       }
  838   
  839       protected void paintTab(Graphics g, int tabPlacement,
  840                               Rectangle[] rects, int tabIndex,
  841                               Rectangle iconRect, Rectangle textRect) {
  842           Rectangle tabRect = rects[tabIndex];
  843           int selectedIndex = tabPane.getSelectedIndex();
  844           boolean isSelected = selectedIndex == tabIndex;
  845   
  846           if (tabsOpaque || tabPane.isOpaque()) {
  847               paintTabBackground(g, tabPlacement, tabIndex, tabRect.x, tabRect.y,
  848                       tabRect.width, tabRect.height, isSelected);
  849           }
  850   
  851           paintTabBorder(g, tabPlacement, tabIndex, tabRect.x, tabRect.y,
  852                          tabRect.width, tabRect.height, isSelected);
  853   
  854           String title = tabPane.getTitleAt(tabIndex);
  855           Font font = tabPane.getFont();
  856           FontMetrics metrics = SwingUtilities2.getFontMetrics(tabPane, g, font);
  857           Icon icon = getIconForTab(tabIndex);
  858   
  859           layoutLabel(tabPlacement, metrics, tabIndex, title, icon,
  860                       tabRect, iconRect, textRect, isSelected);
  861   
  862           if (tabPane.getTabComponentAt(tabIndex) == null) {
  863               String clippedTitle = title;
  864   
  865               if (scrollableTabLayoutEnabled() && tabScroller.croppedEdge.isParamsSet() &&
  866                       tabScroller.croppedEdge.getTabIndex() == tabIndex && isHorizontalTabPlacement()) {
  867                   int availTextWidth = tabScroller.croppedEdge.getCropline() -
  868                           (textRect.x - tabRect.x) - tabScroller.croppedEdge.getCroppedSideWidth();
  869                   clippedTitle = SwingUtilities2.clipStringIfNecessary(null, metrics, title, availTextWidth);
  870               }
  871   
  872               paintText(g, tabPlacement, font, metrics,
  873                       tabIndex, clippedTitle, textRect, isSelected);
  874   
  875               paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected);
  876           }
  877           paintFocusIndicator(g, tabPlacement, rects, tabIndex,
  878                     iconRect, textRect, isSelected);
  879       }
  880   
  881       private boolean isHorizontalTabPlacement() {
  882           return tabPane.getTabPlacement() == TOP || tabPane.getTabPlacement() == BOTTOM;
  883       }
  884   
  885       /* This method will create and return a polygon shape for the given tab rectangle
  886        * which has been cropped at the specified cropline with a torn edge visual.
  887        * e.g. A "File" tab which has cropped been cropped just after the "i":
  888        *             -------------
  889        *             |  .....     |
  890        *             |  .          |
  891        *             |  ...  .    |
  892        *             |  .    .   |
  893        *             |  .    .    |
  894        *             |  .    .     |
  895        *             --------------
  896        *
  897        * The x, y arrays below define the pattern used to create a "torn" edge
  898        * segment which is repeated to fill the edge of the tab.
  899        * For tabs placed on TOP and BOTTOM, this righthand torn edge is created by
  900        * line segments which are defined by coordinates obtained by
  901        * subtracting xCropLen[i] from (tab.x + tab.width) and adding yCroplen[i]
  902        * to (tab.y).
  903        * For tabs placed on LEFT or RIGHT, the bottom torn edge is created by
  904        * subtracting xCropLen[i] from (tab.y + tab.height) and adding yCropLen[i]
  905        * to (tab.x).
  906        */
  907       private static int xCropLen[] = {1,1,0,0,1,1,2,2};
  908       private static int yCropLen[] = {0,3,3,6,6,9,9,12};
  909       private static final int CROP_SEGMENT = 12;
  910   
  911       private static Polygon createCroppedTabShape(int tabPlacement, Rectangle tabRect, int cropline) {
  912           int rlen = 0;
  913           int start = 0;
  914           int end = 0;
  915           int ostart = 0;
  916   
  917           switch(tabPlacement) {
  918             case LEFT:
  919             case RIGHT:
  920                 rlen = tabRect.width;
  921                 start = tabRect.x;
  922                 end = tabRect.x + tabRect.width;
  923                 ostart = tabRect.y + tabRect.height;
  924                 break;
  925             case TOP:
  926             case BOTTOM:
  927             default:
  928                rlen = tabRect.height;
  929                start = tabRect.y;
  930                end = tabRect.y + tabRect.height;
  931                ostart = tabRect.x + tabRect.width;
  932           }
  933           int rcnt = rlen/CROP_SEGMENT;
  934           if (rlen%CROP_SEGMENT > 0) {
  935               rcnt++;
  936           }
  937           int npts = 2 + (rcnt*8);
  938           int xp[] = new int[npts];
  939           int yp[] = new int[npts];
  940           int pcnt = 0;
  941   
  942           xp[pcnt] = ostart;
  943           yp[pcnt++] = end;
  944           xp[pcnt] = ostart;
  945           yp[pcnt++] = start;
  946           for(int i = 0; i < rcnt; i++) {
  947               for(int j = 0; j < xCropLen.length; j++) {
  948                   xp[pcnt] = cropline - xCropLen[j];
  949                   yp[pcnt] = start + (i*CROP_SEGMENT) + yCropLen[j];
  950                   if (yp[pcnt] >= end) {
  951                       yp[pcnt] = end;
  952                       pcnt++;
  953                       break;
  954                   }
  955                   pcnt++;
  956               }
  957           }
  958           if (tabPlacement == JTabbedPane.TOP || tabPlacement == JTabbedPane.BOTTOM) {
  959              return new Polygon(xp, yp, pcnt);
  960   
  961           } else { // LEFT or RIGHT
  962              return new Polygon(yp, xp, pcnt);
  963           }
  964       }
  965   
  966       /* If tabLayoutPolicy == SCROLL_TAB_LAYOUT, this method will paint an edge
  967        * indicating the tab is cropped in the viewport display
  968        */
  969       private void paintCroppedTabEdge(Graphics g) {
  970           int tabIndex = tabScroller.croppedEdge.getTabIndex();
  971           int cropline = tabScroller.croppedEdge.getCropline();
  972           int x,y;
  973           switch(tabPane.getTabPlacement()) {
  974             case LEFT:
  975             case RIGHT:
  976               x = rects[tabIndex].x;
  977               y = cropline;
  978               int xx = x;
  979               g.setColor(shadow);
  980               while(xx <= x+rects[tabIndex].width) {
  981                   for (int i=0; i < xCropLen.length; i+=2) {
  982                       g.drawLine(xx+yCropLen[i],y-xCropLen[i],
  983                                  xx+yCropLen[i+1]-1,y-xCropLen[i+1]);
  984                   }
  985                   xx+=CROP_SEGMENT;
  986               }
  987               break;
  988             case TOP:
  989             case BOTTOM:
  990             default:
  991               x = cropline;
  992               y = rects[tabIndex].y;
  993               int yy = y;
  994               g.setColor(shadow);
  995               while(yy <= y+rects[tabIndex].height) {
  996                   for (int i=0; i < xCropLen.length; i+=2) {
  997                       g.drawLine(x-xCropLen[i],yy+yCropLen[i],
  998                                  x-xCropLen[i+1],yy+yCropLen[i+1]-1);
  999                   }
 1000                   yy+=CROP_SEGMENT;
 1001               }
 1002           }
 1003       }
 1004   
 1005       protected void layoutLabel(int tabPlacement,
 1006                                  FontMetrics metrics, int tabIndex,
 1007                                  String title, Icon icon,
 1008                                  Rectangle tabRect, Rectangle iconRect,
 1009                                  Rectangle textRect, boolean isSelected ) {
 1010           textRect.x = textRect.y = iconRect.x = iconRect.y = 0;
 1011   
 1012           View v = getTextViewForTab(tabIndex);
 1013           if (v != null) {
 1014               tabPane.putClientProperty("html", v);
 1015           }
 1016   
 1017           SwingUtilities.layoutCompoundLabel((JComponent) tabPane,
 1018                                              metrics, title, icon,
 1019                                              SwingUtilities.CENTER,
 1020                                              SwingUtilities.CENTER,
 1021                                              SwingUtilities.CENTER,
 1022                                              SwingUtilities.TRAILING,
 1023                                              tabRect,
 1024                                              iconRect,
 1025                                              textRect,
 1026                                              textIconGap);
 1027   
 1028           tabPane.putClientProperty("html", null);
 1029   
 1030           int xNudge = getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
 1031           int yNudge = getTabLabelShiftY(tabPlacement, tabIndex, isSelected);
 1032           iconRect.x += xNudge;
 1033           iconRect.y += yNudge;
 1034           textRect.x += xNudge;
 1035           textRect.y += yNudge;
 1036       }
 1037   
 1038       protected void paintIcon(Graphics g, int tabPlacement,
 1039                                int tabIndex, Icon icon, Rectangle iconRect,
 1040                                boolean isSelected ) {
 1041           if (icon != null) {
 1042               icon.paintIcon(tabPane, g, iconRect.x, iconRect.y);
 1043           }
 1044       }
 1045   
 1046       protected void paintText(Graphics g, int tabPlacement,
 1047                                Font font, FontMetrics metrics, int tabIndex,
 1048                                String title, Rectangle textRect,
 1049                                boolean isSelected) {
 1050   
 1051           g.setFont(font);
 1052   
 1053           View v = getTextViewForTab(tabIndex);
 1054           if (v != null) {
 1055               // html
 1056               v.paint(g, textRect);
 1057           } else {
 1058               // plain text
 1059               int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
 1060   
 1061               if (tabPane.isEnabled() && tabPane.isEnabledAt(tabIndex)) {
 1062                   Color fg = tabPane.getForegroundAt(tabIndex);
 1063                   if (isSelected && (fg instanceof UIResource)) {
 1064                       Color selectedFG = UIManager.getColor(
 1065                                     "TabbedPane.selectedForeground");
 1066                       if (selectedFG != null) {
 1067                           fg = selectedFG;
 1068                       }
 1069                   }
 1070                   g.setColor(fg);
 1071                   SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
 1072                                title, mnemIndex,
 1073                                textRect.x, textRect.y + metrics.getAscent());
 1074   
 1075               } else { // tab disabled
 1076                   g.setColor(tabPane.getBackgroundAt(tabIndex).brighter());
 1077                   SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
 1078                                title, mnemIndex,
 1079                                textRect.x, textRect.y + metrics.getAscent());
 1080                   g.setColor(tabPane.getBackgroundAt(tabIndex).darker());
 1081                   SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
 1082                                title, mnemIndex,
 1083                                textRect.x - 1, textRect.y + metrics.getAscent() - 1);
 1084   
 1085               }
 1086           }
 1087       }
 1088   
 1089   
 1090       protected int getTabLabelShiftX(int tabPlacement, int tabIndex, boolean isSelected) {
 1091           Rectangle tabRect = rects[tabIndex];
 1092           String propKey = (isSelected ? "selectedLabelShift" : "labelShift");
 1093           int nudge = DefaultLookup.getInt(
 1094                   tabPane, this, "TabbedPane." + propKey, 1);
 1095   
 1096           switch (tabPlacement) {
 1097               case LEFT:
 1098                   return nudge;
 1099               case RIGHT:
 1100                   return -nudge;
 1101               case BOTTOM:
 1102               case TOP:
 1103               default:
 1104                   return tabRect.width % 2;
 1105           }
 1106       }
 1107   
 1108       protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) {
 1109           Rectangle tabRect = rects[tabIndex];
 1110           String propKey = (isSelected ? "selectedLabelShift" : "labelShift");
 1111           int nudge = DefaultLookup.getInt(
 1112                   tabPane, this, "TabbedPane." + propKey, 1);
 1113   
 1114           switch (tabPlacement) {
 1115               case BOTTOM:
 1116                   return -nudge;
 1117               case LEFT:
 1118               case RIGHT:
 1119                   return tabRect.height % 2;
 1120               case TOP:
 1121               default:
 1122                   return nudge;
 1123           }
 1124       }
 1125   
 1126       protected void paintFocusIndicator(Graphics g, int tabPlacement,
 1127                                          Rectangle[] rects, int tabIndex,
 1128                                          Rectangle iconRect, Rectangle textRect,
 1129                                          boolean isSelected) {
 1130           Rectangle tabRect = rects[tabIndex];
 1131           if (tabPane.hasFocus() && isSelected) {
 1132               int x, y, w, h;
 1133               g.setColor(focus);
 1134               switch(tabPlacement) {
 1135                 case LEFT:
 1136                     x = tabRect.x + 3;
 1137                     y = tabRect.y + 3;
 1138                     w = tabRect.width - 5;
 1139                     h = tabRect.height - 6;
 1140                     break;
 1141                 case RIGHT:
 1142                     x = tabRect.x + 2;
 1143                     y = tabRect.y + 3;
 1144                     w = tabRect.width - 5;
 1145                     h = tabRect.height - 6;
 1146                     break;
 1147                 case BOTTOM:
 1148                     x = tabRect.x + 3;
 1149                     y = tabRect.y + 2;
 1150                     w = tabRect.width - 6;
 1151                     h = tabRect.height - 5;
 1152                     break;
 1153                 case TOP:
 1154                 default:
 1155                     x = tabRect.x + 3;
 1156                     y = tabRect.y + 3;
 1157                     w = tabRect.width - 6;
 1158                     h = tabRect.height - 5;
 1159               }
 1160               BasicGraphicsUtils.drawDashedRect(g, x, y, w, h);
 1161           }
 1162       }
 1163   
 1164       /**
 1165         * this function draws the border around each tab
 1166         * note that this function does now draw the background of the tab.
 1167         * that is done elsewhere
 1168         */
 1169       protected void paintTabBorder(Graphics g, int tabPlacement,
 1170                                     int tabIndex,
 1171                                     int x, int y, int w, int h,
 1172                                     boolean isSelected ) {
 1173           g.setColor(lightHighlight);
 1174   
 1175           switch (tabPlacement) {
 1176             case LEFT:
 1177                 g.drawLine(x+1, y+h-2, x+1, y+h-2); // bottom-left highlight
 1178                 g.drawLine(x, y+2, x, y+h-3); // left highlight
 1179                 g.drawLine(x+1, y+1, x+1, y+1); // top-left highlight
 1180                 g.drawLine(x+2, y, x+w-1, y); // top highlight
 1181   
 1182                 g.setColor(shadow);
 1183                 g.drawLine(x+2, y+h-2, x+w-1, y+h-2); // bottom shadow
 1184   
 1185                 g.setColor(darkShadow);
 1186                 g.drawLine(x+2, y+h-1, x+w-1, y+h-1); // bottom dark shadow
 1187                 break;
 1188             case RIGHT:
 1189                 g.drawLine(x, y, x+w-3, y); // top highlight
 1190   
 1191                 g.setColor(shadow);
 1192                 g.drawLine(x, y+h-2, x+w-3, y+h-2); // bottom shadow
 1193                 g.drawLine(x+w-2, y+2, x+w-2, y+h-3); // right shadow
 1194   
 1195                 g.setColor(darkShadow);
 1196                 g.drawLine(x+w-2, y+1, x+w-2, y+1); // top-right dark shadow
 1197                 g.drawLine(x+w-2, y+h-2, x+w-2, y+h-2); // bottom-right dark shadow
 1198                 g.drawLine(x+w-1, y+2, x+w-1, y+h-3); // right dark shadow
 1199                 g.drawLine(x, y+h-1, x+w-3, y+h-1); // bottom dark shadow
 1200                 break;
 1201             case BOTTOM:
 1202                 g.drawLine(x, y, x, y+h-3); // left highlight
 1203                 g.drawLine(x+1, y+h-2, x+1, y+h-2); // bottom-left highlight
 1204   
 1205                 g.setColor(shadow);
 1206                 g.drawLine(x+2, y+h-2, x+w-3, y+h-2); // bottom shadow
 1207                 g.drawLine(x+w-2, y, x+w-2, y+h-3); // right shadow
 1208   
 1209                 g.setColor(darkShadow);
 1210                 g.drawLine(x+2, y+h-1, x+w-3, y+h-1); // bottom dark shadow
 1211                 g.drawLine(x+w-2, y+h-2, x+w-2, y+h-2); // bottom-right dark shadow
 1212                 g.drawLine(x+w-1, y, x+w-1, y+h-3); // right dark shadow
 1213                 break;
 1214             case TOP:
 1215             default:
 1216                 g.drawLine(x, y+2, x, y+h-1); // left highlight
 1217                 g.drawLine(x+1, y+1, x+1, y+1); // top-left highlight
 1218                 g.drawLine(x+2, y, x+w-3, y); // top highlight
 1219   
 1220                 g.setColor(shadow);
 1221                 g.drawLine(x+w-2, y+2, x+w-2, y+h-1); // right shadow
 1222   
 1223                 g.setColor(darkShadow);
 1224                 g.drawLine(x+w-1, y+2, x+w-1, y+h-1); // right dark-shadow
 1225                 g.drawLine(x+w-2, y+1, x+w-2, y+1); // top-right shadow
 1226           }
 1227       }
 1228   
 1229       protected void paintTabBackground(Graphics g, int tabPlacement,
 1230                                         int tabIndex,
 1231                                         int x, int y, int w, int h,
 1232                                         boolean isSelected ) {
 1233           g.setColor(!isSelected || selectedColor == null?
 1234                      tabPane.getBackgroundAt(tabIndex) : selectedColor);
 1235           switch(tabPlacement) {
 1236             case LEFT:
 1237                 g.fillRect(x+1, y+1, w-1, h-3);
 1238                 break;
 1239             case RIGHT:
 1240                 g.fillRect(x, y+1, w-2, h-3);
 1241                 break;
 1242             case BOTTOM:
 1243                 g.fillRect(x+1, y, w-3, h-1);
 1244                 break;
 1245             case TOP:
 1246             default:
 1247                 g.fillRect(x+1, y+1, w-3, h-1);
 1248           }
 1249       }
 1250   
 1251       protected void paintContentBorder(Graphics g, int tabPlacement, int selectedIndex) {
 1252           int width = tabPane.getWidth();
 1253           int height = tabPane.getHeight();
 1254           Insets insets = tabPane.getInsets();
 1255           Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
 1256   
 1257           int x = insets.left;
 1258           int y = insets.top;
 1259           int w = width - insets.right - insets.left;
 1260           int h = height - insets.top - insets.bottom;
 1261   
 1262           switch(tabPlacement) {
 1263             case LEFT:
 1264                 x += calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
 1265                 if (tabsOverlapBorder) {
 1266                     x -= tabAreaInsets.right;
 1267                 }
 1268                 w -= (x - insets.left);
 1269                 break;
 1270             case RIGHT:
 1271                 w -= calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
 1272                 if (tabsOverlapBorder) {
 1273                     w += tabAreaInsets.left;
 1274                 }
 1275                 break;
 1276             case BOTTOM:
 1277                 h -= calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
 1278                 if (tabsOverlapBorder) {
 1279                     h += tabAreaInsets.top;
 1280                 }
 1281                 break;
 1282             case TOP:
 1283             default:
 1284                 y += calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
 1285                 if (tabsOverlapBorder) {
 1286                     y -= tabAreaInsets.bottom;
 1287                 }
 1288                 h -= (y - insets.top);
 1289           }
 1290   
 1291               if ( tabPane.getTabCount() > 0 && (contentOpaque || tabPane.isOpaque()) ) {
 1292               // Fill region behind content area
 1293               Color color = UIManager.getColor("TabbedPane.contentAreaColor");
 1294               if (color != null) {
 1295                   g.setColor(color);
 1296               }
 1297               else if ( selectedColor == null || selectedIndex == -1 ) {
 1298                   g.setColor(tabPane.getBackground());
 1299               }
 1300               else {
 1301                   g.setColor(selectedColor);
 1302               }
 1303               g.fillRect(x,y,w,h);
 1304           }
 1305   
 1306           paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h);
 1307           paintContentBorderLeftEdge(g, tabPlacement, selectedIndex, x, y, w, h);
 1308           paintContentBorderBottomEdge(g, tabPlacement, selectedIndex, x, y, w, h);
 1309           paintContentBorderRightEdge(g, tabPlacement, selectedIndex, x, y, w, h);
 1310   
 1311       }
 1312   
 1313       protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
 1314                                            int selectedIndex,
 1315                                            int x, int y, int w, int h) {
 1316           Rectangle selRect = selectedIndex < 0? null :
 1317                                  getTabBounds(selectedIndex, calcRect);
 1318   
 1319           g.setColor(lightHighlight);
 1320   
 1321           // Draw unbroken line if tabs are not on TOP, OR
 1322           // selected tab is not in run adjacent to content, OR
 1323           // selected tab is not visible (SCROLL_TAB_LAYOUT)
 1324           //
 1325           if (tabPlacement != TOP || selectedIndex < 0 ||
 1326               (selRect.y + selRect.height + 1 < y) ||
 1327               (selRect.x < x || selRect.x > x + w)) {
 1328               g.drawLine(x, y, x+w-2, y);
 1329           } else {
 1330               // Break line to show visual connection to selected tab
 1331               g.drawLine(x, y, selRect.x - 1, y);
 1332               if (selRect.x + selRect.width < x + w - 2) {
 1333                   g.drawLine(selRect.x + selRect.width, y,
 1334                              x+w-2, y);
 1335               } else {
 1336                   g.setColor(shadow);
 1337                   g.drawLine(x+w-2, y, x+w-2, y);
 1338               }
 1339           }
 1340       }
 1341   
 1342       protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
 1343                                                  int selectedIndex,
 1344                                                  int x, int y, int w, int h) {
 1345           Rectangle selRect = selectedIndex < 0? null :
 1346                                  getTabBounds(selectedIndex, calcRect);
 1347   
 1348           g.setColor(lightHighlight);
 1349   
 1350           // Draw unbroken line if tabs are not on LEFT, OR
 1351           // selected tab is not in run adjacent to content, OR
 1352           // selected tab is not visible (SCROLL_TAB_LAYOUT)
 1353           //
 1354           if (tabPlacement != LEFT || selectedIndex < 0 ||
 1355               (selRect.x + selRect.width + 1 < x) ||
 1356               (selRect.y < y || selRect.y > y + h)) {
 1357               g.drawLine(x, y, x, y+h-2);
 1358           } else {
 1359               // Break line to show visual connection to selected tab
 1360               g.drawLine(x, y, x, selRect.y - 1);
 1361               if (selRect.y + selRect.height < y + h - 2) {
 1362                   g.drawLine(x, selRect.y + selRect.height,
 1363                              x, y+h-2);
 1364               }
 1365           }
 1366       }
 1367   
 1368       protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
 1369                                                  int selectedIndex,
 1370                                                  int x, int y, int w, int h) {
 1371           Rectangle selRect = selectedIndex < 0? null :
 1372                                  getTabBounds(selectedIndex, calcRect);
 1373   
 1374           g.setColor(shadow);
 1375   
 1376           // Draw unbroken line if tabs are not on BOTTOM, OR
 1377           // selected tab is not in run adjacent to content, OR
 1378           // selected tab is not visible (SCROLL_TAB_LAYOUT)
 1379           //
 1380           if (tabPlacement != BOTTOM || selectedIndex < 0 ||
 1381                (selRect.y - 1 > h) ||
 1382                (selRect.x < x || selRect.x > x + w)) {
 1383               g.drawLine(x+1, y+h-2, x+w-2, y+h-2);
 1384               g.setColor(darkShadow);
 1385               g.drawLine(x, y+h-1, x+w-1, y+h-1);
 1386           } else {
 1387               // Break line to show visual connection to selected tab
 1388               g.drawLine(x+1, y+h-2, selRect.x - 1, y+h-2);
 1389               g.setColor(darkShadow);
 1390               g.drawLine(x, y+h-1, selRect.x - 1, y+h-1);
 1391               if (selRect.x + selRect.width < x + w - 2) {
 1392                   g.setColor(shadow);
 1393                   g.drawLine(selRect.x + selRect.width, y+h-2, x+w-2, y+h-2);
 1394                   g.setColor(darkShadow);
 1395                   g.drawLine(selRect.x + selRect.width, y+h-1, x+w-1, y+h-1);
 1396               }
 1397           }
 1398   
 1399       }
 1400   
 1401       protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
 1402                                                  int selectedIndex,
 1403                                                  int x, int y, int w, int h) {
 1404           Rectangle selRect = selectedIndex < 0? null :
 1405                                  getTabBounds(selectedIndex, calcRect);
 1406   
 1407           g.setColor(shadow);
 1408   
 1409           // Draw unbroken line if tabs are not on RIGHT, OR
 1410           // selected tab is not in run adjacent to content, OR
 1411           // selected tab is not visible (SCROLL_TAB_LAYOUT)
 1412           //
 1413           if (tabPlacement != RIGHT || selectedIndex < 0 ||
 1414                (selRect.x - 1 > w) ||
 1415                (selRect.y < y || selRect.y > y + h)) {
 1416               g.drawLine(x+w-2, y+1, x+w-2, y+h-3);
 1417               g.setColor(darkShadow);
 1418               g.drawLine(x+w-1, y, x+w-1, y+h-1);
 1419           } else {
 1420               // Break line to show visual connection to selected tab
 1421               g.drawLine(x+w-2, y+1, x+w-2, selRect.y - 1);
 1422               g.setColor(darkShadow);
 1423               g.drawLine(x+w-1, y, x+w-1, selRect.y - 1);
 1424   
 1425               if (selRect.y + selRect.height < y + h - 2) {
 1426                   g.setColor(shadow);
 1427                   g.drawLine(x+w-2, selRect.y + selRect.height,
 1428                              x+w-2, y+h-2);
 1429                   g.setColor(darkShadow);
 1430                   g.drawLine(x+w-1, selRect.y + selRect.height,
 1431                              x+w-1, y+h-2);
 1432               }
 1433           }
 1434       }
 1435   
 1436       private void ensureCurrentLayout() {
 1437           if (!tabPane.isValid()) {
 1438               tabPane.validate();
 1439           }
 1440           /* If tabPane doesn't have a peer yet, the validate() call will
 1441            * silently fail.  We handle that by forcing a layout if tabPane
 1442            * is still invalid.  See bug 4237677.
 1443            */
 1444           if (!tabPane.isValid()) {
 1445               TabbedPaneLayout layout = (TabbedPaneLayout)tabPane.getLayout();
 1446               layout.calculateLayoutInfo();
 1447           }
 1448       }
 1449   
 1450   
 1451   // TabbedPaneUI methods
 1452   
 1453       /**
 1454        * Returns the bounds of the specified tab index.  The bounds are
 1455        * with respect to the JTabbedPane's coordinate space.
 1456        */
 1457       public Rectangle getTabBounds(JTabbedPane pane, int i) {
 1458           ensureCurrentLayout();
 1459           Rectangle tabRect = new Rectangle();
 1460           return getTabBounds(i, tabRect);
 1461       }
 1462   
 1463       public int getTabRunCount(JTabbedPane pane) {
 1464           ensureCurrentLayout();
 1465           return runCount;
 1466       }
 1467   
 1468       /**
 1469        * Returns the tab index which intersects the specified point
 1470        * in the JTabbedPane's coordinate space.
 1471        */
 1472       public int tabForCoordinate(JTabbedPane pane, int x, int y) {
 1473           return tabForCoordinate(pane, x, y, true);
 1474       }
 1475   
 1476       private int tabForCoordinate(JTabbedPane pane, int x, int y,
 1477                                    boolean validateIfNecessary) {
 1478           if (validateIfNecessary) {
 1479               ensureCurrentLayout();
 1480           }
 1481           if (isRunsDirty) {
 1482               // We didn't recalculate the layout, runs and tabCount may not
 1483               // line up, bail.
 1484               return -1;
 1485           }
 1486           Point p = new Point(x, y);
 1487   
 1488           if (scrollableTabLayoutEnabled()) {
 1489               translatePointToTabPanel(x, y, p);
 1490               Rectangle viewRect = tabScroller.viewport.getViewRect();
 1491               if (!viewRect.contains(p)) {
 1492                   return -1;
 1493               }
 1494           }
 1495           int tabCount = tabPane.getTabCount();
 1496           for (int i = 0; i < tabCount; i++) {
 1497               if (rects[i].contains(p.x, p.y)) {
 1498                   return i;
 1499               }
 1500           }
 1501           return -1;
 1502       }
 1503   
 1504       /**
 1505        * Returns the bounds of the specified tab in the coordinate space
 1506        * of the JTabbedPane component.  This is required because the tab rects
 1507        * are by default defined in the coordinate space of the component where
 1508        * they are rendered, which could be the JTabbedPane
 1509        * (for WRAP_TAB_LAYOUT) or a ScrollableTabPanel (SCROLL_TAB_LAYOUT).
 1510        * This method should be used whenever the tab rectangle must be relative
 1511        * to the JTabbedPane itself and the result should be placed in a
 1512        * designated Rectangle object (rather than instantiating and returning
 1513        * a new Rectangle each time). The tab index parameter must be a valid
 1514        * tabbed pane tab index (0 to tab count - 1, inclusive).  The destination
 1515        * rectangle parameter must be a valid <code>Rectangle</code> instance.
 1516        * The handling of invalid parameters is unspecified.
 1517        *
 1518        * @param tabIndex the index of the tab
 1519        * @param dest the rectangle where the result should be placed
 1520        * @return the resulting rectangle
 1521        *
 1522        * @since 1.4
 1523        */
 1524       protected Rectangle getTabBounds(int tabIndex, Rectangle dest) {
 1525           dest.width = rects[tabIndex].width;
 1526           dest.height = rects[tabIndex].height;
 1527   
 1528           if (scrollableTabLayoutEnabled()) { // SCROLL_TAB_LAYOUT
 1529               // Need to translate coordinates based on viewport location &
 1530               // view position
 1531               Point vpp = tabScroller.viewport.getLocation();
 1532               Point viewp = tabScroller.viewport.getViewPosition();
 1533               dest.x = rects[tabIndex].x + vpp.x - viewp.x;
 1534               dest.y = rects[tabIndex].y + vpp.y - viewp.y;
 1535   
 1536           } else { // WRAP_TAB_LAYOUT
 1537               dest.x = rects[tabIndex].x;
 1538               dest.y = rects[tabIndex].y;
 1539           }
 1540           return dest;
 1541       }
 1542   
 1543       /**
 1544        * Returns the index of the tab closest to the passed in location, note
 1545        * that the returned tab may not contain the location x,y.
 1546        */
 1547       private int getClosestTab(int x, int y) {
 1548           int min = 0;
 1549           int tabCount = Math.min(rects.length, tabPane.getTabCount());
 1550           int max = tabCount;
 1551           int tabPlacement = tabPane.getTabPlacement();
 1552           boolean useX = (tabPlacement == TOP || tabPlacement == BOTTOM);
 1553           int want = (useX) ? x : y;
 1554   
 1555           while (min != max) {
 1556               int current = (max + min) / 2;
 1557               int minLoc;
 1558               int maxLoc;
 1559   
 1560               if (useX) {
 1561                   minLoc = rects[current].x;
 1562                   maxLoc = minLoc + rects[current].width;
 1563               }
 1564               else {
 1565                   minLoc = rects[current].y;
 1566                   maxLoc = minLoc + rects[current].height;
 1567               }
 1568               if (want < minLoc) {
 1569                   max = current;
 1570                   if (min == max) {
 1571                       return Math.max(0, current - 1);
 1572                   }
 1573               }
 1574               else if (want >= maxLoc) {
 1575                   min = current;
 1576                   if (max - min <= 1) {
 1577                       return Math.max(current + 1, tabCount - 1);
 1578                   }
 1579               }
 1580               else {
 1581                   return current;
 1582               }
 1583           }
 1584           return min;
 1585       }
 1586   
 1587       /**
 1588        * Returns a point which is translated from the specified point in the
 1589        * JTabbedPane's coordinate space to the coordinate space of the
 1590        * ScrollableTabPanel.  This is used for SCROLL_TAB_LAYOUT ONLY.
 1591        */
 1592       private Point translatePointToTabPanel(int srcx, int srcy, Point dest) {
 1593           Point vpp = tabScroller.viewport.getLocation();
 1594           Point viewp = tabScroller.viewport.getViewPosition();
 1595           dest.x = srcx - vpp.x + viewp.x;
 1596           dest.y = srcy - vpp.y + viewp.y;
 1597           return dest;
 1598       }
 1599   
 1600   // BasicTabbedPaneUI methods
 1601   
 1602       protected Component getVisibleComponent() {
 1603           return visibleComponent;
 1604       }
 1605   
 1606       protected void setVisibleComponent(Component component) {
 1607           if (visibleComponent != null
 1608                   && visibleComponent != component
 1609                   && visibleComponent.getParent() == tabPane
 1610                   && visibleComponent.isVisible()) {
 1611   
 1612               visibleComponent.setVisible(false);
 1613           }
 1614           if (component != null && !component.isVisible()) {
 1615               component.setVisible(true);
 1616           }
 1617           visibleComponent = component;
 1618       }
 1619   
 1620       protected void assureRectsCreated(int tabCount) {
 1621           int rectArrayLen = rects.length;
 1622           if (tabCount != rectArrayLen ) {
 1623               Rectangle[] tempRectArray = new Rectangle[tabCount];
 1624               System.arraycopy(rects, 0, tempRectArray, 0,
 1625                                Math.min(rectArrayLen, tabCount));
 1626               rects = tempRectArray;
 1627               for (int rectIndex = rectArrayLen; rectIndex < tabCount; rectIndex++) {
 1628                   rects[rectIndex] = new Rectangle();
 1629               }
 1630           }
 1631   
 1632       }
 1633   
 1634       protected void expandTabRunsArray() {
 1635           int rectLen = tabRuns.length;
 1636           int[] newArray = new int[rectLen+10];
 1637           System.arraycopy(tabRuns, 0, newArray, 0, runCount);
 1638           tabRuns = newArray;
 1639       }
 1640   
 1641       protected int getRunForTab(int tabCount, int tabIndex) {
 1642           for (int i = 0; i < runCount; i++) {
 1643               int first = tabRuns[i];
 1644               int last = lastTabInRun(tabCount, i);
 1645               if (tabIndex >= first && tabIndex <= last) {
 1646                   return i;
 1647               }
 1648           }
 1649           return 0;
 1650       }
 1651   
 1652       protected int lastTabInRun(int tabCount, int run) {
 1653           if (runCount == 1) {
 1654               return tabCount - 1;
 1655           }
 1656           int nextRun = (run == runCount - 1? 0 : run + 1);
 1657           if (tabRuns[nextRun] == 0) {
 1658               return tabCount - 1;
 1659           }
 1660           return tabRuns[nextRun]-1;
 1661       }
 1662   
 1663       protected int getTabRunOverlay(int tabPlacement) {
 1664           return tabRunOverlay;
 1665       }
 1666   
 1667       protected int getTabRunIndent(int tabPlacement, int run) {
 1668           return 0;
 1669       }
 1670   
 1671       protected boolean shouldPadTabRun(int tabPlacement, int run) {
 1672           return runCount > 1;
 1673       }
 1674   
 1675       protected boolean shouldRotateTabRuns(int tabPlacement) {
 1676           return true;
 1677       }
 1678   
 1679       protected Icon getIconForTab(int tabIndex) {
 1680           return (!tabPane.isEnabled() || !tabPane.isEnabledAt(tabIndex))?
 1681                             tabPane.getDisabledIconAt(tabIndex) : tabPane.getIconAt(tabIndex);
 1682       }
 1683   
 1684       /**
 1685        * Returns the text View object required to render stylized text (HTML) for
 1686        * the specified tab or null if no specialized text rendering is needed
 1687        * for this tab. This is provided to support html rendering inside tabs.
 1688        *
 1689        * @param tabIndex the index of the tab
 1690        * @return the text view to render the tab's text or null if no
 1691        *         specialized rendering is required
 1692        *
 1693        * @since 1.4
 1694        */
 1695       protected View getTextViewForTab(int tabIndex) {
 1696           if (htmlViews != null) {
 1697               return (View)htmlViews.elementAt(tabIndex);
 1698           }
 1699           return null;
 1700       }
 1701   
 1702       protected int calculateTabHeight(int tabPlacement, int tabIndex, int fontHeight) {
 1703           int height = 0;
 1704           Component c = tabPane.getTabComponentAt(tabIndex);
 1705           if (c != null) {
 1706               height = c.getPreferredSize().height;
 1707           } else {
 1708               View v = getTextViewForTab(tabIndex);
 1709               if (v != null) {
 1710                   // html
 1711                   height += (int) v.getPreferredSpan(View.Y_AXIS);
 1712               } else {
 1713                   // plain text
 1714                   height += fontHeight;
 1715               }
 1716               Icon icon = getIconForTab(tabIndex);
 1717   
 1718               if (icon != null) {
 1719                   height = Math.max(height, icon.getIconHeight());
 1720               }
 1721           }
 1722           Insets tabInsets = getTabInsets(tabPlacement, tabIndex);
 1723           height += tabInsets.top + tabInsets.bottom + 2;
 1724           return height;
 1725       }
 1726   
 1727       protected int calculateMaxTabHeight(int tabPlacement) {
 1728           FontMetrics metrics = getFontMetrics();
 1729           int tabCount = tabPane.getTabCount();
 1730           int result = 0;
 1731           int fontHeight = metrics.getHeight();
 1732           for(int i = 0; i < tabCount; i++) {
 1733               result = Math.max(calculateTabHeight(tabPlacement, i, fontHeight), result);
 1734           }
 1735           return result;
 1736       }
 1737   
 1738       protected int calculateTabWidth(int tabPlacement, int tabIndex, FontMetrics metrics) {
 1739           Insets tabInsets = getTabInsets(tabPlacement, tabIndex);
 1740           int width = tabInsets.left + tabInsets.right + 3;
 1741           Component tabComponent = tabPane.getTabComponentAt(tabIndex);
 1742           if (tabComponent != null) {
 1743               width += tabComponent.getPreferredSize().width;
 1744           } else {
 1745               Icon icon = getIconForTab(tabIndex);
 1746               if (icon != null) {
 1747                   width += icon.getIconWidth() + textIconGap;
 1748               }
 1749               View v = getTextViewForTab(tabIndex);
 1750               if (v != null) {
 1751                   // html
 1752                   width += (int) v.getPreferredSpan(View.X_AXIS);
 1753               } else {
 1754                   // plain text
 1755                   String title = tabPane.getTitleAt(tabIndex);
 1756                   width += SwingUtilities2.stringWidth(tabPane, metrics, title);
 1757               }
 1758           }
 1759           return width;
 1760       }
 1761   
 1762       protected int calculateMaxTabWidth(int tabPlacement) {
 1763           FontMetrics metrics = getFontMetrics();
 1764           int tabCount = tabPane.getTabCount();
 1765           int result = 0;
 1766           for(int i = 0; i < tabCount; i++) {
 1767               result = Math.max(calculateTabWidth(tabPlacement, i, metrics), result);
 1768           }
 1769           return result;
 1770       }
 1771   
 1772       protected int calculateTabAreaHeight(int tabPlacement, int horizRunCount, int maxTabHeight) {
 1773           Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
 1774           int tabRunOverlay = getTabRunOverlay(tabPlacement);
 1775           return (horizRunCount > 0?
 1776                   horizRunCount * (maxTabHeight-tabRunOverlay) + tabRunOverlay +
 1777                   tabAreaInsets.top + tabAreaInsets.bottom :
 1778                   0);
 1779       }
 1780   
 1781       protected int calculateTabAreaWidth(int tabPlacement, int vertRunCount, int maxTabWidth) {
 1782           Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
 1783           int tabRunOverlay = getTabRunOverlay(tabPlacement);
 1784           return (vertRunCount > 0?
 1785                   vertRunCount * (maxTabWidth-tabRunOverlay) + tabRunOverlay +
 1786                   tabAreaInsets.left + tabAreaInsets.right :
 1787                   0);
 1788       }
 1789   
 1790       protected Insets getTabInsets(int tabPlacement, int tabIndex) {
 1791           return tabInsets;
 1792       }
 1793   
 1794       protected Insets getSelectedTabPadInsets(int tabPlacement) {
 1795           rotateInsets(selectedTabPadInsets, currentPadInsets, tabPlacement);
 1796           return currentPadInsets;
 1797       }
 1798   
 1799       protected Insets getTabAreaInsets(int tabPlacement) {
 1800           rotateInsets(tabAreaInsets, currentTabAreaInsets, tabPlacement);
 1801           return currentTabAreaInsets;
 1802       }
 1803   
 1804       protected Insets getContentBorderInsets(int tabPlacement) {
 1805           return contentBorderInsets;
 1806       }
 1807   
 1808       protected FontMetrics getFontMetrics() {
 1809           Font font = tabPane.getFont();
 1810           return tabPane.getFontMetrics(font);
 1811       }
 1812   
 1813   
 1814   // Tab Navigation methods
 1815   
 1816       protected void navigateSelectedTab(int direction) {
 1817           int tabPlacement = tabPane.getTabPlacement();
 1818           int current = DefaultLookup.getBoolean(tabPane, this,
 1819                                "TabbedPane.selectionFollowsFocus", true) ?
 1820                                tabPane.getSelectedIndex() : getFocusIndex();
 1821           int tabCount = tabPane.getTabCount();
 1822           boolean leftToRight = BasicGraphicsUtils.isLeftToRight(tabPane);
 1823   
 1824           // If we have no tabs then don't navigate.
 1825           if (tabCount <= 0) {
 1826               return;
 1827           }
 1828   
 1829           int offset;
 1830           switch(tabPlacement) {
 1831             case LEFT:
 1832             case RIGHT:
 1833                 switch(direction) {
 1834                    case NEXT:
 1835                        selectNextTab(current);
 1836                        break;
 1837                    case PREVIOUS:
 1838                        selectPreviousTab(current);
 1839                        break;
 1840                   case NORTH:
 1841                       selectPreviousTabInRun(current);
 1842                       break;
 1843                   case SOUTH:
 1844                       selectNextTabInRun(current);
 1845                       break;
 1846                   case WEST:
 1847                       offset = getTabRunOffset(tabPlacement, tabCount, current, false);
 1848                       selectAdjacentRunTab(tabPlacement, current, offset);
 1849                       break;
 1850                   case EAST:
 1851                       offset = getTabRunOffset(tabPlacement, tabCount, current, true);
 1852                       selectAdjacentRunTab(tabPlacement, current, offset);
 1853                       break;
 1854                   default:
 1855                 }
 1856                 break;
 1857             case BOTTOM:
 1858             case TOP:
 1859             default:
 1860                 switch(direction) {
 1861                   case NEXT:
 1862                       selectNextTab(current);
 1863                       break;
 1864                   case PREVIOUS:
 1865                       selectPreviousTab(current);
 1866                       break;
 1867                   case NORTH:
 1868                       offset = getTabRunOffset(tabPlacement, tabCount, current, false);
 1869                       selectAdjacentRunTab(tabPlacement, current, offset);
 1870                       break;
 1871                   case SOUTH:
 1872                       offset = getTabRunOffset(tabPlacement, tabCount, current, true);
 1873                       selectAdjacentRunTab(tabPlacement, current, offset);
 1874                       break;
 1875                   case EAST:
 1876                       if (leftToRight) {
 1877                           selectNextTabInRun(current);
 1878                       } else {
 1879                           selectPreviousTabInRun(current);
 1880                       }
 1881                       break;
 1882                   case WEST:
 1883                       if (leftToRight) {
 1884                           selectPreviousTabInRun(current);
 1885                       } else {
 1886                           selectNextTabInRun(current);
 1887                       }
 1888                       break;
 1889                   default:
 1890                 }
 1891           }
 1892       }
 1893   
 1894       protected void selectNextTabInRun(int current) {
 1895           int tabCount = tabPane.getTabCount();
 1896           int tabIndex = getNextTabIndexInRun(tabCount, current);
 1897   
 1898           while(tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
 1899               tabIndex = getNextTabIndexInRun(tabCount, tabIndex);
 1900           }
 1901           navigateTo(tabIndex);
 1902       }
 1903   
 1904       protected void selectPreviousTabInRun(int current) {
 1905           int tabCount = tabPane.getTabCount();
 1906           int tabIndex = getPreviousTabIndexInRun(tabCount, current);
 1907   
 1908           while(tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
 1909               tabIndex = getPreviousTabIndexInRun(tabCount, tabIndex);
 1910           }
 1911           navigateTo(tabIndex);
 1912       }
 1913   
 1914       protected void selectNextTab(int current) {
 1915           int tabIndex = getNextTabIndex(current);
 1916   
 1917           while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
 1918               tabIndex = getNextTabIndex(tabIndex);
 1919           }
 1920           navigateTo(tabIndex);
 1921       }
 1922   
 1923       protected void selectPreviousTab(int current) {
 1924           int tabIndex = getPreviousTabIndex(current);
 1925   
 1926           while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
 1927               tabIndex = getPreviousTabIndex(tabIndex);
 1928           }
 1929           navigateTo(tabIndex);
 1930       }
 1931   
 1932       protected void selectAdjacentRunTab(int tabPlacement,
 1933                                           int tabIndex, int offset) {
 1934           if ( runCount < 2 ) {
 1935               return;
 1936           }
 1937           int newIndex;
 1938           Rectangle r = rects[tabIndex];
 1939           switch(tabPlacement) {
 1940             case LEFT:
 1941             case RIGHT:
 1942                 newIndex = tabForCoordinate(tabPane, r.x + r.width/2 + offset,
 1943                                          r.y + r.height/2);
 1944                 break;
 1945             case BOTTOM:
 1946             case TOP:
 1947             default:
 1948                 newIndex = tabForCoordinate(tabPane, r.x + r.width/2,
 1949                                          r.y + r.height/2 + offset);
 1950           }
 1951           if (newIndex != -1) {
 1952               while (!tabPane.isEnabledAt(newIndex) && newIndex != tabIndex) {
 1953                   newIndex = getNextTabIndex(newIndex);
 1954               }
 1955               navigateTo(newIndex);
 1956           }
 1957       }
 1958   
 1959       private void navigateTo(int index) {
 1960           if (DefaultLookup.getBoolean(tabPane, this,
 1961                                "TabbedPane.selectionFollowsFocus", true)) {
 1962               tabPane.setSelectedIndex(index);
 1963           } else {
 1964               // Just move focus (not selection)
 1965               setFocusIndex(index, true);
 1966           }
 1967       }
 1968   
 1969       void setFocusIndex(int index, boolean repaint) {
 1970           if (repaint && !isRunsDirty) {
 1971               repaintTab(focusIndex);
 1972               focusIndex = index;
 1973               repaintTab(focusIndex);
 1974           }
 1975           else {
 1976               focusIndex = index;
 1977           }
 1978       }
 1979   
 1980       /**
 1981        * Repaints the specified tab.
 1982        */
 1983       private void repaintTab(int index) {
 1984           // If we're not valid that means we will shortly be validated and
 1985           // painted, which means we don't have to do anything here.
 1986           if (!isRunsDirty && index >= 0 && index < tabPane.getTabCount()) {
 1987               tabPane.repaint(getTabBounds(tabPane, index));
 1988           }
 1989       }
 1990   
 1991       /**
 1992        * Makes sure the focusIndex is valid.
 1993        */
 1994       private void validateFocusIndex() {
 1995           if (focusIndex >= tabPane.getTabCount()) {
 1996               setFocusIndex(tabPane.getSelectedIndex(), false);
 1997           }
 1998       }
 1999   
 2000       /**
 2001        * Returns the index of the tab that has focus.
 2002        *
 2003        * @return index of tab that has focus
 2004        * @since 1.5
 2005        */
 2006       protected int getFocusIndex() {
 2007           return focusIndex;
 2008       }
 2009   
 2010       protected int getTabRunOffset(int tabPlacement, int tabCount,
 2011                                     int tabIndex, boolean forward) {
 2012           int run = getRunForTab(tabCount, tabIndex);
 2013           int offset;
 2014           switch(tabPlacement) {
 2015             case LEFT: {
 2016                 if (run == 0) {
 2017                     offset = (forward?
 2018                               -(calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth) :
 2019                               -maxTabWidth);
 2020   
 2021                 } else if (run == runCount - 1) {
 2022                     offset = (forward?
 2023                               maxTabWidth :
 2024                               calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth);
 2025                 } else {
 2026                     offset = (forward? maxTabWidth : -maxTabWidth);
 2027                 }
 2028                 break;
 2029             }
 2030             case RIGHT: {
 2031                 if (run == 0) {
 2032                     offset = (forward?
 2033                               maxTabWidth :
 2034                               calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth);
 2035                 } else if (run == runCount - 1) {
 2036                     offset = (forward?
 2037                               -(calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth) :
 2038                               -maxTabWidth);
 2039                 } else {
 2040                     offset = (forward? maxTabWidth : -maxTabWidth);
 2041                 }
 2042                 break;
 2043             }
 2044             case BOTTOM: {
 2045                 if (run == 0) {
 2046                     offset = (forward?
 2047                               maxTabHeight :
 2048                               calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight);
 2049                 } else if (run == runCount - 1) {
 2050                     offset = (forward?
 2051                               -(calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight) :
 2052                               -maxTabHeight);
 2053                 } else {
 2054                     offset = (forward? maxTabHeight : -maxTabHeight);
 2055                 }
 2056                 break;
 2057             }
 2058             case TOP:
 2059             default: {
 2060                 if (run == 0) {
 2061                     offset = (forward?
 2062                               -(calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight) :
 2063                               -maxTabHeight);
 2064                 } else if (run == runCount - 1) {
 2065                     offset = (forward?
 2066                               maxTabHeight :
 2067                               calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight);
 2068                 } else {
 2069                     offset = (forward? maxTabHeight : -maxTabHeight);
 2070                 }
 2071             }
 2072           }
 2073           return offset;
 2074       }
 2075   
 2076       protected int getPreviousTabIndex(int base) {
 2077           int tabIndex = (base - 1 >= 0? base - 1 : tabPane.getTabCount() - 1);
 2078           return (tabIndex >= 0? tabIndex : 0);
 2079       }
 2080   
 2081       protected int getNextTabIndex(int base) {
 2082           return (base+1)%tabPane.getTabCount();
 2083       }
 2084   
 2085       protected int getNextTabIndexInRun(int tabCount, int base) {
 2086           if (runCount < 2) {
 2087               return getNextTabIndex(base);
 2088           }
 2089           int currentRun = getRunForTab(tabCount, base);
 2090           int next = getNextTabIndex(base);
 2091           if (next == tabRuns[getNextTabRun(currentRun)]) {
 2092               return tabRuns[currentRun];
 2093           }
 2094           return next;
 2095       }
 2096   
 2097       protected int getPreviousTabIndexInRun(int tabCount, int base) {
 2098           if (runCount < 2) {
 2099               return getPreviousTabIndex(base);
 2100           }
 2101           int currentRun = getRunForTab(tabCount, base);
 2102           if (base == tabRuns[currentRun]) {
 2103               int previous = tabRuns[getNextTabRun(currentRun)]-1;
 2104               return (previous != -1? previous : tabCount-1);
 2105           }
 2106           return getPreviousTabIndex(base);
 2107       }
 2108   
 2109       protected int getPreviousTabRun(int baseRun) {
 2110           int runIndex = (baseRun - 1 >= 0? baseRun - 1 : runCount - 1);
 2111           return (runIndex >= 0? runIndex : 0);
 2112       }
 2113   
 2114       protected int getNextTabRun(int baseRun) {
 2115           return (baseRun+1)%runCount;
 2116       }
 2117   
 2118       protected static void rotateInsets(Insets topInsets, Insets targetInsets, int targetPlacement) {
 2119   
 2120           switch(targetPlacement) {
 2121             case LEFT:
 2122                 targetInsets.top = topInsets.left;
 2123                 targetInsets.left = topInsets.top;
 2124                 targetInsets.bottom = topInsets.right;
 2125                 targetInsets.right = topInsets.bottom;
 2126                 break;
 2127             case BOTTOM:
 2128                 targetInsets.top = topInsets.bottom;
 2129                 targetInsets.left = topInsets.left;
 2130                 targetInsets.bottom = topInsets.top;
 2131                 targetInsets.right = topInsets.right;
 2132                 break;
 2133             case RIGHT:
 2134                 targetInsets.top = topInsets.left;
 2135                 targetInsets.left = topInsets.bottom;
 2136                 targetInsets.bottom = topInsets.right;
 2137                 targetInsets.right = topInsets.top;
 2138                 break;
 2139             case TOP:
 2140             default:
 2141                 targetInsets.top = topInsets.top;
 2142                 targetInsets.left = topInsets.left;
 2143                 targetInsets.bottom = topInsets.bottom;
 2144                 targetInsets.right = topInsets.right;
 2145           }
 2146       }
 2147   
 2148       // REMIND(aim,7/29/98): This method should be made
 2149       // protected in the next release where
 2150       // API changes are allowed
 2151       boolean requestFocusForVisibleComponent() {
 2152           return SwingUtilities2.tabbedPaneChangeFocusTo(getVisibleComponent());
 2153       }
 2154   
 2155       private static class Actions extends UIAction {
 2156           final static String NEXT = "navigateNext";
 2157           final static String PREVIOUS = "navigatePrevious";
 2158           final static String RIGHT = "navigateRight";
 2159           final static String LEFT = "navigateLeft";
 2160           final static String UP = "navigateUp";
 2161           final static String DOWN = "navigateDown";
 2162           final static String PAGE_UP = "navigatePageUp";
 2163           final static String PAGE_DOWN = "navigatePageDown";
 2164           final static String REQUEST_FOCUS = "requestFocus";
 2165           final static String REQUEST_FOCUS_FOR_VISIBLE =
 2166                                       "requestFocusForVisibleComponent";
 2167           final static String SET_SELECTED = "setSelectedIndex";
 2168           final static String SELECT_FOCUSED = "selectTabWithFocus";
 2169           final static String SCROLL_FORWARD = "scrollTabsForwardAction";
 2170           final static String SCROLL_BACKWARD = "scrollTabsBackwardAction";
 2171   
 2172           Actions(String key) {
 2173               super(key);
 2174           }
 2175   
 2176           public void actionPerformed(ActionEvent e) {
 2177               String key = getName();
 2178               JTabbedPane pane = (JTabbedPane)e.getSource();
 2179               BasicTabbedPaneUI ui = (BasicTabbedPaneUI)BasicLookAndFeel.
 2180                          getUIOfType(pane.getUI(), BasicTabbedPaneUI.class);
 2181   
 2182               if (ui == null) {
 2183                   return;
 2184               }
 2185               if (key == NEXT) {
 2186                   ui.navigateSelectedTab(SwingConstants.NEXT);
 2187               }
 2188               else if (key == PREVIOUS) {
 2189                   ui.navigateSelectedTab(SwingConstants.PREVIOUS);
 2190               }
 2191               else if (key == RIGHT) {
 2192                   ui.navigateSelectedTab(SwingConstants.EAST);
 2193               }
 2194               else if (key == LEFT) {
 2195                   ui.navigateSelectedTab(SwingConstants.WEST);
 2196               }
 2197               else if (key == UP) {
 2198                   ui.navigateSelectedTab(SwingConstants.NORTH);
 2199               }
 2200               else if (key == DOWN) {
 2201                   ui.navigateSelectedTab(SwingConstants.SOUTH);
 2202               }
 2203               else if (key == PAGE_UP) {
 2204                   int tabPlacement = pane.getTabPlacement();
 2205                   if (tabPlacement == TOP|| tabPlacement == BOTTOM) {
 2206                       ui.navigateSelectedTab(SwingConstants.WEST);
 2207                   } else {
 2208                       ui.navigateSelectedTab(SwingConstants.NORTH);
 2209                   }
 2210               }
 2211               else if (key == PAGE_DOWN) {
 2212                   int tabPlacement = pane.getTabPlacement();
 2213                   if (tabPlacement == TOP || tabPlacement == BOTTOM) {
 2214                       ui.navigateSelectedTab(SwingConstants.EAST);
 2215                   } else {
 2216                       ui.navigateSelectedTab(SwingConstants.SOUTH);
 2217                   }
 2218               }
 2219               else if (key == REQUEST_FOCUS) {
 2220                   pane.requestFocus();
 2221               }
 2222               else if (key == REQUEST_FOCUS_FOR_VISIBLE) {
 2223                   ui.requestFocusForVisibleComponent();
 2224               }
 2225               else if (key == SET_SELECTED) {
 2226                   String command = e.getActionCommand();
 2227   
 2228                   if (command != null && command.length() > 0) {
 2229                       int mnemonic = (int)e.getActionCommand().charAt(0);
 2230                       if (mnemonic >= 'a' && mnemonic <='z') {
 2231                           mnemonic  -= ('a' - 'A');
 2232                       }
 2233                       Integer index = (Integer)ui.mnemonicToIndexMap.
 2234                                    get(Integer.valueOf(mnemonic));
 2235                       if (index != null && pane.isEnabledAt(index.intValue())) {
 2236                           pane.setSelectedIndex(index.intValue());
 2237                       }
 2238                   }
 2239               }
 2240               else if (key == SELECT_FOCUSED) {
 2241                   int focusIndex = ui.getFocusIndex();
 2242                   if (focusIndex != -1) {
 2243                       pane.setSelectedIndex(focusIndex);
 2244                   }
 2245               }
 2246               else if (key == SCROLL_FORWARD) {
 2247                   if (ui.scrollableTabLayoutEnabled()) {
 2248                       ui.tabScroller.scrollForward(pane.getTabPlacement());
 2249                   }
 2250               }
 2251               else if (key == SCROLL_BACKWARD) {
 2252                   if (ui.scrollableTabLayoutEnabled()) {
 2253                       ui.tabScroller.scrollBackward(pane.getTabPlacement());
 2254                   }
 2255               }
 2256           }
 2257       }
 2258   
 2259       /**
 2260        * This class should be treated as a &quot;protected&quot; inner class.
 2261        * Instantiate it only within subclasses of BasicTabbedPaneUI.
 2262        */
 2263       public class TabbedPaneLayout implements LayoutManager {
 2264   
 2265           public void addLayoutComponent(String name, Component comp) {}
 2266   
 2267           public void removeLayoutComponent(Component comp) {}
 2268   
 2269           public Dimension preferredLayoutSize(Container parent) {
 2270               return calculateSize(false);
 2271           }
 2272   
 2273           public Dimension minimumLayoutSize(Container parent) {
 2274               return calculateSize(true);
 2275           }
 2276   
 2277           protected Dimension calculateSize(boolean minimum) {
 2278               int tabPlacement = tabPane.getTabPlacement();
 2279               Insets insets = tabPane.getInsets();
 2280               Insets contentInsets = getContentBorderInsets(tabPlacement);
 2281               Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
 2282   
 2283               Dimension zeroSize = new Dimension(0,0);
 2284               int height = 0;
 2285               int width = 0;
 2286               int cWidth = 0;
 2287               int cHeight = 0;
 2288   
 2289               // Determine minimum size required to display largest
 2290               // child in each dimension
 2291               //
 2292               for (int i = 0; i < tabPane.getTabCount(); i++) {
 2293                   Component component = tabPane.getComponentAt(i);
 2294                   if (component != null) {
 2295                       Dimension size = zeroSize;
 2296                       size = minimum? component.getMinimumSize() :
 2297                                   component.getPreferredSize();
 2298   
 2299                       if (size != null) {
 2300                           cHeight = Math.max(size.height, cHeight);
 2301                           cWidth = Math.max(size.width, cWidth);
 2302                       }
 2303                   }
 2304               }
 2305               // Add content border insets to minimum size
 2306               width += cWidth;
 2307               height += cHeight;
 2308               int tabExtent = 0;
 2309   
 2310               // Calculate how much space the tabs will need, based on the
 2311               // minimum size required to display largest child + content border
 2312               //
 2313               switch(tabPlacement) {
 2314                 case LEFT:
 2315                 case RIGHT:
 2316                     height = Math.max(height, calculateMaxTabHeight(tabPlacement));
 2317                     tabExtent = preferredTabAreaWidth(tabPlacement, height - tabAreaInsets.top - tabAreaInsets.bottom);
 2318                     width += tabExtent;
 2319                     break;
 2320                 case TOP:
 2321                 case BOTTOM:
 2322                 default:
 2323                     width = Math.max(width, calculateMaxTabWidth(tabPlacement));
 2324                     tabExtent = preferredTabAreaHeight(tabPlacement, width - tabAreaInsets.left - tabAreaInsets.right);
 2325                     height += tabExtent;
 2326               }
 2327               return new Dimension(width + insets.left + insets.right + contentInsets.left + contentInsets.right,
 2328                                height + insets.bottom + insets.top + contentInsets.top + contentInsets.bottom);
 2329   
 2330           }
 2331   
 2332           protected int preferredTabAreaHeight(int tabPlacement, int width) {
 2333               FontMetrics metrics = getFontMetrics();
 2334               int tabCount = tabPane.getTabCount();
 2335               int total = 0;
 2336               if (tabCount > 0) {
 2337                   int rows = 1;
 2338                   int x = 0;
 2339   
 2340                   int maxTabHeight = calculateMaxTabHeight(tabPlacement);
 2341   
 2342                   for (int i = 0; i < tabCount; i++) {
 2343                       int tabWidth = calculateTabWidth(tabPlacement, i, metrics);
 2344   
 2345                       if (x != 0 && x + tabWidth > width) {
 2346                           rows++;
 2347                           x = 0;
 2348                       }
 2349                       x += tabWidth;
 2350                   }
 2351                   total = calculateTabAreaHeight(tabPlacement, rows, maxTabHeight);
 2352               }
 2353               return total;
 2354           }
 2355   
 2356           protected int preferredTabAreaWidth(int tabPlacement, int height) {
 2357               FontMetrics metrics = getFontMetrics();
 2358               int tabCount = tabPane.getTabCount();
 2359               int total = 0;
 2360               if (tabCount > 0) {
 2361                   int columns = 1;
 2362                   int y = 0;
 2363                   int fontHeight = metrics.getHeight();
 2364   
 2365                   maxTabWidth = calculateMaxTabWidth(tabPlacement);
 2366   
 2367                   for (int i = 0; i < tabCount; i++) {
 2368                       int tabHeight = calculateTabHeight(tabPlacement, i, fontHeight);
 2369   
 2370                       if (y != 0 && y + tabHeight > height) {
 2371                           columns++;
 2372                           y = 0;
 2373                       }
 2374                       y += tabHeight;
 2375                   }
 2376                   total = calculateTabAreaWidth(tabPlacement, columns, maxTabWidth);
 2377               }
 2378               return total;
 2379           }
 2380   
 2381           public void layoutContainer(Container parent) {
 2382               /* Some of the code in this method deals with changing the
 2383               * visibility of components to hide and show the contents for the
 2384               * selected tab. This is older code that has since been duplicated
 2385               * in JTabbedPane.fireStateChanged(), so as to allow visibility
 2386               * changes to happen sooner (see the note there). This code remains
 2387               * for backward compatibility as there are some cases, such as
 2388               * subclasses that don't fireStateChanged() where it may be used.
 2389               * Any changes here need to be kept in synch with
 2390               * JTabbedPane.fireStateChanged().
 2391               */
 2392   
 2393               setRolloverTab(-1);
 2394   
 2395               int tabPlacement = tabPane.getTabPlacement();
 2396               Insets insets = tabPane.getInsets();
 2397               int selectedIndex = tabPane.getSelectedIndex();
 2398               Component visibleComponent = getVisibleComponent();
 2399   
 2400               calculateLayoutInfo();
 2401   
 2402               Component selectedComponent = null;
 2403               if (selectedIndex < 0) {
 2404                   if (visibleComponent != null) {
 2405                       // The last tab was removed, so remove the component
 2406                       setVisibleComponent(null);
 2407                   }
 2408               } else {
 2409                   selectedComponent = tabPane.getComponentAt(selectedIndex);
 2410               }
 2411               int cx, cy, cw, ch;
 2412               int totalTabWidth = 0;
 2413               int totalTabHeight = 0;
 2414               Insets contentInsets = getContentBorderInsets(tabPlacement);
 2415   
 2416               boolean shouldChangeFocus = false;
 2417   
 2418               // In order to allow programs to use a single component
 2419               // as the display for multiple tabs, we will not change
 2420               // the visible compnent if the currently selected tab
 2421               // has a null component.  This is a bit dicey, as we don't
 2422               // explicitly state we support this in the spec, but since
 2423               // programs are now depending on this, we're making it work.
 2424               //
 2425               if(selectedComponent != null) {
 2426                   if(selectedComponent != visibleComponent &&
 2427                           visibleComponent != null) {
 2428                       if(SwingUtilities.findFocusOwner(visibleComponent) != null) {
 2429                           shouldChangeFocus = true;
 2430                       }
 2431                   }
 2432                   setVisibleComponent(selectedComponent);
 2433               }
 2434   
 2435               Rectangle bounds = tabPane.getBounds();
 2436               int numChildren = tabPane.getComponentCount();
 2437   
 2438               if(numChildren > 0) {
 2439   
 2440                   switch(tabPlacement) {
 2441                       case LEFT:
 2442                           totalTabWidth = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
 2443                           cx = insets.left + totalTabWidth + contentInsets.left;
 2444                           cy = insets.top + contentInsets.top;
 2445                           break;
 2446                       case RIGHT:
 2447                           totalTabWidth = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
 2448                           cx = insets.left + contentInsets.left;
 2449                           cy = insets.top + contentInsets.top;
 2450                           break;
 2451                       case BOTTOM:
 2452                           totalTabHeight = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
 2453                           cx = insets.left + contentInsets