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

    1   /*
    2    * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   package javax.swing.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<View> htmlViews;
  146   
  147       private Hashtable<Integer, Integer> 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           // Fix for 6711145 BasicTabbedPanuUI should not throw a NPE if these
  384           // keys are missing. So we are setting them to there default values here
  385           // if the keys are missing.
  386           if (tabInsets == null) tabInsets = new Insets(0,4,1,4);
  387           if (selectedTabPadInsets == null) selectedTabPadInsets = new Insets(2,2,2,1);
  388           if (tabAreaInsets == null) tabAreaInsets = new Insets(3,2,0,2);
  389           if (contentBorderInsets == null) contentBorderInsets = new Insets(2,2,3,3);
  390       }
  391   
  392       protected void uninstallDefaults() {
  393           highlight = null;
  394           lightHighlight = null;
  395           shadow = null;
  396           darkShadow = null;
  397           focus = null;
  398           tabInsets = null;
  399           selectedTabPadInsets = null;
  400           tabAreaInsets = null;
  401           contentBorderInsets = null;
  402       }
  403   
  404       protected void installListeners() {
  405           if ((propertyChangeListener = createPropertyChangeListener()) != null) {
  406               tabPane.addPropertyChangeListener(propertyChangeListener);
  407           }
  408           if ((tabChangeListener = createChangeListener()) != null) {
  409               tabPane.addChangeListener(tabChangeListener);
  410           }
  411           if ((mouseListener = createMouseListener()) != null) {
  412               tabPane.addMouseListener(mouseListener);
  413           }
  414           tabPane.addMouseMotionListener(getHandler());
  415           if ((focusListener = createFocusListener()) != null) {
  416               tabPane.addFocusListener(focusListener);
  417           }
  418           tabPane.addContainerListener(getHandler());
  419           if (tabPane.getTabCount()>0) {
  420               htmlViews = createHTMLVector();
  421           }
  422       }
  423   
  424       protected void uninstallListeners() {
  425           if (mouseListener != null) {
  426               tabPane.removeMouseListener(mouseListener);
  427               mouseListener = null;
  428           }
  429           tabPane.removeMouseMotionListener(getHandler());
  430           if (focusListener != null) {
  431               tabPane.removeFocusListener(focusListener);
  432               focusListener = null;
  433           }
  434   
  435           tabPane.removeContainerListener(getHandler());
  436           if (htmlViews!=null) {
  437               htmlViews.removeAllElements();
  438               htmlViews = null;
  439           }
  440           if (tabChangeListener != null) {
  441               tabPane.removeChangeListener(tabChangeListener);
  442               tabChangeListener = null;
  443           }
  444           if (propertyChangeListener != null) {
  445               tabPane.removePropertyChangeListener(propertyChangeListener);
  446               propertyChangeListener = null;
  447           }
  448           handler = null;
  449       }
  450   
  451       protected MouseListener createMouseListener() {
  452           return getHandler();
  453       }
  454   
  455       protected FocusListener createFocusListener() {
  456           return getHandler();
  457       }
  458   
  459       protected ChangeListener createChangeListener() {
  460           return getHandler();
  461       }
  462   
  463       protected PropertyChangeListener createPropertyChangeListener() {
  464           return getHandler();
  465       }
  466   
  467       private Handler getHandler() {
  468           if (handler == null) {
  469               handler = new Handler();
  470           }
  471           return handler;
  472       }
  473   
  474       protected void installKeyboardActions() {
  475           InputMap km = getInputMap(JComponent.
  476                                     WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  477   
  478           SwingUtilities.replaceUIInputMap(tabPane, JComponent.
  479                                            WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
  480                                            km);
  481           km = getInputMap(JComponent.WHEN_FOCUSED);
  482           SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, km);
  483   
  484           LazyActionMap.installLazyActionMap(tabPane, BasicTabbedPaneUI.class,
  485                                              "TabbedPane.actionMap");
  486           updateMnemonics();
  487       }
  488   
  489       InputMap getInputMap(int condition) {
  490           if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
  491               return (InputMap)DefaultLookup.get(tabPane, this,
  492                                                  "TabbedPane.ancestorInputMap");
  493           }
  494           else if (condition == JComponent.WHEN_FOCUSED) {
  495               return (InputMap)DefaultLookup.get(tabPane, this,
  496                                                  "TabbedPane.focusInputMap");
  497           }
  498           return null;
  499       }
  500   
  501       protected void uninstallKeyboardActions() {
  502           SwingUtilities.replaceUIActionMap(tabPane, null);
  503           SwingUtilities.replaceUIInputMap(tabPane, JComponent.
  504                                            WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
  505                                            null);
  506           SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED,
  507                                            null);
  508           SwingUtilities.replaceUIInputMap(tabPane,
  509                                            JComponent.WHEN_IN_FOCUSED_WINDOW,
  510                                            null);
  511           mnemonicToIndexMap = null;
  512           mnemonicInputMap = null;
  513       }
  514   
  515       /**
  516        * Reloads the mnemonics. This should be invoked when a memonic changes,
  517        * when the title of a mnemonic changes, or when tabs are added/removed.
  518        */
  519       private void updateMnemonics() {
  520           resetMnemonics();
  521           for (int counter = tabPane.getTabCount() - 1; counter >= 0;
  522                counter--) {
  523               int mnemonic = tabPane.getMnemonicAt(counter);
  524   
  525               if (mnemonic > 0) {
  526                   addMnemonic(counter, mnemonic);
  527               }
  528           }
  529       }
  530   
  531       /**
  532        * Resets the mnemonics bindings to an empty state.
  533        */
  534       private void resetMnemonics() {
  535           if (mnemonicToIndexMap != null) {
  536               mnemonicToIndexMap.clear();
  537               mnemonicInputMap.clear();
  538           }
  539       }
  540   
  541       /**
  542        * Adds the specified mnemonic at the specified index.
  543        */
  544       private void addMnemonic(int index, int mnemonic) {
  545           if (mnemonicToIndexMap == null) {
  546               initMnemonics();
  547           }
  548           mnemonicInputMap.put(KeyStroke.getKeyStroke(mnemonic, Event.ALT_MASK),
  549                                "setSelectedIndex");
  550           mnemonicToIndexMap.put(Integer.valueOf(mnemonic), Integer.valueOf(index));
  551       }
  552   
  553       /**
  554        * Installs the state needed for mnemonics.
  555        */
  556       private void initMnemonics() {
  557           mnemonicToIndexMap = new Hashtable<Integer, Integer>();
  558           mnemonicInputMap = new ComponentInputMapUIResource(tabPane);
  559           mnemonicInputMap.setParent(SwingUtilities.getUIInputMap(tabPane,
  560                                 JComponent.WHEN_IN_FOCUSED_WINDOW));
  561           SwingUtilities.replaceUIInputMap(tabPane,
  562                                 JComponent.WHEN_IN_FOCUSED_WINDOW,
  563                                            mnemonicInputMap);
  564       }
  565   
  566       /**
  567        * Sets the tab the mouse is over by location. This is a cover method
  568        * for <code>setRolloverTab(tabForCoordinate(x, y, false))</code>.
  569        */
  570       private void setRolloverTab(int x, int y) {
  571           // NOTE:
  572           // This calls in with false otherwise it could trigger a validate,
  573           // which should NOT happen if the user is only dragging the
  574           // mouse around.
  575           setRolloverTab(tabForCoordinate(tabPane, x, y, false));
  576       }
  577   
  578       /**
  579        * Sets the tab the mouse is currently over to <code>index</code>.
  580        * <code>index</code> will be -1 if the mouse is no longer over any
  581        * tab. No checking is done to ensure the passed in index identifies a
  582        * valid tab.
  583        *
  584        * @param index Index of the tab the mouse is over.
  585        * @since 1.5
  586        */
  587       protected void setRolloverTab(int index) {
  588           rolloverTabIndex = index;
  589       }
  590   
  591       /**
  592        * Returns the tab the mouse is currently over, or {@code -1} if the mouse is no
  593        * longer over any tab.
  594        *
  595        * @return the tab the mouse is currently over, or {@code -1} if the mouse is no
  596        * longer over any tab
  597        * @since 1.5
  598        */
  599       protected int getRolloverTab() {
  600           return rolloverTabIndex;
  601       }
  602   
  603       public Dimension getMinimumSize(JComponent c) {
  604           // Default to LayoutManager's minimumLayoutSize
  605           return null;
  606       }
  607   
  608       public Dimension getMaximumSize(JComponent c) {
  609           // Default to LayoutManager's maximumLayoutSize
  610           return null;
  611       }
  612   
  613       /**
  614        * Returns the baseline.
  615        *
  616        * @throws NullPointerException {@inheritDoc}
  617        * @throws IllegalArgumentException {@inheritDoc}
  618        * @see javax.swing.JComponent#getBaseline(int, int)
  619        * @since 1.6
  620        */
  621       public int getBaseline(JComponent c, int width, int height) {
  622           super.getBaseline(c, width, height);
  623           int baseline = calculateBaselineIfNecessary();
  624           if (baseline != -1) {
  625               int placement = tabPane.getTabPlacement();
  626               Insets insets = tabPane.getInsets();
  627               Insets tabAreaInsets = getTabAreaInsets(placement);
  628               switch(placement) {
  629               case JTabbedPane.TOP:
  630                   baseline += insets.top + tabAreaInsets.top;
  631                   return baseline;
  632               case JTabbedPane.BOTTOM:
  633                   baseline = height - insets.bottom -
  634                       tabAreaInsets.bottom - maxTabHeight + baseline;
  635                   return baseline;
  636               case JTabbedPane.LEFT:
  637               case JTabbedPane.RIGHT:
  638                   baseline += insets.top + tabAreaInsets.top;
  639                   return baseline;
  640               }
  641           }
  642           return -1;
  643       }
  644   
  645       /**
  646        * Returns an enum indicating how the baseline of the component
  647        * changes as the size changes.
  648        *
  649        * @throws NullPointerException {@inheritDoc}
  650        * @see javax.swing.JComponent#getBaseline(int, int)
  651        * @since 1.6
  652        */
  653       public Component.BaselineResizeBehavior getBaselineResizeBehavior(
  654               JComponent c) {
  655           super.getBaselineResizeBehavior(c);
  656           switch(tabPane.getTabPlacement()) {
  657           case JTabbedPane.LEFT:
  658           case JTabbedPane.RIGHT:
  659           case JTabbedPane.TOP:
  660               return Component.BaselineResizeBehavior.CONSTANT_ASCENT;
  661           case JTabbedPane.BOTTOM:
  662               return Component.BaselineResizeBehavior.CONSTANT_DESCENT;
  663           }
  664           return Component.BaselineResizeBehavior.OTHER;
  665       }
  666   
  667       /**
  668        * Returns the baseline for the specified tab.
  669        *
  670        * @param tab index of tab to get baseline for
  671        * @exception IndexOutOfBoundsException if index is out of range
  672        *            (index < 0 || index >= tab count)
  673        * @return baseline or a value &lt; 0 indicating there is no reasonable
  674        *                  baseline
  675        * @since 1.6
  676        */
  677       protected int getBaseline(int tab) {
  678           if (tabPane.getTabComponentAt(tab) != null) {
  679               int offset = getBaselineOffset();
  680               if (offset != 0) {
  681                   // The offset is not applied to the tab component, and so
  682                   // in general we can't get good alignment like with components
  683                   // in the tab.
  684                   return -1;
  685               }
  686               Component c = tabPane.getTabComponentAt(tab);
  687               Dimension pref = c.getPreferredSize();
  688               Insets tabInsets = getTabInsets(tabPane.getTabPlacement(), tab);
  689               int cellHeight = maxTabHeight - tabInsets.top - tabInsets.bottom;
  690               return c.getBaseline(pref.width, pref.height) +
  691                       (cellHeight - pref.height) / 2 + tabInsets.top;
  692           }
  693           else {
  694               View view = getTextViewForTab(tab);
  695               if (view != null) {
  696                   int viewHeight = (int)view.getPreferredSpan(View.Y_AXIS);
  697                   int baseline = BasicHTML.getHTMLBaseline(
  698                       view, (int)view.getPreferredSpan(View.X_AXIS), viewHeight);
  699                   if (baseline >= 0) {
  700                       return maxTabHeight / 2 - viewHeight / 2 + baseline +
  701                           getBaselineOffset();
  702                   }
  703                   return -1;
  704               }
  705           }
  706           FontMetrics metrics = getFontMetrics();
  707           int fontHeight = metrics.getHeight();
  708           int fontBaseline = metrics.getAscent();
  709           return maxTabHeight / 2 - fontHeight / 2 + fontBaseline +
  710                   getBaselineOffset();
  711       }
  712   
  713       /**
  714        * Returns the amount the baseline is offset by.  This is typically
  715        * the same as <code>getTabLabelShiftY</code>.
  716        *
  717        * @return amount to offset the baseline by
  718        * @since 1.6
  719        */
  720       protected int getBaselineOffset() {
  721           switch(tabPane.getTabPlacement()) {
  722           case JTabbedPane.TOP:
  723               if (tabPane.getTabCount() > 1) {
  724                   return 1;
  725               }
  726               else {
  727                   return -1;
  728               }
  729           case JTabbedPane.BOTTOM:
  730               if (tabPane.getTabCount() > 1) {
  731                   return -1;
  732               }
  733               else {
  734                   return 1;
  735               }
  736           default: // RIGHT|LEFT
  737               return (maxTabHeight % 2);
  738           }
  739       }
  740   
  741       private int calculateBaselineIfNecessary() {
  742           if (!calculatedBaseline) {
  743               calculatedBaseline = true;
  744               baseline = -1;
  745               if (tabPane.getTabCount() > 0) {
  746                   calculateBaseline();
  747               }
  748           }
  749           return baseline;
  750       }
  751   
  752       private void calculateBaseline() {
  753           int tabCount = tabPane.getTabCount();
  754           int tabPlacement = tabPane.getTabPlacement();
  755           maxTabHeight = calculateMaxTabHeight(tabPlacement);
  756           baseline = getBaseline(0);
  757           if (isHorizontalTabPlacement()) {
  758               for(int i = 1; i < tabCount; i++) {
  759                   if (getBaseline(i) != baseline) {
  760                       baseline = -1;
  761                       break;
  762                   }
  763               }
  764           }
  765           else {
  766               // left/right, tabs may be different sizes.
  767               FontMetrics fontMetrics = getFontMetrics();
  768               int fontHeight = fontMetrics.getHeight();
  769               int height = calculateTabHeight(tabPlacement, 0, fontHeight);
  770               for(int i = 1; i < tabCount; i++) {
  771                   int newHeight = calculateTabHeight(tabPlacement, i,fontHeight);
  772                   if (height != newHeight) {
  773                       // assume different baseline
  774                       baseline = -1;
  775                       break;
  776                   }
  777               }
  778           }
  779       }
  780   
  781   // UI Rendering
  782   
  783       public void paint(Graphics g, JComponent c) {
  784           int selectedIndex = tabPane.getSelectedIndex();
  785           int tabPlacement = tabPane.getTabPlacement();
  786   
  787           ensureCurrentLayout();
  788   
  789           // Paint content border and tab area
  790           if (tabsOverlapBorder) {
  791               paintContentBorder(g, tabPlacement, selectedIndex);
  792           }
  793           // If scrollable tabs are enabled, the tab area will be
  794           // painted by the scrollable tab panel instead.
  795           //
  796           if (!scrollableTabLayoutEnabled()) { // WRAP_TAB_LAYOUT
  797               paintTabArea(g, tabPlacement, selectedIndex);
  798           }
  799           if (!tabsOverlapBorder) {
  800               paintContentBorder(g, tabPlacement, selectedIndex);
  801           }
  802       }
  803   
  804       /**
  805        * Paints the tabs in the tab area.
  806        * Invoked by paint().
  807        * The graphics parameter must be a valid <code>Graphics</code>
  808        * object.  Tab placement may be either:
  809        * <code>JTabbedPane.TOP</code>, <code>JTabbedPane.BOTTOM</code>,
  810        * <code>JTabbedPane.LEFT</code>, or <code>JTabbedPane.RIGHT</code>.
  811        * The selected index must be a valid tabbed pane tab index (0 to
  812        * tab count - 1, inclusive) or -1 if no tab is currently selected.
  813        * The handling of invalid parameters is unspecified.
  814        *
  815        * @param g the graphics object to use for rendering
  816        * @param tabPlacement the placement for the tabs within the JTabbedPane
  817        * @param selectedIndex the tab index of the selected component
  818        *
  819        * @since 1.4
  820        */
  821       protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex) {
  822           int tabCount = tabPane.getTabCount();
  823   
  824           Rectangle iconRect = new Rectangle(),
  825                     textRect = new Rectangle();
  826           Rectangle clipRect = g.getClipBounds();
  827   
  828           // Paint tabRuns of tabs from back to front
  829           for (int i = runCount - 1; i >= 0; i--) {
  830               int start = tabRuns[i];
  831               int next = tabRuns[(i == runCount - 1)? 0 : i + 1];
  832               int end = (next != 0? next - 1: tabCount - 1);
  833               for (int j = start; j <= end; j++) {
  834                   if (j != selectedIndex && rects[j].intersects(clipRect)) {
  835                       paintTab(g, tabPlacement, rects, j, iconRect, textRect);
  836                   }
  837               }
  838           }
  839   
  840           // Paint selected tab if its in the front run
  841           // since it may overlap other tabs
  842           if (selectedIndex >= 0 && rects[selectedIndex].intersects(clipRect)) {
  843               paintTab(g, tabPlacement, rects, selectedIndex, iconRect, textRect);
  844           }
  845       }
  846   
  847       protected void paintTab(Graphics g, int tabPlacement,
  848                               Rectangle[] rects, int tabIndex,
  849                               Rectangle iconRect, Rectangle textRect) {
  850           Rectangle tabRect = rects[tabIndex];
  851           int selectedIndex = tabPane.getSelectedIndex();
  852           boolean isSelected = selectedIndex == tabIndex;
  853   
  854           if (tabsOpaque || tabPane.isOpaque()) {
  855               paintTabBackground(g, tabPlacement, tabIndex, tabRect.x, tabRect.y,
  856                       tabRect.width, tabRect.height, isSelected);
  857           }
  858   
  859           paintTabBorder(g, tabPlacement, tabIndex, tabRect.x, tabRect.y,
  860                          tabRect.width, tabRect.height, isSelected);
  861   
  862           String title = tabPane.getTitleAt(tabIndex);
  863           Font font = tabPane.getFont();
  864           FontMetrics metrics = SwingUtilities2.getFontMetrics(tabPane, g, font);
  865           Icon icon = getIconForTab(tabIndex);
  866   
  867           layoutLabel(tabPlacement, metrics, tabIndex, title, icon,
  868                       tabRect, iconRect, textRect, isSelected);
  869   
  870           if (tabPane.getTabComponentAt(tabIndex) == null) {
  871               String clippedTitle = title;
  872   
  873               if (scrollableTabLayoutEnabled() && tabScroller.croppedEdge.isParamsSet() &&
  874                       tabScroller.croppedEdge.getTabIndex() == tabIndex && isHorizontalTabPlacement()) {
  875                   int availTextWidth = tabScroller.croppedEdge.getCropline() -
  876                           (textRect.x - tabRect.x) - tabScroller.croppedEdge.getCroppedSideWidth();
  877                   clippedTitle = SwingUtilities2.clipStringIfNecessary(null, metrics, title, availTextWidth);
  878               }
  879   
  880               paintText(g, tabPlacement, font, metrics,
  881                       tabIndex, clippedTitle, textRect, isSelected);
  882   
  883               paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected);
  884           }
  885           paintFocusIndicator(g, tabPlacement, rects, tabIndex,
  886                     iconRect, textRect, isSelected);
  887       }
  888   
  889       private boolean isHorizontalTabPlacement() {
  890           return tabPane.getTabPlacement() == TOP || tabPane.getTabPlacement() == BOTTOM;
  891       }
  892   
  893       /* This method will create and return a polygon shape for the given tab rectangle
  894        * which has been cropped at the specified cropline with a torn edge visual.
  895        * e.g. A "File" tab which has cropped been cropped just after the "i":
  896        *             -------------
  897        *             |  .....     |
  898        *             |  .          |
  899        *             |  ...  .    |
  900        *             |  .    .   |
  901        *             |  .    .    |
  902        *             |  .    .     |
  903        *             --------------
  904        *
  905        * The x, y arrays below define the pattern used to create a "torn" edge
  906        * segment which is repeated to fill the edge of the tab.
  907        * For tabs placed on TOP and BOTTOM, this righthand torn edge is created by
  908        * line segments which are defined by coordinates obtained by
  909        * subtracting xCropLen[i] from (tab.x + tab.width) and adding yCroplen[i]
  910        * to (tab.y).
  911        * For tabs placed on LEFT or RIGHT, the bottom torn edge is created by
  912        * subtracting xCropLen[i] from (tab.y + tab.height) and adding yCropLen[i]
  913        * to (tab.x).
  914        */
  915       private static int xCropLen[] = {1,1,0,0,1,1,2,2};
  916       private static int yCropLen[] = {0,3,3,6,6,9,9,12};
  917       private static final int CROP_SEGMENT = 12;
  918   
  919       private static Polygon createCroppedTabShape(int tabPlacement, Rectangle tabRect, int cropline) {
  920           int rlen;
  921           int start;
  922           int end;
  923           int ostart;
  924   
  925           switch(tabPlacement) {
  926             case LEFT:
  927             case RIGHT:
  928                 rlen = tabRect.width;
  929                 start = tabRect.x;
  930                 end = tabRect.x + tabRect.width;
  931                 ostart = tabRect.y + tabRect.height;
  932                 break;
  933             case TOP:
  934             case BOTTOM:
  935             default:
  936                rlen = tabRect.height;
  937                start = tabRect.y;
  938                end = tabRect.y + tabRect.height;
  939                ostart = tabRect.x + tabRect.width;
  940           }
  941           int rcnt = rlen/CROP_SEGMENT;
  942           if (rlen%CROP_SEGMENT > 0) {
  943               rcnt++;
  944           }
  945           int npts = 2 + (rcnt*8);
  946           int xp[] = new int[npts];
  947           int yp[] = new int[npts];
  948           int pcnt = 0;
  949   
  950           xp[pcnt] = ostart;
  951           yp[pcnt++] = end;
  952           xp[pcnt] = ostart;
  953           yp[pcnt++] = start;
  954           for(int i = 0; i < rcnt; i++) {
  955               for(int j = 0; j < xCropLen.length; j++) {
  956                   xp[pcnt] = cropline - xCropLen[j];
  957                   yp[pcnt] = start + (i*CROP_SEGMENT) + yCropLen[j];
  958                   if (yp[pcnt] >= end) {
  959                       yp[pcnt] = end;
  960                       pcnt++;
  961                       break;
  962                   }
  963                   pcnt++;
  964               }
  965           }
  966           if (tabPlacement == JTabbedPane.TOP || tabPlacement == JTabbedPane.BOTTOM) {
  967              return new Polygon(xp, yp, pcnt);
  968   
  969           } else { // LEFT or RIGHT
  970              return new Polygon(yp, xp, pcnt);
  971           }
  972       }
  973   
  974       /* If tabLayoutPolicy == SCROLL_TAB_LAYOUT, this method will paint an edge
  975        * indicating the tab is cropped in the viewport display
  976        */
  977       private void paintCroppedTabEdge(Graphics g) {
  978           int tabIndex = tabScroller.croppedEdge.getTabIndex();
  979           int cropline = tabScroller.croppedEdge.getCropline();
  980           int x,y;
  981           switch(tabPane.getTabPlacement()) {
  982             case LEFT:
  983             case RIGHT:
  984               x = rects[tabIndex].x;
  985               y = cropline;
  986               int xx = x;
  987               g.setColor(shadow);
  988               while(xx <= x+rects[tabIndex].width) {
  989                   for (int i=0; i < xCropLen.length; i+=2) {
  990                       g.drawLine(xx+yCropLen[i],y-xCropLen[i],
  991                                  xx+yCropLen[i+1]-1,y-xCropLen[i+1]);
  992                   }
  993                   xx+=CROP_SEGMENT;
  994               }
  995               break;
  996             case TOP:
  997             case BOTTOM:
  998             default:
  999               x = cropline;
 1000               y = rects[tabIndex].y;
 1001               int yy = y;
 1002               g.setColor(shadow);
 1003               while(yy <= y+rects[tabIndex].height) {
 1004                   for (int i=0; i < xCropLen.length; i+=2) {
 1005                       g.drawLine(x-xCropLen[i],yy+yCropLen[i],
 1006                                  x-xCropLen[i+1],yy+yCropLen[i+1]-1);
 1007                   }
 1008                   yy+=CROP_SEGMENT;
 1009               }
 1010           }
 1011       }
 1012   
 1013       protected void layoutLabel(int tabPlacement,
 1014                                  FontMetrics metrics, int tabIndex,
 1015                                  String title, Icon icon,
 1016                                  Rectangle tabRect, Rectangle iconRect,
 1017                                  Rectangle textRect, boolean isSelected ) {
 1018           textRect.x = textRect.y = iconRect.x = iconRect.y = 0;
 1019   
 1020           View v = getTextViewForTab(tabIndex);
 1021           if (v != null) {
 1022               tabPane.putClientProperty("html", v);
 1023           }
 1024   
 1025           SwingUtilities.layoutCompoundLabel(tabPane,
 1026                                              metrics, title, icon,
 1027                                              SwingUtilities.CENTER,
 1028                                              SwingUtilities.CENTER,
 1029                                              SwingUtilities.CENTER,
 1030                                              SwingUtilities.TRAILING,
 1031                                              tabRect,
 1032                                              iconRect,
 1033                                              textRect,
 1034                                              textIconGap);
 1035   
 1036           tabPane.putClientProperty("html", null);
 1037   
 1038           int xNudge = getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
 1039           int yNudge = getTabLabelShiftY(tabPlacement, tabIndex, isSelected);
 1040           iconRect.x += xNudge;
 1041           iconRect.y += yNudge;
 1042           textRect.x += xNudge;
 1043           textRect.y += yNudge;
 1044       }
 1045   
 1046       protected void paintIcon(Graphics g, int tabPlacement,
 1047                                int tabIndex, Icon icon, Rectangle iconRect,
 1048                                boolean isSelected ) {
 1049           if (icon != null) {
 1050               icon.paintIcon(tabPane, g, iconRect.x, iconRect.y);
 1051           }
 1052       }
 1053   
 1054       protected void paintText(Graphics g, int tabPlacement,
 1055                                Font font, FontMetrics metrics, int tabIndex,
 1056                                String title, Rectangle textRect,
 1057                                boolean isSelected) {
 1058   
 1059           g.setFont(font);
 1060   
 1061           View v = getTextViewForTab(tabIndex);
 1062           if (v != null) {
 1063               // html
 1064               v.paint(g, textRect);
 1065           } else {
 1066               // plain text
 1067               int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
 1068   
 1069               if (tabPane.isEnabled() && tabPane.isEnabledAt(tabIndex)) {
 1070                   Color fg = tabPane.getForegroundAt(tabIndex);
 1071                   if (isSelected && (fg instanceof UIResource)) {
 1072                       Color selectedFG = UIManager.getColor(
 1073                                     "TabbedPane.selectedForeground");
 1074                       if (selectedFG != null) {
 1075                           fg = selectedFG;
 1076                       }
 1077                   }
 1078                   g.setColor(fg);
 1079                   SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
 1080                                title, mnemIndex,
 1081                                textRect.x, textRect.y + metrics.getAscent());
 1082   
 1083               } else { // tab disabled
 1084                   g.setColor(tabPane.getBackgroundAt(tabIndex).brighter());
 1085                   SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
 1086                                title, mnemIndex,
 1087                                textRect.x, textRect.y + metrics.getAscent());
 1088                   g.setColor(tabPane.getBackgroundAt(tabIndex).darker());
 1089                   SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
 1090                                title, mnemIndex,
 1091                                textRect.x - 1, textRect.y + metrics.getAscent() - 1);
 1092   
 1093               }
 1094           }
 1095       }
 1096   
 1097   
 1098       protected int getTabLabelShiftX(int tabPlacement, int tabIndex, boolean isSelected) {
 1099           Rectangle tabRect = rects[tabIndex];
 1100           String propKey = (isSelected ? "selectedLabelShift" : "labelShift");
 1101           int nudge = DefaultLookup.getInt(
 1102                   tabPane, this, "TabbedPane." + propKey, 1);
 1103   
 1104           switch (tabPlacement) {
 1105               case LEFT:
 1106                   return nudge;
 1107               case RIGHT:
 1108                   return -nudge;
 1109               case BOTTOM:
 1110               case TOP:
 1111               default:
 1112                   return tabRect.width % 2;
 1113           }
 1114       }
 1115   
 1116       protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) {
 1117           Rectangle tabRect = rects[tabIndex];
 1118           String propKey = (isSelected ? "selectedLabelShift" : "labelShift");
 1119           int nudge = DefaultLookup.getInt(
 1120                   tabPane, this, "TabbedPane." + propKey, 1);
 1121   
 1122           switch (tabPlacement) {
 1123               case BOTTOM:
 1124                   return -nudge;
 1125               case LEFT:
 1126               case RIGHT:
 1127                   return tabRect.height % 2;
 1128               case TOP:
 1129               default:
 1130                   return nudge;
 1131           }
 1132       }
 1133   
 1134       protected void paintFocusIndicator(Graphics g, int tabPlacement,
 1135                                          Rectangle[] rects, int tabIndex,
 1136                                          Rectangle iconRect, Rectangle textRect,
 1137                                          boolean isSelected) {
 1138           Rectangle tabRect = rects[tabIndex];
 1139           if (tabPane.hasFocus() && isSelected) {
 1140               int x, y, w, h;
 1141               g.setColor(focus);
 1142               switch(tabPlacement) {
 1143                 case LEFT:
 1144                     x = tabRect.x + 3;
 1145                     y = tabRect.y + 3;
 1146                     w = tabRect.width - 5;
 1147                     h = tabRect.height - 6;
 1148                     break;
 1149                 case RIGHT:
 1150                     x = tabRect.x + 2;
 1151                     y = tabRect.y + 3;
 1152                     w = tabRect.width - 5;
 1153                     h = tabRect.height - 6;
 1154                     break;
 1155                 case BOTTOM:
 1156                     x = tabRect.x + 3;
 1157                     y = tabRect.y + 2;
 1158                     w = tabRect.width - 6;
 1159                     h = tabRect.height - 5;
 1160                     break;
 1161                 case TOP:
 1162                 default:
 1163                     x = tabRect.x + 3;
 1164                     y = tabRect.y + 3;
 1165                     w = tabRect.width - 6;
 1166                     h = tabRect.height - 5;
 1167               }
 1168               BasicGraphicsUtils.drawDashedRect(g, x, y, w, h);
 1169           }
 1170       }
 1171   
 1172       /**
 1173         * this function draws the border around each tab
 1174         * note that this function does now draw the background of the tab.
 1175         * that is done elsewhere
 1176         */
 1177       protected void paintTabBorder(Graphics g, int tabPlacement,
 1178                                     int tabIndex,
 1179                                     int x, int y, int w, int h,
 1180                                     boolean isSelected ) {
 1181           g.setColor(lightHighlight);
 1182   
 1183           switch (tabPlacement) {
 1184             case LEFT:
 1185                 g.drawLine(x+1, y+h-2, x+1, y+h-2); // bottom-left highlight
 1186                 g.drawLine(x, y+2, x, y+h-3); // left highlight
 1187                 g.drawLine(x+1, y+1, x+1, y+1); // top-left highlight
 1188                 g.drawLine(x+2, y, x+w-1, y); // top highlight
 1189   
 1190                 g.setColor(shadow);
 1191                 g.drawLine(x+2, y+h-2, x+w-1, y+h-2); // bottom shadow
 1192   
 1193                 g.setColor(darkShadow);
 1194                 g.drawLine(x+2, y+h-1, x+w-1, y+h-1); // bottom dark shadow
 1195                 break;
 1196             case RIGHT:
 1197                 g.drawLine(x, y, x+w-3, y); // top highlight
 1198   
 1199                 g.setColor(shadow);
 1200                 g.drawLine(x, y+h-2, x+w-3, y+h-2); // bottom shadow
 1201                 g.drawLine(x+w-2, y+2, x+w-2, y+h-3); // right shadow
 1202   
 1203                 g.setColor(darkShadow);
 1204                 g.drawLine(x+w-2, y+1, x+w-2, y+1); // top-right dark shadow
 1205                 g.drawLine(x+w-2, y+h-2, x+w-2, y+h-2); // bottom-right dark shadow
 1206                 g.drawLine(x+w-1, y+2, x+w-1, y+h-3); // right dark shadow
 1207                 g.drawLine(x, y+h-1, x+w-3, y+h-1); // bottom dark shadow
 1208                 break;
 1209             case BOTTOM:
 1210                 g.drawLine(x, y, x, y+h-3); // left highlight
 1211                 g.drawLine(x+1, y+h-2, x+1, y+h-2); // bottom-left highlight
 1212   
 1213                 g.setColor(shadow);
 1214                 g.drawLine(x+2, y+h-2, x+w-3, y+h-2); // bottom shadow
 1215                 g.drawLine(x+w-2, y, x+w-2, y+h-3); // right shadow
 1216   
 1217                 g.setColor(darkShadow);
 1218                 g.drawLine(x+2, y+h-1, x+w-3, y+h-1); // bottom dark shadow
 1219                 g.drawLine(x+w-2, y+h-2, x+w-2, y+h-2); // bottom-right dark shadow
 1220                 g.drawLine(x+w-1, y, x+w-1, y+h-3); // right dark shadow
 1221                 break;
 1222             case TOP:
 1223             default:
 1224                 g.drawLine(x, y+2, x, y+h-1); // left highlight
 1225                 g.drawLine(x+1, y+1, x+1, y+1); // top-left highlight
 1226                 g.drawLine(x+2, y, x+w-3, y); // top highlight
 1227   
 1228                 g.setColor(shadow);
 1229                 g.drawLine(x+w-2, y+2, x+w-2, y+h-1); // right shadow
 1230   
 1231                 g.setColor(darkShadow);
 1232                 g.drawLine(x+w-1, y+2, x+w-1, y+h-1); // right dark-shadow
 1233                 g.drawLine(x+w-2, y+1, x+w-2, y+1); // top-right shadow
 1234           }
 1235       }
 1236   
 1237       protected void paintTabBackground(Graphics g, int tabPlacement,
 1238                                         int tabIndex,
 1239                                         int x, int y, int w, int h,
 1240                                         boolean isSelected ) {
 1241           g.setColor(!isSelected || selectedColor == null?
 1242                      tabPane.getBackgroundAt(tabIndex) : selectedColor);
 1243           switch(tabPlacement) {
 1244             case LEFT:
 1245                 g.fillRect(x+1, y+1, w-1, h-3);
 1246                 break;
 1247             case RIGHT:
 1248                 g.fillRect(x, y+1, w-2, h-3);
 1249                 break;
 1250             case BOTTOM:
 1251                 g.fillRect(x+1, y, w-3, h-1);
 1252                 break;
 1253             case TOP:
 1254             default:
 1255                 g.fillRect(x+1, y+1, w-3, h-1);
 1256           }
 1257       }
 1258   
 1259       protected void paintContentBorder(Graphics g, int tabPlacement, int selectedIndex) {
 1260           int width = tabPane.getWidth();
 1261           int height = tabPane.getHeight();
 1262           Insets insets = tabPane.getInsets();
 1263           Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
 1264   
 1265           int x = insets.left;
 1266           int y = insets.top;
 1267           int w = width - insets.right - insets.left;
 1268           int h = height - insets.top - insets.bottom;
 1269   
 1270           switch(tabPlacement) {
 1271             case LEFT:
 1272                 x += calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
 1273                 if (tabsOverlapBorder) {
 1274                     x -= tabAreaInsets.right;
 1275                 }
 1276                 w -= (x - insets.left);
 1277                 break;
 1278             case RIGHT:
 1279                 w -= calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
 1280                 if (tabsOverlapBorder) {
 1281                     w += tabAreaInsets.left;
 1282                 }
 1283                 break;
 1284             case BOTTOM:
 1285                 h -= calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
 1286                 if (tabsOverlapBorder) {
 1287                     h += tabAreaInsets.top;
 1288                 }
 1289                 break;
 1290             case TOP:
 1291             default:
 1292                 y += calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
 1293                 if (tabsOverlapBorder) {
 1294                     y -= tabAreaInsets.bottom;
 1295                 }
 1296                 h -= (y - insets.top);
 1297           }
 1298   
 1299               if ( tabPane.getTabCount() > 0 && (contentOpaque || tabPane.isOpaque()) ) {
 1300               // Fill region behind content area
 1301               Color color = UIManager.getColor("TabbedPane.contentAreaColor");
 1302               if (color != null) {
 1303                   g.setColor(color);
 1304               }
 1305               else if ( selectedColor == null || selectedIndex == -1 ) {
 1306                   g.setColor(tabPane.getBackground());
 1307               }
 1308               else {
 1309                   g.setColor(selectedColor);
 1310               }
 1311               g.fillRect(x,y,w,h);
 1312           }
 1313   
 1314           paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h);
 1315           paintContentBorderLeftEdge(g, tabPlacement, selectedIndex, x, y, w, h);
 1316           paintContentBorderBottomEdge(g, tabPlacement, selectedIndex, x, y, w, h);
 1317           paintContentBorderRightEdge(g, tabPlacement, selectedIndex, x, y, w, h);
 1318   
 1319       }
 1320   
 1321       protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
 1322                                            int selectedIndex,
 1323                                            int x, int y, int w, int h) {
 1324           Rectangle selRect = selectedIndex < 0? null :
 1325                                  getTabBounds(selectedIndex, calcRect);
 1326   
 1327           g.setColor(lightHighlight);
 1328   
 1329           // Draw unbroken line if tabs are not on TOP, OR
 1330           // selected tab is not in run adjacent to content, OR
 1331           // selected tab is not visible (SCROLL_TAB_LAYOUT)
 1332           //
 1333           if (tabPlacement != TOP || selectedIndex < 0 ||
 1334               (selRect.y + selRect.height + 1 < y) ||
 1335               (selRect.x < x || selRect.x > x + w)) {
 1336               g.drawLine(x, y, x+w-2, y);
 1337           } else {
 1338               // Break line to show visual connection to selected tab
 1339               g.drawLine(x, y, selRect.x - 1, y);
 1340               if (selRect.x + selRect.width < x + w - 2) {
 1341                   g.drawLine(selRect.x + selRect.width, y,
 1342                              x+w-2, y);
 1343               } else {
 1344                   g.setColor(shadow);
 1345                   g.drawLine(x+w-2, y, x+w-2, y);
 1346               }
 1347           }
 1348       }
 1349   
 1350       protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
 1351                                                  int selectedIndex,
 1352                                                  int x, int y, int w, int h) {
 1353           Rectangle selRect = selectedIndex < 0? null :
 1354                                  getTabBounds(selectedIndex, calcRect);
 1355   
 1356           g.setColor(lightHighlight);
 1357   
 1358           // Draw unbroken line if tabs are not on LEFT, OR
 1359           // selected tab is not in run adjacent to content, OR
 1360           // selected tab is not visible (SCROLL_TAB_LAYOUT)
 1361           //
 1362           if (tabPlacement != LEFT || selectedIndex < 0 ||
 1363               (selRect.x + selRect.width + 1 < x) ||
 1364               (selRect.y < y || selRect.y > y + h)) {
 1365               g.drawLine(x, y, x, y+h-2);
 1366           } else {
 1367               // Break line to show visual connection to selected tab
 1368               g.drawLine(x, y, x, selRect.y - 1);
 1369               if (selRect.y + selRect.height < y + h - 2) {
 1370                   g.drawLine(x, selRect.y + selRect.height,
 1371                              x, y+h-2);
 1372               }
 1373           }
 1374       }
 1375   
 1376       protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
 1377                                                  int selectedIndex,
 1378                                                  int x, int y, int w, int h) {
 1379           Rectangle selRect = selectedIndex < 0? null :
 1380                                  getTabBounds(selectedIndex, calcRect);
 1381   
 1382           g.setColor(shadow);
 1383   
 1384           // Draw unbroken line if tabs are not on BOTTOM, OR
 1385           // selected tab is not in run adjacent to content, OR
 1386           // selected tab is not visible (SCROLL_TAB_LAYOUT)
 1387           //
 1388           if (tabPlacement != BOTTOM || selectedIndex < 0 ||
 1389                (selRect.y - 1 > h) ||
 1390                (selRect.x < x || selRect.x > x + w)) {
 1391               g.drawLine(x+1, y+h-2, x+w-2, y+h-2);
 1392               g.setColor(darkShadow);
 1393               g.drawLine(x, y+h-1, x+w-1, y+h-1);
 1394           } else {
 1395               // Break line to show visual connection to selected tab
 1396               g.drawLine(x+1, y+h-2, selRect.x - 1, y+h-2);
 1397               g.setColor(darkShadow);
 1398               g.drawLine(x, y+h-1, selRect.x - 1, y+h-1);
 1399               if (selRect.x + selRect.width < x + w - 2) {
 1400                   g.setColor(shadow);
 1401                   g.drawLine(selRect.x + selRect.width, y+h-2, x+w-2, y+h-2);
 1402                   g.setColor(darkShadow);
 1403                   g.drawLine(selRect.x + selRect.width, y+h-1, x+w-1, y+h-1);
 1404               }
 1405           }
 1406   
 1407       }
 1408   
 1409       protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
 1410                                                  int selectedIndex,
 1411                                                  int x, int y, int w, int h) {
 1412           Rectangle selRect = selectedIndex < 0? null :
 1413                                  getTabBounds(selectedIndex, calcRect);
 1414   
 1415           g.setColor(shadow);
 1416   
 1417           // Draw unbroken line if tabs are not on RIGHT, OR
 1418           // selected tab is not in run adjacent to content, OR
 1419           // selected tab is not visible (SCROLL_TAB_LAYOUT)
 1420           //
 1421           if (tabPlacement != RIGHT || selectedIndex < 0 ||
 1422                (selRect.x - 1 > w) ||
 1423                (selRect.y < y || selRect.y > y + h)) {
 1424               g.drawLine(x+w-2, y+1, x+w-2, y+h-3);
 1425               g.setColor(darkShadow);
 1426               g.drawLine(x+w-1, y, x+w-1, y+h-1);
 1427           } else {
 1428               // Break line to show visual connection to selected tab
 1429               g.drawLine(x+w-2, y+1, x+w-2, selRect.y - 1);
 1430               g.setColor(darkShadow);
 1431               g.drawLine(x+w-1, y, x+w-1, selRect.y - 1);
 1432   
 1433               if (selRect.y + selRect.height < y + h - 2) {
 1434                   g.setColor(shadow);
 1435                   g.drawLine(x+w-2, selRect.y + selRect.height,
 1436                              x+w-2, y+h-2);
 1437                   g.setColor(darkShadow);
 1438                   g.drawLine(x+w-1, selRect.y + selRect.height,
 1439                              x+w-1, y+h-2);
 1440               }
 1441           }
 1442       }
 1443   
 1444       private void ensureCurrentLayout() {
 1445           if (!tabPane.isValid()) {
 1446               tabPane.validate();
 1447           }
 1448           /* If tabPane doesn't have a peer yet, the validate() call will
 1449            * silently fail.  We handle that by forcing a layout if tabPane
 1450            * is still invalid.  See bug 4237677.
 1451            */
 1452           if (!tabPane.isValid()) {
 1453               TabbedPaneLayout layout = (TabbedPaneLayout)tabPane.getLayout();
 1454               layout.calculateLayoutInfo();
 1455           }
 1456       }
 1457   
 1458   
 1459   // TabbedPaneUI methods
 1460   
 1461       /**
 1462        * Returns the bounds of the specified tab index.  The bounds are
 1463        * with respect to the JTabbedPane's coordinate space.
 1464        */
 1465       public Rectangle getTabBounds(JTabbedPane pane, int i) {
 1466           ensureCurrentLayout();
 1467           Rectangle tabRect = new Rectangle();
 1468           return getTabBounds(i, tabRect);
 1469       }
 1470   
 1471       public int getTabRunCount(JTabbedPane pane) {
 1472           ensureCurrentLayout();
 1473           return runCount;
 1474       }
 1475   
 1476       /**
 1477        * Returns the tab index which intersects the specified point
 1478        * in the JTabbedPane's coordinate space.
 1479        */
 1480       public int tabForCoordinate(JTabbedPane pane, int x, int y) {
 1481           return tabForCoordinate(pane, x, y, true);
 1482       }
 1483   
 1484       private int tabForCoordinate(JTabbedPane pane, int x, int y,
 1485                                    boolean validateIfNecessary) {
 1486           if (validateIfNecessary) {
 1487               ensureCurrentLayout();
 1488           }
 1489           if (isRunsDirty) {
 1490               // We didn't recalculate the layout, runs and tabCount may not
 1491               // line up, bail.
 1492               return -1;
 1493           }
 1494           Point p = new Point(x, y);
 1495   
 1496           if (scrollableTabLayoutEnabled()) {
 1497               translatePointToTabPanel(x, y, p);
 1498               Rectangle viewRect = tabScroller.viewport.getViewRect();
 1499               if (!viewRect.contains(p)) {
 1500                   return -1;
 1501               }
 1502           }
 1503           int tabCount = tabPane.getTabCount();
 1504           for (int i = 0; i < tabCount; i++) {
 1505               if (rects[i].contains(p.x, p.y)) {
 1506                   return i;
 1507               }
 1508           }
 1509           return -1;
 1510       }
 1511   
 1512       /**
 1513        * Returns the bounds of the specified tab in the coordinate space
 1514        * of the JTabbedPane component.  This is required because the tab rects
 1515        * are by default defined in the coordinate space of the component where
 1516        * they are rendered, which could be the JTabbedPane
 1517        * (for WRAP_TAB_LAYOUT) or a ScrollableTabPanel (SCROLL_TAB_LAYOUT).
 1518        * This method should be used whenever the tab rectangle must be relative
 1519        * to the JTabbedPane itself and the result should be placed in a
 1520        * designated Rectangle object (rather than instantiating and returning
 1521        * a new Rectangle each time). The tab index parameter must be a valid
 1522        * tabbed pane tab index (0 to tab count - 1, inclusive).  The destination
 1523        * rectangle parameter must be a valid <code>Rectangle</code> instance.
 1524        * The handling of invalid parameters is unspecified.
 1525        *
 1526        * @param tabIndex the index of the tab
 1527        * @param dest the rectangle where the result should be placed
 1528        * @return the resulting rectangle
 1529        *
 1530        * @since 1.4
 1531        */
 1532       protected Rectangle getTabBounds(int tabIndex, Rectangle dest) {
 1533           dest.width = rects[tabIndex].width;
 1534           dest.height = rects[tabIndex].height;
 1535   
 1536           if (scrollableTabLayoutEnabled()) { // SCROLL_TAB_LAYOUT
 1537               // Need to translate coordinates based on viewport location &
 1538               // view position
 1539               Point vpp = tabScroller.viewport.getLocation();
 1540               Point viewp = tabScroller.viewport.getViewPosition();
 1541               dest.x = rects[tabIndex].x + vpp.x - viewp.x;
 1542               dest.y = rects[tabIndex].y + vpp.y - viewp.y;
 1543   
 1544           } else { // WRAP_TAB_LAYOUT
 1545               dest.x = rects[tabIndex].x;
 1546               dest.y = rects[tabIndex].y;
 1547           }
 1548           return dest;
 1549       }
 1550   
 1551       /**
 1552        * Returns the index of the tab closest to the passed in location, note
 1553        * that the returned tab may not contain the location x,y.
 1554        */
 1555       private int getClosestTab(int x, int y) {
 1556           int min = 0;
 1557           int tabCount = Math.min(rects.length, tabPane.getTabCount());
 1558           int max = tabCount;
 1559           int tabPlacement = tabPane.getTabPlacement();
 1560           boolean useX = (tabPlacement == TOP || tabPlacement == BOTTOM);
 1561           int want = (useX) ? x : y;
 1562   
 1563           while (min != max) {
 1564               int current = (max + min) / 2;
 1565               int minLoc;
 1566               int maxLoc;
 1567   
 1568               if (useX) {
 1569                   minLoc = rects[current].x;
 1570                   maxLoc = minLoc + rects[current].width;
 1571               }
 1572               else {
 1573                   minLoc = rects[current].y;
 1574                   maxLoc = minLoc + rects[current].height;
 1575               }
 1576               if (want < minLoc) {
 1577                   max = current;
 1578                   if (min == max) {
 1579                       return Math.max(0, current - 1);
 1580                   }
 1581               }
 1582               else if (want >= maxLoc) {
 1583                   min = current;
 1584                   if (max - min <= 1) {
 1585                       return Math.max(current + 1, tabCount - 1);
 1586                   }
 1587               }
 1588               else {
 1589                   return current;
 1590               }
 1591           }
 1592           return min;
 1593       }
 1594   
 1595       /**
 1596        * Returns a point which is translated from the specified point in the
 1597        * JTabbedPane's coordinate space to the coordinate space of the
 1598        * ScrollableTabPanel.  This is used for SCROLL_TAB_LAYOUT ONLY.
 1599        */
 1600       private Point translatePointToTabPanel(int srcx, int srcy, Point dest) {
 1601           Point vpp = tabScroller.viewport.getLocation();
 1602           Point viewp = tabScroller.viewport.getViewPosition();
 1603           dest.x = srcx - vpp.x + viewp.x;
 1604           dest.y = srcy - vpp.y + viewp.y;
 1605           return dest;
 1606       }
 1607   
 1608   // BasicTabbedPaneUI methods
 1609   
 1610       protected Component getVisibleComponent() {
 1611           return visibleComponent;
 1612       }
 1613   
 1614       protected void setVisibleComponent(Component component) {
 1615           if (visibleComponent != null
 1616                   && visibleComponent != component
 1617                   && visibleComponent.getParent() == tabPane
 1618                   && visibleComponent.isVisible()) {
 1619   
 1620               visibleComponent.setVisible(false);
 1621           }
 1622           if (component != null && !component.isVisible()) {
 1623               component.setVisible(true);
 1624           }
 1625           visibleComponent = component;
 1626       }
 1627   
 1628       protected void assureRectsCreated(int tabCount) {
 1629           int rectArrayLen = rects.length;
 1630           if (tabCount != rectArrayLen ) {
 1631               Rectangle[] tempRectArray = new Rectangle[tabCount];
 1632               System.arraycopy(rects, 0, tempRectArray, 0,
 1633                                Math.min(rectArrayLen, tabCount));
 1634               rects = tempRectArray;
 1635               for (int rectIndex = rectArrayLen; rectIndex < tabCount; rectIndex++) {
 1636                   rects[rectIndex] = new Rectangle();
 1637               }
 1638           }
 1639   
 1640       }
 1641   
 1642       protected void expandTabRunsArray() {
 1643           int rectLen = tabRuns.length;
 1644           int[] newArray = new int[rectLen+10];
 1645           System.arraycopy(tabRuns, 0, newArray, 0, runCount);
 1646           tabRuns = newArray;
 1647       }
 1648   
 1649       protected int getRunForTab(int tabCount, int tabIndex) {
 1650           for (int i = 0; i < runCount; i++) {
 1651               int first = tabRuns[i];
 1652               int last = lastTabInRun(tabCount, i);
 1653               if (tabIndex >= first && tabIndex <= last) {
 1654                   return i;
 1655               }
 1656           }
 1657           return 0;
 1658       }
 1659   
 1660       protected int lastTabInRun(int tabCount, int run) {
 1661           if (runCount == 1) {
 1662               return tabCount - 1;
 1663           }
 1664           int nextRun = (run == runCount - 1? 0 : run + 1);
 1665           if (tabRuns[nextRun] == 0) {
 1666               return tabCount - 1;
 1667           }
 1668           return tabRuns[nextRun]-1;
 1669       }
 1670   
 1671       protected int getTabRunOverlay(int tabPlacement) {
 1672           return tabRunOverlay;
 1673       }
 1674   
 1675       protected int getTabRunIndent(int tabPlacement, int run) {
 1676           return 0;
 1677       }
 1678   
 1679       protected boolean shouldPadTabRun(int tabPlacement, int run) {
 1680           return runCount > 1;
 1681       }
 1682   
 1683       protected boolean shouldRotateTabRuns(int tabPlacement) {
 1684           return true;
 1685       }
 1686   
 1687       protected Icon getIconForTab(int tabIndex) {
 1688           return (!tabPane.isEnabled() || !tabPane.isEnabledAt(tabIndex))?
 1689                             tabPane.getDisabledIconAt(tabIndex) : tabPane.getIconAt(tabIndex);
 1690       }
 1691   
 1692       /**
 1693        * Returns the text View object required to render stylized text (HTML) for
 1694        * the specified tab or null if no specialized text rendering is needed
 1695        * for this tab. This is provided to support html rendering inside tabs.
 1696        *
 1697        * @param tabIndex the index of the tab
 1698        * @return the text view to render the tab's text or null if no
 1699        *         specialized rendering is required
 1700        *
 1701        * @since 1.4
 1702        */
 1703       protected View getTextViewForTab(int tabIndex) {
 1704           if (htmlViews != null) {
 1705               return htmlViews.elementAt(tabIndex);
 1706           }
 1707           return null;
 1708       }
 1709   
 1710       protected int calculateTabHeight(int tabPlacement, int tabIndex, int fontHeight) {
 1711           int height = 0;
 1712           Component c = tabPane.getTabComponentAt(tabIndex);
 1713           if (c != null) {
 1714               height = c.getPreferredSize().height;
 1715           } else {
 1716               View v = getTextViewForTab(tabIndex);
 1717               if (v != null) {
 1718                   // html
 1719                   height += (int) v.getPreferredSpan(View.Y_AXIS);
 1720               } else {
 1721                   // plain text
 1722                   height += fontHeight;
 1723               }
 1724               Icon icon = getIconForTab(tabIndex);
 1725   
 1726               if (icon != null) {
 1727                   height = Math.max(height, icon.getIconHeight());
 1728               }
 1729           }
 1730           Insets tabInsets = getTabInsets(tabPlacement, tabIndex);
 1731           height += tabInsets.top + tabInsets.bottom + 2;
 1732           return height;
 1733       }
 1734   
 1735       protected int calculateMaxTabHeight(int tabPlacement) {
 1736           FontMetrics metrics = getFontMetrics();
 1737           int tabCount = tabPane.getTabCount();
 1738           int result = 0;
 1739           int fontHeight = metrics.getHeight();
 1740           for(int i = 0; i < tabCount; i++) {
 1741               result = Math.max(calculateTabHeight(tabPlacement, i, fontHeight), result);
 1742           }
 1743           return result;
 1744       }
 1745   
 1746       protected int calculateTabWidth(int tabPlacement, int tabIndex, FontMetrics metrics) {
 1747           Insets tabInsets = getTabInsets(tabPlacement, tabIndex);
 1748           int width = tabInsets.left + tabInsets.right + 3;
 1749           Component tabComponent = tabPane.getTabComponentAt(tabIndex);
 1750           if (tabComponent != null) {
 1751               width += tabComponent.getPreferredSize().width;
 1752           } else {
 1753               Icon icon = getIconForTab(tabIndex);
 1754               if (icon != null) {
 1755                   width += icon.getIconWidth() + textIconGap;
 1756               }
 1757               View v = getTextViewForTab(tabIndex);
 1758               if (v != null) {
 1759                   // html
 1760                   width += (int) v.getPreferredSpan(View.X_AXIS);
 1761               } else {
 1762                   // plain text
 1763                   String title = tabPane.getTitleAt(tabIndex);
 1764                   width += SwingUtilities2.stringWidth(tabPane, metrics, title);
 1765               }
 1766           }
 1767           return width;
 1768       }
 1769   
 1770       protected int calculateMaxTabWidth(int tabPlacement) {
 1771           FontMetrics metrics = getFontMetrics();
 1772           int tabCount = tabPane.getTabCount();
 1773           int result = 0;
 1774           for(int i = 0; i < tabCount; i++) {
 1775               result = Math.max(calculateTabWidth(tabPlacement, i, metrics), result);
 1776           }
 1777           return result;
 1778       }
 1779   
 1780       protected int calculateTabAreaHeight(int tabPlacement, int horizRunCount, int maxTabHeight) {
 1781           Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
 1782           int tabRunOverlay = getTabRunOverlay(tabPlacement);
 1783           return (horizRunCount > 0?
 1784                   horizRunCount * (maxTabHeight-tabRunOverlay) + tabRunOverlay +
 1785                   tabAreaInsets.top + tabAreaInsets.bottom :
 1786                   0);
 1787       }
 1788   
 1789       protected int calculateTabAreaWidth(int tabPlacement, int vertRunCount, int maxTabWidth) {
 1790           Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
 1791           int tabRunOverlay = getTabRunOverlay(tabPlacement);
 1792           return (vertRunCount > 0?
 1793                   vertRunCount * (maxTabWidth-tabRunOverlay) + tabRunOverlay +
 1794                   tabAreaInsets.left + tabAreaInsets.right :
 1795                   0);
 1796       }
 1797   
 1798       protected Insets getTabInsets(int tabPlacement, int tabIndex) {
 1799           return tabInsets;
 1800       }
 1801   
 1802       protected Insets getSelectedTabPadInsets(int tabPlacement) {
 1803           rotateInsets(selectedTabPadInsets, currentPadInsets, tabPlacement);
 1804           return currentPadInsets;
 1805       }
 1806   
 1807       protected Insets getTabAreaInsets(int tabPlacement) {
 1808           rotateInsets(tabAreaInsets, currentTabAreaInsets, tabPlacement);
 1809           return currentTabAreaInsets;
 1810       }
 1811   
 1812       protected Insets getContentBorderInsets(int tabPlacement) {
 1813           return contentBorderInsets;
 1814       }
 1815   
 1816       protected FontMetrics getFontMetrics() {
 1817           Font font = tabPane.getFont();
 1818           return tabPane.getFontMetrics(font);
 1819       }
 1820   
 1821   
 1822   // Tab Navigation methods
 1823   
 1824       protected void navigateSelectedTab(int direction) {
 1825           int tabPlacement = tabPane.getTabPlacement();
 1826           int current = DefaultLookup.getBoolean(tabPane, this,
 1827                                "TabbedPane.selectionFollowsFocus", true) ?
 1828                                tabPane.getSelectedIndex() : getFocusIndex();
 1829           int tabCount = tabPane.getTabCount();
 1830           boolean leftToRight = BasicGraphicsUtils.isLeftToRight(tabPane);
 1831   
 1832           // If we have no tabs then don't navigate.
 1833           if (tabCount <= 0) {
 1834               return;
 1835           }
 1836   
 1837           int offset;
 1838           switch(tabPlacement) {
 1839             case LEFT:
 1840             case RIGHT:
 1841                 switch(direction) {
 1842                    case NEXT:
 1843                        selectNextTab(current);
 1844                        break;
 1845                    case PREVIOUS:
 1846                        selectPreviousTab(current);
 1847                        break;
 1848                   case NORTH:
 1849                       selectPreviousTabInRun(current);
 1850                       break;
 1851                   case SOUTH:
 1852                       selectNextTabInRun(current);
 1853                       break;
 1854                   case WEST:
 1855                       offset = getTabRunOffset(tabPlacement, tabCount, current, false);
 1856                       selectAdjacentRunTab(tabPlacement, current, offset);
 1857                       break;
 1858                   case EAST:
 1859                       offset = getTabRunOffset(tabPlacement, tabCount, current, true);
 1860                       selectAdjacentRunTab(tabPlacement, current, offset);
 1861                       break;
 1862                   default:
 1863                 }
 1864                 break;
 1865             case BOTTOM:
 1866             case TOP:
 1867             default:
 1868                 switch(direction) {
 1869                   case NEXT:
 1870                       selectNextTab(current);
 1871                       break;
 1872                   case PREVIOUS:
 1873                       selectPreviousTab(current);
 1874                       break;
 1875                   case NORTH:
 1876                       offset = getTabRunOffset(tabPlacement, tabCount, current, false);
 1877                       selectAdjacentRunTab(tabPlacement, current, offset);
 1878                       break;
 1879                   case SOUTH:
 1880                       offset = getTabRunOffset(tabPlacement, tabCount, current, true);
 1881                       selectAdjacentRunTab(tabPlacement, current, offset);
 1882                       break;
 1883                   case EAST:
 1884                       if (leftToRight) {
 1885                           selectNextTabInRun(current);
 1886                       } else {
 1887                           selectPreviousTabInRun(current);
 1888                       }
 1889                       break;
 1890                   case WEST:
 1891                       if (leftToRight) {
 1892                           selectPreviousTabInRun(current);
 1893                       } else {
 1894                           selectNextTabInRun(current);
 1895                       }
 1896                       break;
 1897                   default:
 1898                 }
 1899           }
 1900       }
 1901   
 1902       protected void selectNextTabInRun(int current) {
 1903           int tabCount = tabPane.getTabCount();
 1904           int tabIndex = getNextTabIndexInRun(tabCount, current);
 1905   
 1906           while(tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
 1907               tabIndex = getNextTabIndexInRun(tabCount, tabIndex);
 1908           }
 1909           navigateTo(tabIndex);
 1910       }
 1911   
 1912       protected void selectPreviousTabInRun(int current) {
 1913           int tabCount = tabPane.getTabCount();
 1914           int tabIndex = getPreviousTabIndexInRun(tabCount, current);
 1915   
 1916           while(tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
 1917               tabIndex = getPreviousTabIndexInRun(tabCount, tabIndex);
 1918           }
 1919           navigateTo(tabIndex);
 1920       }
 1921   
 1922       protected void selectNextTab(int current) {
 1923           int tabIndex = getNextTabIndex(current);
 1924   
 1925           while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
 1926               tabIndex = getNextTabIndex(tabIndex);
 1927           }
 1928           navigateTo(tabIndex);
 1929       }
 1930   
 1931       protected void selectPreviousTab(int current) {
 1932           int tabIndex = getPreviousTabIndex(current);
 1933   
 1934           while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
 1935               tabIndex = getPreviousTabIndex(tabIndex);
 1936           }
 1937           navigateTo(tabIndex);
 1938       }
 1939   
 1940       protected void selectAdjacentRunTab(int tabPlacement,
 1941                                           int tabIndex, int offset) {
 1942           if ( runCount < 2 ) {
 1943               return;
 1944           }
 1945           int newIndex;
 1946           Rectangle r = rects[tabIndex];
 1947           switch(tabPlacement) {
 1948             case LEFT:
 1949             case RIGHT:
 1950                 newIndex = tabForCoordinate(tabPane, r.x + r.width/2 + offset,
 1951                                          r.y + r.height/2);
 1952                 break;
 1953             case BOTTOM:
 1954             case TOP:
 1955             default:
 1956                 newIndex = tabForCoordinate(tabPane, r.x + r.width/2,
 1957                                          r.y + r.height/2 + offset);
 1958           }
 1959           if (newIndex != -1) {
 1960               while (!tabPane.isEnabledAt(newIndex) && newIndex != tabIndex) {
 1961                   newIndex = getNextTabIndex(newIndex);
 1962               }
 1963               navigateTo(newIndex);
 1964           }
 1965       }
 1966   
 1967       private void navigateTo(int index) {
 1968           if (DefaultLookup.getBoolean(tabPane, this,
 1969                                "TabbedPane.selectionFollowsFocus", true)) {
 1970               tabPane.setSelectedIndex(index);
 1971           } else {
 1972               // Just move focus (not selection)
 1973               setFocusIndex(index, true);
 1974           }
 1975       }
 1976   
 1977       void setFocusIndex(int index, boolean repaint) {
 1978           if (repaint && !isRunsDirty) {
 1979               repaintTab(focusIndex);
 1980               focusIndex = index;
 1981               repaintTab(focusIndex);
 1982           }
 1983           else {
 1984               focusIndex = index;
 1985           }
 1986       }
 1987   
 1988       /**
 1989        * Repaints the specified tab.
 1990        */
 1991       private void repaintTab(int index) {
 1992           // If we're not valid that means we will shortly be validated and
 1993           // painted, which means we don't have to do anything here.
 1994           if (!isRunsDirty && index >= 0 && index < tabPane.getTabCount()) {
 1995               tabPane.repaint(getTabBounds(tabPane, index));
 1996           }
 1997       }
 1998   
 1999       /**
 2000        * Makes sure the focusIndex is valid.
 2001        */
 2002       private void validateFocusIndex() {
 2003           if (focusIndex >= tabPane.getTabCount()) {
 2004               setFocusIndex(tabPane.getSelectedIndex(), false);
 2005           }
 2006       }
 2007   
 2008       /**
 2009        * Returns the index of the tab that has focus.
 2010        *
 2011        * @return index of tab that has focus
 2012        * @since 1.5
 2013        */
 2014       protected int getFocusIndex() {
 2015           return focusIndex;
 2016       }
 2017   
 2018       protected int getTabRunOffset(int tabPlacement, int tabCount,
 2019                                     int tabIndex, boolean forward) {
 2020           int run = getRunForTab(tabCount, tabIndex);
 2021           int offset;
 2022           switch(tabPlacement) {
 2023             case LEFT: {
 2024                 if (run == 0) {
 2025                     offset = (forward?
 2026                               -(calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth) :
 2027                               -maxTabWidth);
 2028   
 2029                 } else if (run == runCount - 1) {
 2030                     offset = (forward?
 2031                               maxTabWidth :
 2032                               calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth);
 2033                 } else {
 2034                     offset = (forward? maxTabWidth : -maxTabWidth);
 2035                 }
 2036                 break;
 2037             }
 2038             case RIGHT: {
 2039                 if (run == 0) {
 2040                     offset = (forward?
 2041                               maxTabWidth :
 2042                               calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth);
 2043                 } else if (run == runCount - 1) {
 2044                     offset = (forward?
 2045                               -(calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth) :
 2046                               -maxTabWidth);
 2047                 } else {
 2048                     offset = (forward? maxTabWidth : -maxTabWidth);
 2049                 }
 2050                 break;
 2051             }
 2052             case BOTTOM: {
 2053                 if (run == 0) {
 2054                     offset = (forward?
 2055                               maxTabHeight :
 2056                               calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight);
 2057                 } else if (run == runCount - 1) {
 2058                     offset = (forward?
 2059                               -(calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight) :
 2060                               -maxTabHeight);
 2061                 } else {
 2062                     offset = (forward? maxTabHeight : -maxTabHeight);
 2063                 }
 2064                 break;
 2065             }
 2066             case TOP:
 2067             default: {
 2068                 if (run == 0) {
 2069                     offset = (forward?
 2070                               -(calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight) :
 2071                               -maxTabHeight);
 2072                 } else if (run == runCount - 1) {
 2073                     offset = (forward?
 2074                               maxTabHeight :
 2075                               calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight);
 2076                 } else {
 2077                     offset = (forward? maxTabHeight : -maxTabHeight);
 2078                 }
 2079             }
 2080           }
 2081           return offset;
 2082       }
 2083   
 2084       protected int getPreviousTabIndex(int base) {
 2085           int tabIndex = (base - 1 >= 0? base - 1 : tabPane.getTabCount() - 1);
 2086           return (tabIndex >= 0? tabIndex : 0);
 2087       }
 2088   
 2089       protected int getNextTabIndex(int base) {
 2090           return (base+1)%tabPane.getTabCount();
 2091       }
 2092   
 2093       protected int getNextTabIndexInRun(int tabCount, int base) {
 2094           if (runCount < 2) {
 2095               return getNextTabIndex(base);
 2096           }
 2097           int currentRun = getRunForTab(tabCount, base);
 2098           int next = getNextTabIndex(base);
 2099           if (next == tabRuns[getNextTabRun(currentRun)]) {
 2100               return tabRuns[currentRun];
 2101           }
 2102           return next;
 2103       }
 2104   
 2105       protected int getPreviousTabIndexInRun(int tabCount, int base) {
 2106           if (runCount < 2) {
 2107               return getPreviousTabIndex(base);
 2108           }
 2109           int currentRun = getRunForTab(tabCount, base);
 2110           if (base == tabRuns[currentRun]) {
 2111               int previous = tabRuns[getNextTabRun(currentRun)]-1;
 2112               return (previous != -1? previous : tabCount-1);
 2113           }
 2114           return getPreviousTabIndex(base);
 2115       }
 2116   
 2117       protected int getPreviousTabRun(int baseRun) {
 2118           int runIndex = (baseRun - 1 >= 0? baseRun - 1 : runCount - 1);
 2119           return (runIndex >= 0? runIndex : 0);
 2120       }
 2121   
 2122       protected int getNextTabRun(int baseRun) {
 2123           return (baseRun+1)%runCount;
 2124       }
 2125   
 2126       protected static void rotateInsets(Insets topInsets, Insets targetInsets, int targetPlacement) {
 2127   
 2128           switch(targetPlacement) {
 2129             case LEFT:
 2130                 targetInsets.top = topInsets.left;
 2131                 targetInsets.left = topInsets.top;
 2132                 targetInsets.bottom = topInsets.right;
 2133                 targetInsets.right = topInsets.bottom;
 2134                 break;
 2135             case BOTTOM:
 2136                 targetInsets.top = topInsets.bottom;
 2137                 targetInsets.left = topInsets.left;
 2138                 targetInsets.bottom = topInsets.top;
 2139                 targetInsets.right = topInsets.right;
 2140                 break;
 2141             case RIGHT:
 2142                 targetInsets.top = topInsets.left;
 2143                 targetInsets.left = topInsets.bottom;
 2144                 targetInsets.bottom = topInsets.right;
 2145                 targetInsets.right = topInsets.top;
 2146                 break;
 2147             case TOP:
 2148             default:
 2149                 targetInsets.top = topInsets.top;
 2150                 targetInsets.left = topInsets.left;
 2151                 targetInsets.bottom = topInsets.bottom;
 2152                 targetInsets.right = topInsets.right;
 2153           }
 2154       }
 2155   
 2156       // REMIND(aim,7/29/98): This method should be made
 2157       // protected in the next release where
 2158       // API changes are allowed
 2159       boolean requestFocusForVisibleComponent() {
 2160           return SwingUtilities2.tabbedPaneChangeFocusTo(getVisibleComponent());
 2161       }
 2162   
 2163       private static class Actions extends UIAction {
 2164           final static String NEXT = "navigateNext";
 2165           final static String PREVIOUS = "navigatePrevious";
 2166           final static String RIGHT = "navigateRight";
 2167           final static String LEFT = "navigateLeft";
 2168           final static String UP = "navigateUp";
 2169           final static String DOWN = "navigateDown";
 2170           final static String PAGE_UP = "navigatePageUp";
 2171           final static String PAGE_DOWN = "navigatePageDown";
 2172           final static String REQUEST_FOCUS = "requestFocus";
 2173           final static String REQUEST_FOCUS_FOR_VISIBLE =
 2174                                       "requestFocusForVisibleComponent";
 2175           final static String SET_SELECTED = "setSelectedIndex";
 2176           final static String SELECT_FOCUSED = "selectTabWithFocus";
 2177           final static String SCROLL_FORWARD = "scrollTabsForwardAction";
 2178           final static String SCROLL_BACKWARD = "scrollTabsBackwardAction";
 2179   
 2180           Actions(String key) {
 2181               super(key);
 2182           }
 2183   
 2184           public void actionPerformed(ActionEvent e) {
 2185               String key = getName();
 2186               JTabbedPane pane = (JTabbedPane)e.getSource();
 2187               BasicTabbedPaneUI ui = (BasicTabbedPaneUI)BasicLookAndFeel.
 2188                          getUIOfType(pane.getUI(), BasicTabbedPaneUI.class);
 2189   
 2190               if (ui == null) {
 2191                   return;
 2192               }
 2193               if (key == NEXT) {
 2194                   ui.navigateSelectedTab(SwingConstants.NEXT);
 2195               }
 2196               else if (key == PREVIOUS) {
 2197                   ui.navigateSelectedTab(SwingConstants.PREVIOUS);
 2198               }
 2199               else if (key == RIGHT) {
 2200                   ui.navigateSelectedTab(SwingConstants.EAST);
 2201               }
 2202               else if (key == LEFT) {
 2203                   ui.navigateSelectedTab(SwingConstants.WEST);
 2204               }
 2205               else if (key == UP) {
 2206                   ui.navigateSelectedTab(SwingConstants.NORTH);
 2207               }
 2208               else if (key == DOWN) {
 2209                   ui.navigateSelectedTab(SwingConstants.SOUTH);
 2210               }
 2211               else if (key == PAGE_UP) {
 2212                   int tabPlacement = pane.getTabPlacement();
 2213                   if (tabPlacement == TOP|| tabPlacement == BOTTOM) {
 2214                       ui.navigateSelectedTab(SwingConstants.WEST);
 2215                   } else {
 2216                       ui.navigateSelectedTab(SwingConstants.NORTH);
 2217                   }
 2218               }
 2219               else if (key == PAGE_DOWN) {
 2220                   int tabPlacement = pane.getTabPlacement();
 2221                   if (tabPlacement == TOP || tabPlacement == BOTTOM) {
 2222                       ui.navigateSelectedTab(SwingConstants.EAST);
 2223                   } else {
 2224                       ui.navigateSelectedTab(SwingConstants.SOUTH);
 2225                   }
 2226               }
 2227               else if (key == REQUEST_FOCUS) {
 2228                   pane.requestFocus();
 2229               }
 2230               else if (key == REQUEST_FOCUS_FOR_VISIBLE) {
 2231                   ui.requestFocusForVisibleComponent();
 2232               }
 2233               else if (key == SET_SELECTED) {
 2234                   String command = e.getActionCommand();
 2235   
 2236                   if (command != null && command.length() > 0) {
 2237                       int mnemonic = (int)e.getActionCommand().charAt(0);
 2238                       if (mnemonic >= 'a' && mnemonic <='z') {
 2239                           mnemonic  -= ('a' - 'A');
 2240                       }
 2241                       Integer index = ui.mnemonicToIndexMap.get(Integer.valueOf(mnemonic));
 2242                       if (index != null && pane.isEnabledAt(index.intValue())) {
 2243                           pane.setSelectedIndex(index.intValue());
 2244                       }
 2245                   }
 2246               }
 2247               else if (key == SELECT_FOCUSED) {
 2248                   int focusIndex = ui.getFocusIndex();
 2249                   if (focusIndex != -1) {
 2250                       pane.setSelectedIndex(focusIndex);
 2251                   }
 2252               }
 2253               else if (key == SCROLL_FORWARD) {
 2254                   if (ui.scrollableTabLayoutEnabled()) {
 2255                       ui.tabScroller.scrollForward(pane.getTabPlacement());
 2256                   }
 2257               }
 2258               else if (key == SCROLL_BACKWARD) {
 2259                   if (ui.scrollableTabLayoutEnabled()) {
 2260                       ui.tabScroller.scrollBackward(pane.getTabPlacement());
 2261                   }
 2262               }
 2263           }
 2264       }
 2265   
 2266       /**
 2267        * This class should be treated as a &quot;protected&quot; inner class.
 2268        * Instantiate it only within subclasses of BasicTabbedPaneUI.
 2269        */
 2270       public class TabbedPaneLayout implements LayoutManager {
 2271   
 2272           public void addLayoutComponent(String name, Component comp) {}
 2273   
 2274           public void removeLayoutComponent(Component comp) {}
 2275   
 2276           public Dimension preferredLayoutSize(Container parent) {
 2277               return calculateSize(false);
 2278           }
 2279   
 2280           public Dimension minimumLayoutSize(Container parent) {
 2281               return calculateSize(true);
 2282           }
 2283   
 2284           protected Dimension calculateSize(boolean minimum) {
 2285               int tabPlacement = tabPane.getTabPlacement();
 2286               Insets insets = tabPane.getInsets();
 2287               Insets contentInsets = getContentBorderInsets(tabPlacement);
 2288               Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
 2289   
 2290               Dimension zeroSize = new Dimension(0,0);
 2291               int height = 0;
 2292               int width = 0;
 2293               int cWidth = 0;
 2294               int cHeight = 0;
 2295   
 2296               // Determine minimum size required to display largest
 2297               // child in each dimension
 2298               //
 2299               for (int i = 0; i < tabPane.getTabCount(); i++) {
 2300                   Component component = tabPane.getComponentAt(i);
 2301                   if (component != null) {
 2302                       Dimension size = minimum ? component.getMinimumSize() :
 2303                                   component.getPreferredSize();
 2304   
 2305                       if (size != null) {
 2306                           cHeight = Math.max(size.height, cHeight);
 2307                           cWidth = Math.max(size.width, cWidth);
 2308                       }
 2309                   }
 2310               }
 2311               // Add content border insets to minimum size
 2312               width += cWidth;
 2313               height += cHeight;
 2314               int tabExtent;
 2315   
 2316               // Calculate how much space the tabs will need, based on the
 2317               // minimum size required to display largest child + content border
 2318               //
 2319               switch(tabPlacement) {
 2320                 case LEFT:
 2321                 case RIGHT:
 2322                     height = Math.max(height, calculateMaxTabHeight(tabPlacement));
 2323                     tabExtent = preferredTabAreaWidth(tabPlacement, height - tabAreaInsets.top - tabAreaInsets.bottom);
 2324                     width += tabExtent;
 2325                     break;
 2326                 case TOP:
 2327                 case BOTTOM:
 2328                 default:
 2329                     width = Math.max(width, calculateMaxTabWidth(tabPlacement));
 2330                     tabExtent = preferredTabAreaHeight(tabPlacement, width - tabAreaInsets.left - tabAreaInsets.right);
 2331                     height += tabExtent;
 2332               }
 2333               return new Dimension(width + insets.left + insets.right + contentInsets.left + contentInsets.right,
 2334                                height + insets.bottom + insets.top + contentInsets.top + contentInsets.bottom);
 2335   
 2336           }
 2337   
 2338           protected int preferredTabAreaHeight(int tabPlacement, int width) {
 2339               FontMetrics metrics = getFontMetrics();
 2340               int tabCount = tabPane.getTabCount();
 2341               int total = 0;
 2342               if (tabCount > 0) {
 2343                   int rows = 1;
 2344                   int x = 0;
 2345   
 2346                   int maxTabHeight = calculateMaxTabHeight(tabPlacement);
 2347   
 2348                   for (int i = 0; i < tabCount; i++) {
 2349                       int tabWidth = calculateTabWidth(tabPlacement, i, metrics);
 2350   
 2351                       if (x != 0 && x + tabWidth > width) {
 2352                           rows++;
 2353                           x = 0;
 2354                       }
 2355                       x += tabWidth;
 2356                   }
 2357                   total = calculateTabAreaHeight(tabPlacement, rows, maxTabHeight);
 2358               }
 2359               return total;
 2360           }
 2361   
 2362           protected int preferredTabAreaWidth(int tabPlacement, int height) {
 2363               FontMetrics metrics = getFontMetrics();
 2364               int tabCount = tabPane.getTabCount();
 2365               int total = 0;
 2366               if (tabCount > 0) {
 2367                   int columns = 1;
 2368                   int y = 0;
 2369                   int fontHeight = metrics.getHeight();
 2370   
 2371                   maxTabWidth = calculateMaxTabWidth(tabPlacement);
 2372   
 2373                   for (int i = 0; i < tabCount; i++) {
 2374                       int tabHeight = calculateTabHeight(tabPlacement, i, fontHeight);
 2375   
 2376                       if (y != 0 && y + tabHeight > height) {
 2377                           columns++;
 2378                           y = 0;
 2379                       }
 2380                       y += tabHeight;
 2381                   }
 2382                   total = calculateTabAreaWidth(tabPlacement, columns, maxTabWidth);
 2383               }
 2384               return total;
 2385           }
 2386   
 2387           public void layoutContainer(Container parent) {
 2388               /* Some of the code in this method deals with changing the
 2389               * visibility of components to hide and show the contents for the
 2390               * selected tab. This is older code that has since been duplicated
 2391               * in JTabbedPane.fireStateChanged(), so as to allow visibility
 2392               * changes to happen sooner (see the note there). This code remains
 2393               * for backward compatibility as there are some cases, such as
 2394               * subclasses that don't fireStateChanged() where it may be used.
 2395               * Any changes here need to be kept in synch with
 2396               * JTabbedPane.fireStateChanged().
 2397               */
 2398   
 2399               setRolloverTab(-1);
 2400   
 2401               int tabPlacement = tabPane.getTabPlacement();
 2402               Insets insets = tabPane.getInsets();
 2403               int selectedIndex = tabPane.getSelectedIndex();
 2404               Component visibleComponent = getVisibleComponent();
 2405   
 2406               calculateLayoutInfo();
 2407   
 2408               Component selectedComponent = null;
 2409               if (selectedIndex < 0) {
 2410                   if (visibleComponent != null) {
 2411                       // The last tab was removed, so remove the component
 2412                       setVisibleComponent(null);
 2413                   }
 2414               } else {
 2415                   selectedComponent = tabPane.getComponentAt(selectedIndex);
 2416               }
 2417               int cx, cy, cw, ch;
 2418               int totalTabWidth = 0;
 2419               int totalTabHeight = 0;
 2420               Insets contentInsets = getContentBorderInsets(tabPlacement);
 2421   
 2422               boolean shouldChangeFocus = false;
 2423   
 2424               // In order to allow programs to use a single component
 2425               // as the display for multiple tabs, we will not change
 2426               // the visible compnent if the currently selected tab
 2427               // has a null component.  This is a bit dicey, as we don't
 2428               // explicitly state we support this in the spec, but since
 2429               // programs are now depending on this, we're making it work.
 2430               //
 2431               if(selectedComponent != null) {
 2432                   if(selectedComponent != visibleComponent &&
 2433                           visibleComponent != null) {
 2434                       if(SwingUtilities.findFocusOwner(visibleComponent) != null) {
 2435                           shouldChangeFocus = true;
 2436                       }
 2437                   }
 2438                   setVisibleComponent(selectedComponent);
 2439               }
 2440   
 2441               Rectangle bounds = tabPane.getBounds();
 2442               int numChildren = tabPane.getComponentCount();
 2443   
 2444               if(numChildren > 0) {
 2445   
 2446                   switch(tabPlacement) {
 2447                       case LEFT:
 2448                           totalTabWidth = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
 2449                           cx = insets.left + totalTabWidth + contentInsets.left;
 2450                           cy = insets.top + contentInsets.top;
 2451                           break;
 2452                       case RIGHT:
 2453                           totalTabWidth = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
 2454                           cx = insets.left + contentInsets.left;
 2455                           cy = insets.top + contentInsets.top;
 2456                           break;
 2457                       case BOTTOM:
 2458                           totalTabHeight = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
 2459                           cx = insets.left + contentInsets.left;
 2460                           cy = insets.top + contentInsets.top;
 2461                           break;
 2462                       case TOP:
 2463                       default:
 2464                           totalTabHeight = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
 2465                           cx = insets.left + contentInsets.left;
 2466                           cy = insets.top + totalTabHeight + contentInsets.top;
 2467                   }
 2468   
 2469                   cw = bounds.width - totalTabWidth -
 2470                           insets.left - insets.right -
 2471                           contentInsets.left - contentInsets.right;
 2472                   ch = bounds.height - totalTabHeight -
 2473                           insets.top - insets.bottom -
 2474                           contentInsets.top - contentInsets.bottom;
 2475   
 2476                   for(int i = 0; i < numChildren; i++) {
 2477                       Component child = tabPane.getComponent(i);
 2478                       if(child == tabContainer) {
 2479   
 2480                           int tabContainerWidth = totalTabWidth == 0 ? bounds.width :
 2481                                   totalTabWidth + insets.left + insets.right +
 2482                                           contentInsets.left + contentInsets.right;
 2483                           int tabContainerHeight = totalTabHeight == 0 ? bounds.height :
 2484                                   totalTabHeight + insets.top + insets.bottom +
 2485                                           contentInsets.top + contentInsets.bottom;
 2486   
 2487                           int tabContainerX = 0;
 2488                           int tabContainerY = 0;
 2489                           if(tabPlacement == BOTTOM) {
 2490                               tabContainerY = bounds.height - tabContainerHeight;
 2491                           } else if(tabPlacement == RIGHT) {
 2492                               tabContainerX = bounds.width - tabContainerWidth;
 2493                           }
 2494                           child.setBounds(tabContainerX, tabContainerY, tabContainerWidth, tabContainerHeight);
 2495                       } else {
 2496                           child.setBounds(cx, cy, cw, ch);
 2497                       }
 2498                   }
 2499               }
 2500               layoutTabComponents();
 2501               if(shouldChangeFocus) {
 2502                   if(!requestFocusForVisibleComponent()) {
 2503                       tabPane.requestFocus();
 2504                   }
 2505               }
 2506           }
 2507   
 2508           public void calculateLayoutInfo() {
 2509               int tabCount = tabPane.getTabCount();
 2510               assureRectsCreated(tabCount);
 2511               calculateTabRects(tabPane.getTabPlacement(), tabCount);
 2512               isRunsDirty = false;
 2513           }
 2514   
 2515           private void layoutTabComponents() {
 2516               if (tabContainer == null) {
 2517                   return;
 2518               }
 2519               Rectangle rect = new Rectangle();
 2520               Point delta = new Point(-tabContainer.getX(), -tabContainer.getY());
 2521               if (scrollableTabLayoutEnabled()) {
 2522                   translatePointToTabPanel(0, 0, delta);
 2523               }
 2524               for (int i = 0; i < tabPane.getTabCount(); i++) {
 2525                   Component c = tabPane.getTabComponentAt(i);
 2526                   if (c == null) {
 2527                       continue;
 2528                   }
 2529                   getTabBounds(i, rect);
 2530                   Dimension preferredSize = c.getPreferredSize();
 2531                   Insets insets = getTabInsets(tabPane.getTabPlacement(), i);
 2532                   int outerX = rect.x + insets.left + delta.x;
 2533                   int outerY = rect.y + insets.top + delta.y;
 2534                   int outerWidth = rect.width - insets.left - insets.right;
 2535                   int outerHeight = rect.height - insets.top - insets.bottom;
 2536                   //centralize component
 2537                   int x = outerX + (outerWidth - preferredSize.width) / 2;
 2538                   int y = outerY + (outerHeight - preferredSize.height) / 2;
 2539                   int tabPlacement = tabPane.getTabPlacement();
 2540                   boolean isSeleceted = i == tabPane.getSelectedIndex();
 2541                   c.setBounds(x + getTabLabelShiftX(tabPlacement, i, isSeleceted),
 2542                               y + getTabLabelShiftY(tabPlacement, i, isSeleceted),
 2543                           preferredSize.width, preferredSize.height);
 2544               }
 2545           }
 2546   
 2547           protected void calculateTabRects(int tabPlacement, int tabCount) {
 2548               FontMetrics metrics = getFontMetrics();
 2549               Dimension size = tabPane.getSize();
 2550               Insets insets = tabPane.getInsets();
 2551               Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
 2552               int fontHeight = metrics.getHeight();
 2553               int selectedIndex = tabPane.getSelectedIndex();
 2554               int tabRunOverlay;
 2555               int i, j;
 2556               int x, y;
 2557               int returnAt;
 2558               boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
 2559               boolean leftToRight = BasicGraphicsUtils.isLeftToRight(tabPane);
 2560   
 2561               //
 2562               // Calculate bounds within which a tab run must fit
 2563               //
 2564               switch(tabPlacement) {
 2565                 case LEFT:
 2566                     maxTabWidth = calculateMaxTabWidth(tabPlacement);
 2567                     x = insets.left + tabAreaInsets.left;
 2568                     y = insets.top + tabAreaInsets.top;
 2569                     returnAt = size.height - (insets.bottom + tabAreaInsets.bottom);
 2570                     break;
 2571                 case RIGHT:
 2572                     maxTabWidth = calculateMaxTabWidth(tabPlacement);
 2573                     x = size.width - insets.right - tabAreaInsets.right - maxTabWidth;
 2574                     y = insets.top + tabAreaInsets.top;
 2575                     returnAt = size.height - (insets.bottom + tabAreaInsets.bottom);
 2576                     break;
 2577                 case BOTTOM:
 2578                     maxTabHeight = calculateMaxTabHeight(tabPlacement);
 2579                     x = insets.left + tabAreaInsets.left;
 2580                     y = size.height - insets.bottom - tabAreaInsets.bottom - maxTabHeight;
 2581                     returnAt = size.width - (insets.right + tabAreaInsets.right);
 2582                     break;
 2583                 case TOP:
 2584                 default:
 2585                     maxTabHeight = calculateMaxTabHeight(tabPlacement);
 2586                     x = insets.left + tabAreaInsets.left;
 2587                     y = insets.top + tabAreaInsets.top;
 2588                     returnAt = size.width - (insets.right + tabAreaInsets.right);
 2589                     break;
 2590               }
 2591   
 2592               tabRunOverlay = getTabRunOverlay(tabPlacement);
 2593   
 2594               runCount = 0;
 2595               selectedRun = -1;
 2596   
 2597               if (tabCount == 0) {
 2598                   return;
 2599               }
 2600   
 2601               // Run through tabs and partition them into runs
 2602               Rectangle rect;
 2603               for (i = 0; i < tabCount; i++) {
 2604                   rect = rects[i];
 2605   
 2606                   if (!verticalTabRuns) {
 2607                       // Tabs on TOP or BOTTOM....
 2608                       if (i > 0) {
 2609                           rect.x = rects[i-1].x + rects[i-1].width;
 2610                       } else {
 2611                           tabRuns[0] = 0;
 2612                           runCount = 1;
 2613                           maxTabWidth = 0;
 2614                           rect.x = x;
 2615                       }
 2616                       rect.width = calculateTabWidth(tabPlacement, i, metrics);
 2617                       maxTabWidth = Math.max(maxTabWidth, rect.width);
 2618   
 2619                       // Never move a TAB down a run if it is in the first column.
 2620                       // Even if there isn't enough room, moving it to a fresh
 2621                       // line won't help.
 2622                       if (rect.x != 2 + insets.left && rect.x + rect.width > returnAt) {
 2623                           if (runCount > tabRuns.length - 1) {
 2624                               expandTabRunsArray();
 2625                           }
 2626                           tabRuns[runCount] = i;
 2627                           runCount++;
 2628                           rect.x = x;
 2629                       }
 2630                       // Initialize y position in case there's just one run
 2631                       rect.y = y;
 2632                       rect.height = maxTabHeight/* - 2*/;
 2633   
 2634                   } else {
 2635                       // Tabs on LEFT or RIGHT...
 2636                       if (i > 0) {
 2637                           rect.y = rects[i-1].y + rects[i-1].height;
 2638                       } else {
 2639                           tabRuns[0] = 0;
 2640                           runCount = 1;
 2641                           maxTabHeight = 0;
 2642                           rect.y = y;
 2643                       }
 2644                       rect.height = calculateTabHeight(tabPlacement, i, fontHeight);
 2645                       maxTabHeight = Math.max(maxTabHeight, rect.height);
 2646   
 2647                       // Never move a TAB over a run if it is in the first run.
 2648                       // Even if there isn't enough room, moving it to a fresh
 2649                       // column won't help.
 2650                       if (rect.y != 2 + insets.top && rect.y + rect.height > returnAt) {
 2651                           if (runCount > tabRuns.length - 1) {
 2652                               expandTabRunsArray();
 2653                           }
 2654                           tabRuns[runCount] = i;
 2655                           runCount++;
 2656                           rect.y = y;
 2657                       }
 2658                       // Initialize x position in case there's just one column
 2659                       rect.x = x;
 2660                       rect.width = maxTabWidth/* - 2*/;
 2661   
 2662                   }
 2663                   if (i == selectedIndex) {
 2664                       selectedRun = runCount - 1;
 2665                   }
 2666               }
 2667   
 2668               if (runCount > 1) {
 2669                   // Re-distribute tabs in case last run has leftover space
 2670                   normalizeTabRuns(tabPlacement, tabCount, verticalTabRuns? y : x, returnAt);
 2671   
 2672                   selectedRun = getRunForTab(tabCount, selectedIndex);
 2673   
 2674                   // Rotate run array so that selected run is first
 2675                   if (shouldRotateTabRuns(tabPlacement)) {
 2676                       rotateTabRuns(tabPlacement, selectedRun);
 2677                   }
 2678               }
 2679   
 2680               // Step through runs from back to front to calculate
 2681               // tab y locations and to pad runs appropriately
 2682               for (i = runCount - 1; i >= 0; i--) {
 2683                   int start = tabRuns[i];
 2684                   int next = tabRuns[i == (runCount - 1)? 0 : i + 1];
 2685                   int end = (next != 0? next - 1 : tabCount - 1);
 2686                   if (!verticalTabRuns) {
 2687                       for (j = start; j <= end; j++) {
 2688                           rect = rects[j];
 2689                           rect.y = y;
 2690                           rect.x += getTabRunIndent(tabPlacement, i);
 2691                       }
 2692                       if (shouldPadTabRun(tabPlacement, i)) {
 2693                           padTabRun(tabPlacement, start, end, returnAt);
 2694                       }
 2695                       if (tabPlacement == BOTTOM) {
 2696                           y -= (maxTabHeight - tabRunOverlay);
 2697                       } else {
 2698                           y += (maxTabHeight - tabRunOverlay);
 2699                       }
 2700                   } else {
 2701                       for (j = start; j <= end; j++) {
 2702                           rect = rects[j];
 2703                           rect.x = x;
 2704                           rect.y += getTabRunIndent(tabPlacement, i);
 2705                       }
 2706                       if (shouldPadTabRun(tabPlacement, i)) {
 2707                           padTabRun(tabPlacement, start, end, returnAt);
 2708                       }
 2709                       if (tabPlacement == RIGHT) {
 2710                           x -= (maxTabWidth - tabRunOverlay);
 2711                       } else {
 2712                           x += (maxTabWidth - tabRunOverlay);
 2713                       }
 2714                   }
 2715               }
 2716   
 2717               // Pad the selected tab so that it appears raised in front
 2718               padSelectedTab(tabPlacement, selectedIndex);
 2719   
 2720               // if right to left and tab placement on the top or
 2721               // the bottom, flip x positions and adjust by widths
 2722               if (!leftToRight && !verticalTabRuns) {
 2723                   int rightMargin = size.width
 2724                                     - (insets.right + tabAreaInsets.right);
 2725                   for (i = 0; i < tabCount; i++) {
 2726                       rects[i].x = rightMargin - rects[i].x - rects[i].width;
 2727                   }
 2728               }
 2729           }
 2730   
 2731   
 2732          /*
 2733          * Rotates the run-index array so that the selected run is run[0]
 2734          */
 2735           protected void rotateTabRuns(int tabPlacement, int selectedRun) {
 2736               for (int i = 0; i < selectedRun; i++) {
 2737                   int save = tabRuns[0];
 2738                   for (int j = 1; j < runCount; j++) {
 2739                       tabRuns[j - 1] = tabRuns[j];
 2740                   }
 2741                   tabRuns[runCount-1] = save;
 2742               }
 2743           }
 2744   
 2745           protected void normalizeTabRuns(int tabPlacement, int tabCount,
 2746                                        int start, int max) {
 2747               boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
 2748               int run = runCount - 1;
 2749               boolean keepAdjusting = true;
 2750               double weight = 1.25;
 2751   
 2752               // At this point the tab runs are packed to fit as many
 2753               // tabs as possible, which can leave the last run with a lot
 2754               // of extra space (resulting in very fat tabs on the last run).
 2755               // So we'll attempt to distribute this extra space more evenly
 2756               // across the runs in order to make the runs look more consistent.
 2757               //
 2758               // Starting with the last run, determine whether the last tab in
 2759               // the previous run would fit (generously) in this run; if so,
 2760               // move tab to current run and shift tabs accordingly.  Cycle
 2761               // through remaining runs using the same algorithm.
 2762               //
 2763               while (keepAdjusting) {
 2764                   int last = lastTabInRun(tabCount, run);
 2765                   int prevLast = lastTabInRun(tabCount, run-1);
 2766                   int end;
 2767                   int prevLastLen;
 2768   
 2769                   if (!verticalTabRuns) {
 2770                       end = rects[last].x + rects[last].width;
 2771                       prevLastLen = (int)(maxTabWidth*weight);
 2772                   } else {
 2773                       end = rects[last].y + rects[last].height;
 2774                       prevLastLen = (int)(maxTabHeight*weight*2);
 2775                   }
 2776   
 2777                   // Check if the run has enough extra space to fit the last tab
 2778                   // from the previous row...
 2779                   if (max - end > prevLastLen) {
 2780   
 2781                       // Insert tab from previous row and shift rest over
 2782                       tabRuns[run] = prevLast;
 2783                       if (!verticalTabRuns) {
 2784                           rects[prevLast].x = start;
 2785                       } else {
 2786                           rects[prevLast].y = start;
 2787                       }
 2788                       for (int i = prevLast+1; i <= last; i++) {
 2789                           if (!verticalTabRuns) {
 2790                               rects[i].x = rects[i-1].x + rects[i-1].width;
 2791                           } else {
 2792                               rects[i].y = rects[i-1].y + rects[i-1].height;
 2793                           }
 2794                       }
 2795   
 2796                   } else if (run == runCount - 1) {
 2797                       // no more room left in last run, so we're done!
 2798                       keepAdjusting = false;
 2799                   }
 2800                   if (run - 1 > 0) {
 2801                       // check previous run next...
 2802                       run -= 1;
 2803                   } else {
 2804                       // check last run again...but require a higher ratio
 2805                       // of extraspace-to-tabsize because we don't want to
 2806                       // end up with too many tabs on the last run!
 2807                       run = runCount - 1;
 2808                       weight += .25;
 2809                   }
 2810               }
 2811           }
 2812   
 2813           protected void padTabRun(int tabPlacement, int start, int end, int max) {
 2814               Rectangle lastRect = rects[end];
 2815               if (tabPlacement == TOP || tabPlacement == BOTTOM) {
 2816                   int runWidth = (lastRect.x + lastRect.width) - rects[start].x;
 2817                   int deltaWidth = max - (lastRect.x + lastRect.width);
 2818                   float factor = (float)deltaWidth / (float)runWidth;
 2819   
 2820                   for (int j = start; j <= end; j++) {
 2821                       Rectangle pastRect = rects[j];
 2822                       if (j > start) {
 2823                           pastRect.x = rects[j-1].x + rects[j-1].width;
 2824                       }
 2825                       pastRect.width += Math.round((float)pastRect.width * factor);
 2826                   }
 2827                   lastRect.width = max - lastRect.x;
 2828               } else {
 2829                   int runHeight = (lastRect.y + lastRect.height) - rects[start].y;
 2830                   int deltaHeight = max - (lastRect.y + lastRect.height);
 2831                   float factor = (float)deltaHeight / (float)runHeight;
 2832   
 2833                   for (int j = start; j <= end; j++) {
 2834                       Rectangle pastRect = rects[j];
 2835                       if (j > start) {
 2836                           pastRect.y = rects[j-1].y + rects[j-1].height;
 2837                       }
 2838                       pastRect.height += Math.round((float)pastRect.height * factor);
 2839                   }
 2840                   lastRect.height = max - lastRect.y;
 2841               }
 2842           }
 2843   
 2844           protected void padSelectedTab(int tabPlacement, int selectedIndex) {
 2845   
 2846               if (selectedIndex >= 0) {
 2847                   Rectangle selRect = rects[selectedIndex];
 2848                   Insets padInsets = getSelectedTabPadInsets(tabPlacement);
 2849                   selRect.x -= padInsets.left;
 2850                   selRect.width += (padInsets.left + padInsets.right);
 2851                   selRect.y -= padInsets.top;
 2852                   selRect.height += (padInsets.top + padInsets.bottom);
 2853   
 2854                   if (!scrollableTabLayoutEnabled()) { // WRAP_TAB_LAYOUT
 2855                       // do not expand selected tab more then necessary
 2856                       Dimension size = tabPane.getSize();
 2857                       Insets insets = tabPane.getInsets();
 2858   
 2859                       if ((tabPlacement == LEFT) || (tabPlacement == RIGHT)) {
 2860                           int top = insets.top - selRect.y;
 2861                           if (top > 0) {
 2862                               selRect.y += top;
 2863                               selRect.height -= top;
 2864                           }
 2865                           int bottom = (selRect.y + selRect.height) + insets.bottom - size.height;
 2866                           if (bottom > 0) {
 2867                               selRect.height -= bottom;
 2868                           }
 2869                       } else {
 2870                           int left = insets.left - selRect.x;
 2871                           if (left > 0) {
 2872                               selRect.x += left;
 2873                               selRect.width -= left;
 2874                           }
 2875                           int right = (selRect.x + selRect.width) + insets.right - size.width;
 2876                           if (right > 0) {
 2877                               selRect.width -= right;
 2878                           }
 2879                       }
 2880                   }
 2881               }
 2882           }
 2883       }
 2884   
 2885       private class TabbedPaneScrollLayout extends TabbedPaneLayout {
 2886   
 2887           protected int preferredTabAreaHeight(int tabPlacement, int width) {
 2888               return calculateMaxTabHeight(tabPlacement);
 2889           }
 2890   
 2891           protected int preferredTabAreaWidth(int tabPlacement, int height) {
 2892               return calculateMaxTabWidth(tabPlacement);
 2893           }
 2894   
 2895           public void layoutContainer(Container parent) {
 2896               /* Some of the code in this method deals with changing the
 2897                * visibility of components to hide and show the contents for the
 2898                * selected tab. This is older code that has since been duplicated
 2899                * in JTabbedPane.fireStateChanged(), so as to allow visibility
 2900                * changes to happen sooner (see the note there). This code remains
 2901                * for backward compatibility as there are some cases, such as
 2902                * subclasses that don't fireStateChanged() where it may be used.
 2903                * Any changes here need to be kept in synch with
 2904                * JTabbedPane.fireStateChanged().
 2905                */
 2906   
 2907               setRolloverTab(-1);
 2908   
 2909               int tabPlacement = tabPane.getTabPlacement();
 2910               int tabCount = tabPane.getTabCount();
 2911               Insets insets = tabPane.getInsets();
 2912               int selectedIndex = tabPane.getSelectedIndex();
 2913               Component visibleComponent = getVisibleComponent();
 2914   
 2915               calculateLayoutInfo();
 2916   
 2917               Component selectedComponent = null;
 2918               if (selectedIndex < 0) {
 2919                   if (visibleComponent != null) {
 2920                       // The last tab was removed, so remove the component
 2921                       setVisibleComponent(null);
 2922                   }
 2923               } else {
 2924                   selectedComponent = tabPane.getComponentAt(selectedIndex);
 2925               }
 2926   
 2927               if (tabPane.getTabCount() == 0) {
 2928                   tabScroller.croppedEdge.resetParams();
 2929                   tabScroller.scrollForwardButton.setVisible(false);
 2930                   tabScroller.scrollBackwardButton.setVisible(false);
 2931                   return;
 2932               }
 2933   
 2934               boolean shouldChangeFocus = false;
 2935   
 2936               // In order to allow programs to use a single component
 2937               // as the display for multiple tabs, we will not change
 2938               // the visible compnent if the currently selected tab
 2939               // has a null component.  This is a bit dicey, as we don't
 2940               // explicitly state we support this in the spec, but since
 2941               // programs are now depending on this, we're making it work.
 2942               //
 2943               if(selectedComponent != null) {
 2944                   if(selectedComponent != visibleComponent &&
 2945                           visibleComponent != null) {
 2946                       if(SwingUtilities.findFocusOwner(visibleComponent) != null) {
 2947                           shouldChangeFocus = true;
 2948                       }
 2949                   }
 2950                   setVisibleComponent(selectedComponent);
 2951               }
 2952               int tx, ty, tw, th; // tab area bounds
 2953               int cx, cy, cw, ch; // content area bounds
 2954               Insets contentInsets = getContentBorderInsets(tabPlacement);
 2955               Rectangle bounds = tabPane.getBounds();
 2956               int numChildren = tabPane.getComponentCount();
 2957   
 2958               if(numChildren > 0) {
 2959                   switch(tabPlacement) {
 2960                       case LEFT:
 2961                           // calculate tab area bounds
 2962                           tw = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
 2963                           th = bounds.height - insets.top - insets.bottom;
 2964                           tx = insets.left;
 2965                           ty = insets.top;
 2966   
 2967                           // calculate content area bounds
 2968                           cx = tx + tw + contentInsets.left;
 2969                           cy = ty + contentInsets.top;
 2970                           cw = bounds.width - insets.left - insets.right - tw -
 2971                                   contentInsets.left - contentInsets.right;
 2972                           ch = bounds.height - insets.top - insets.bottom -
 2973                                   contentInsets.top - contentInsets.bottom;
 2974                           break;
 2975                       case RIGHT:
 2976                           // calculate tab area bounds
 2977                           tw = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
 2978                           th = bounds.height - insets.top - insets.bottom;
 2979                           tx = bounds.width - insets.right - tw;
 2980                           ty = insets.top;
 2981   
 2982                           // calculate content area bounds
 2983                           cx = insets.left + contentInsets.left;
 2984                           cy = insets.top + contentInsets.top;
 2985                           cw = bounds.width - insets.left - insets.right - tw -
 2986                                   contentInsets.left - contentInsets.right;
 2987                           ch = bounds.height - insets.top - insets.bottom -
 2988                                   contentInsets.top - contentInsets.bottom;
 2989                           break;
 2990                       case BOTTOM:
 2991                           // calculate tab area bounds
 2992                           tw = bounds.width - insets.left - insets.right;
 2993                           th = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
 2994                           tx = insets.left;
 2995                           ty = bounds.height - insets.bottom - th;
 2996   
 2997                           // calculate content area bounds
 2998                           cx = insets.left + contentInsets.left;
 2999                           cy = insets.top + contentInsets.top;
 3000                           cw = bounds.width - insets.left - insets.right -
 3001                                   contentInsets.left - contentInsets.right;
 3002                           ch = bounds.height - insets.top - insets.bottom - th -
 3003                                   contentInsets.top - contentInsets.bottom;
 3004                           break;
 3005                       case TOP:
 3006                       default:
 3007                           // calculate tab area bounds
 3008                           tw = bounds.width - insets.left - insets.right;
 3009                           th = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
 3010                           tx = insets.left;
 3011                           ty = insets.top;
 3012   
 3013                           // calculate content area bounds
 3014                           cx = tx + contentInsets.left;
 3015                           cy = ty + th + contentInsets.top;
 3016                           cw = bounds.width - insets.left - insets.right -
 3017                                   contentInsets.left - contentInsets.right;
 3018                           ch = bounds.height - insets.top - insets.bottom - th -
 3019                                   contentInsets.top - contentInsets.bottom;
 3020                   }
 3021   
 3022                   for(int i = 0; i < numChildren; i++) {
 3023                       Component child = tabPane.getComponent(i);
 3024   
 3025                       if(tabScroller != null && child == tabScroller.viewport) {
 3026                           JViewport viewport = (JViewport) child;
 3027                           Rectangle viewRect = viewport.getViewRect();
 3028                           int vw = tw;
 3029                           int vh = th;
 3030                           Dimension butSize = tabScroller.scrollForwardButton.getPreferredSize();
 3031                           switch(tabPlacement) {
 3032                               case LEFT:
 3033                               case RIGHT:
 3034                                   int totalTabHeight = rects[tabCount - 1].y + rects[tabCount - 1].height;
 3035                                   if(totalTabHeight > th) {
 3036                                       // Allow space for scrollbuttons
 3037                                       vh = (th > 2 * butSize.height) ? th - 2 * butSize.height : 0;
 3038                                       if(totalTabHeight - viewRect.y <= vh) {
 3039                                           // Scrolled to the end, so ensure the viewport size is
 3040                                           // such that the scroll offset aligns with a tab
 3041                                           vh = totalTabHeight - viewRect.y;
 3042                                       }
 3043                                   }
 3044                                   break;
 3045                               case BOTTOM:
 3046                               case TOP:
 3047                               default:
 3048                                   int totalTabWidth = rects[tabCount - 1].x + rects[tabCount - 1].width;
 3049                                   if(totalTabWidth > tw) {
 3050                                       // Need to allow space for scrollbuttons
 3051                                       vw = (tw > 2 * butSize.width) ? tw - 2 * butSize.width : 0;
 3052                                       if(totalTabWidth - viewRect.x <= vw) {
 3053                                           // Scrolled to the end, so ensure the viewport size is
 3054                                           // such that the scroll offset aligns with a tab
 3055                                           vw = totalTabWidth - viewRect.x;
 3056                                       }
 3057                                   }
 3058                           }
 3059                           child.setBounds(tx, ty, vw, vh);
 3060   
 3061                       } else if(tabScroller != null &&
 3062                               (child == tabScroller.scrollForwardButton ||
 3063                               child == tabScroller.scrollBackwardButton)) {
 3064                           Component scrollbutton = child;
 3065                           Dimension bsize = scrollbutton.getPreferredSize();
 3066                           int bx = 0;
 3067                           int by = 0;
 3068                           int bw = bsize.width;
 3069                           int bh = bsize.height;
 3070                           boolean visible = false;
 3071   
 3072                           switch(tabPlacement) {
 3073                               case LEFT:
 3074                               case RIGHT:
 3075                                   int totalTabHeight = rects[tabCount - 1].y + rects[tabCount - 1].height;
 3076                                   if(totalTabHeight > th) {
 3077                                       visible = true;
 3078                                       bx = (tabPlacement == LEFT ? tx + tw - bsize.width : tx);
 3079                                       by = (child == tabScroller.scrollForwardButton) ?
 3080                                               bounds.height - insets.bottom - bsize.height :
 3081                                               bounds.height - insets.bottom - 2 * bsize.height;
 3082                                   }
 3083                                   break;
 3084   
 3085                               case BOTTOM:
 3086                               case TOP:
 3087                               default:
 3088                                   int totalTabWidth = rects[tabCount - 1].x + rects[tabCount - 1].width;
 3089   
 3090                                   if(totalTabWidth > tw) {
 3091                                       visible = true;
 3092                                       bx = (child == tabScroller.scrollForwardButton) ?
 3093                                               bounds.width - insets.left - bsize.width :
 3094                                               bounds.width - insets.left - 2 * bsize.width;
 3095                                       by = (tabPlacement == TOP ? ty + th - bsize.height : ty);
 3096                                   }
 3097                           }
 3098                           child.setVisible(visible);
 3099                           if(visible) {
 3100                               child.setBounds(bx, by, bw, bh);
 3101                           }
 3102   
 3103                       } else {
 3104                           // All content children...
 3105                           child.setBounds(cx, cy, cw, ch);
 3106                       }
 3107                   }
 3108                   super.layoutTabComponents();
 3109                   layoutCroppedEdge();
 3110                   if(shouldChangeFocus) {
 3111                       if(!requestFocusForVisibleComponent()) {
 3112                           tabPane.requestFocus();
 3113                       }
 3114                   }
 3115               }
 3116           }
 3117   
 3118           private void layoutCroppedEdge() {
 3119               tabScroller.croppedEdge.resetParams();
 3120               Rectangle viewRect = tabScroller.viewport.getViewRect();
 3121               int cropline;
 3122               for (int i = 0; i < rects.length; i++) {
 3123                   Rectangle tabRect = rects[i];
 3124                   switch (tabPane.getTabPlacement()) {
 3125                       case LEFT:
 3126                       case RIGHT:
 3127                           cropline = viewRect.y + viewRect.height;
 3128                           if ((tabRect.y < cropline) && (tabRect.y + tabRect.height > cropline)) {
 3129                               tabScroller.croppedEdge.setParams(i, cropline - tabRect.y - 1,
 3130                                       -currentTabAreaInsets.left,  0);
 3131                           }
 3132                           break;
 3133                       case TOP:
 3134                       case BOTTOM:
 3135                       default:
 3136                           cropline = viewRect.x + viewRect.width;
 3137                           if ((tabRect.x < cropline - 1) && (tabRect.x + tabRect.width > cropline)) {
 3138                               tabScroller.croppedEdge.setParams(i, cropline - tabRect.x - 1,
 3139                                       0, -currentTabAreaInsets.top);
 3140                           }
 3141                   }
 3142               }
 3143           }
 3144   
 3145           protected void calculateTabRects(int tabPlacement, int tabCount) {
 3146               FontMetrics metrics = getFontMetrics();
 3147               Dimension size = tabPane.getSize();
 3148               Insets insets = tabPane.getInsets();
 3149               Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
 3150               int fontHeight = metrics.getHeight();
 3151               int selectedIndex = tabPane.getSelectedIndex();
 3152               int i;
 3153               boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
 3154               boolean leftToRight = BasicGraphicsUtils.isLeftToRight(tabPane);
 3155               int x = tabAreaInsets.left;
 3156               int y = tabAreaInsets.top;
 3157               int totalWidth = 0;
 3158               int totalHeight = 0;
 3159   
 3160               //
 3161               // Calculate bounds within which a tab run must fit
 3162               //
 3163               switch(tabPlacement) {
 3164                 case LEFT:
 3165                 case RIGHT:
 3166                     maxTabWidth = calculateMaxTabWidth(tabPlacement);
 3167                     break;
 3168                 case BOTTOM:
 3169                 case TOP:
 3170                 default:
 3171                     maxTabHeight = calculateMaxTabHeight(tabPlacement);
 3172               }
 3173   
 3174               runCount = 0;
 3175               selectedRun = -1;
 3176   
 3177               if (tabCount == 0) {
 3178                   return;
 3179               }
 3180   
 3181               selectedRun = 0;
 3182               runCount = 1;
 3183   
 3184               // Run through tabs and lay them out in a single run
 3185               Rectangle rect;
 3186               for (i = 0; i < tabCount; i++) {
 3187                   rect = rects[i];
 3188   
 3189                   if (!verticalTabRuns) {
 3190                       // Tabs on TOP or BOTTOM....
 3191                       if (i > 0) {
 3192                           rect.x = rects[i-1].x + rects[i-1].width;
 3193                       } else {
 3194                           tabRuns[0] = 0;
 3195                           maxTabWidth = 0;
 3196                           totalHeight += maxTabHeight;
 3197                           rect.x = x;
 3198                       }
 3199                       rect.width = calculateTabWidth(tabPlacement, i, metrics);
 3200                       totalWidth = rect.x + rect.width;
 3201                       maxTabWidth = Math.max(maxTabWidth, rect.width);
 3202   
 3203                       rect.y = y;
 3204                       rect.height = maxTabHeight/* - 2*/;
 3205   
 3206                   } else {
 3207                       // Tabs on LEFT or RIGHT...
 3208                       if (i > 0) {
 3209                           rect.y = rects[i-1].y + rects[i-1].height;
 3210                       } else {
 3211                           tabRuns[0] = 0;
 3212                           maxTabHeight = 0;
 3213                           totalWidth = maxTabWidth;
 3214                           rect.y = y;
 3215                       }
 3216                       rect.height = calculateTabHeight(tabPlacement, i, fontHeight);
 3217                       totalHeight = rect.y + rect.height;
 3218                       maxTabHeight = Math.max(maxTabHeight, rect.height);
 3219   
 3220                       rect.x = x;
 3221                       rect.width = maxTabWidth/* - 2*/;
 3222   
 3223                   }
 3224               }
 3225   
 3226               if (tabsOverlapBorder) {
 3227                   // Pad the selected tab so that it appears raised in front
 3228                   padSelectedTab(tabPlacement, selectedIndex);
 3229               }
 3230   
 3231               // if right to left and tab placement on the top or
 3232               // the bottom, flip x positions and adjust by widths
 3233               if (!leftToRight && !verticalTabRuns) {
 3234                   int rightMargin = size.width
 3235                                     - (insets.right + tabAreaInsets.right);
 3236                   for (i = 0; i < tabCount; i++) {
 3237                       rects[i].x = rightMargin - rects[i].x - rects[i].width;
 3238                   }
 3239               }
 3240               tabScroller.tabPanel.setPreferredSize(new Dimension(totalWidth, totalHeight));
 3241           }
 3242       }
 3243   
 3244       private class ScrollableTabSupport implements ActionListener,
 3245                               ChangeListener {
 3246           public ScrollableTabViewport viewport;
 3247           public ScrollableTabPanel tabPanel;
 3248           public JButton scrollForwardButton;
 3249           public JButton scrollBackwardButton;
 3250           public CroppedEdge croppedEdge;
 3251           public int leadingTabIndex;
 3252   
 3253           private Point tabViewPosition = new Point(0,0);
 3254   
 3255           ScrollableTabSupport(int tabPlacement) {
 3256               viewport = new ScrollableTabViewport();
 3257               tabPanel = new ScrollableTabPanel();
 3258               viewport.setView(tabPanel);
 3259               viewport.addChangeListener(this);
 3260               croppedEdge = new CroppedEdge();
 3261               createButtons();
 3262           }
 3263   
 3264           /**
 3265            * Recreates the scroll buttons and adds them to the TabbedPane.
 3266            */
 3267           void createButtons() {
 3268               if (scrollForwardButton != null) {
 3269                   tabPane.remove(scrollForwardButton);
 3270                   scrollForwardButton.removeActionListener(this);
 3271                   tabPane.remove(scrollBackwardButton);
 3272                   scrollBackwardButton.removeActionListener(this);
 3273               }
 3274               int tabPlacement = tabPane.getTabPlacement();
 3275               if (tabPlacement == TOP || tabPlacement == BOTTOM) {
 3276                   scrollForwardButton = createScrollButton(EAST);
 3277                   scrollBackwardButton = createScrollButton(WEST);
 3278   
 3279               } else { // tabPlacement = LEFT || RIGHT
 3280                   scrollForwardButton = createScrollButton(SOUTH);
 3281                   scrollBackwardButton = createScrollButton(NORTH);
 3282               }
 3283               scrollForwardButton.addActionListener(this);
 3284               scrollBackwardButton.addActionListener(this);
 3285               tabPane.add(scrollForwardButton);
 3286               tabPane.add(scrollBackwardButton);
 3287           }
 3288   
 3289           public void scrollForward(int tabPlacement) {
 3290               Dimension viewSize = viewport.getViewSize();
 3291               Rectangle viewRect = viewport.getViewRect();
 3292   
 3293               if (tabPlacement == TOP || tabPlacement == BOTTOM) {
 3294                   if (viewRect.width >= viewSize.width - viewRect.x) {
 3295                       return; // no room left to scroll
 3296                   }
 3297               } else { // tabPlacement == LEFT || tabPlacement == RIGHT
 3298                   if (viewRect.height >= viewSize.height - viewRect.y) {
 3299                       return;
 3300                   }
 3301               }
 3302               setLeadingTabIndex(tabPlacement, leadingTabIndex+1);
 3303           }
 3304   
 3305           public void scrollBackward(int tabPlacement) {
 3306               if (leadingTabIndex == 0) {
 3307                   return; // no room left to scroll
 3308               }
 3309               setLeadingTabIndex(tabPlacement, leadingTabIndex-1);
 3310           }
 3311   
 3312           public void setLeadingTabIndex(int tabPlacement, int index) {
 3313               leadingTabIndex = index;
 3314               Dimension viewSize = viewport.getViewSize();
 3315               Rectangle viewRect = viewport.getViewRect();
 3316   
 3317               switch(tabPlacement) {
 3318                 case TOP:
 3319                 case BOTTOM:
 3320                   tabViewPosition.x = leadingTabIndex == 0? 0 : rects[leadingTabIndex].x;
 3321   
 3322                   if ((viewSize.width - tabViewPosition.x) < viewRect.width) {
 3323                       // We've scrolled to the end, so adjust the viewport size
 3324                       // to ensure the view position remains aligned on a tab boundary
 3325                       Dimension extentSize = new Dimension(viewSize.width - tabViewPosition.x,
 3326                                                            viewRect.height);
 3327                       viewport.setExtentSize(extentSize);
 3328                   }
 3329                   break;
 3330                 case LEFT:
 3331                 case RIGHT:
 3332                   tabViewPosition.y = leadingTabIndex == 0? 0 : rects[leadingTabIndex].y;
 3333   
 3334                   if ((viewSize.height - tabViewPosition.y) < viewRect.height) {
 3335                   // We've scrolled to the end, so adjust the viewport size
 3336                   // to ensure the view position remains aligned on a tab boundary
 3337                        Dimension extentSize = new Dimension(viewRect.width,
 3338                                                             viewSize.height - tabViewPosition.y);
 3339                        viewport.setExtentSize(extentSize);
 3340                   }
 3341               }
 3342               viewport.setViewPosition(tabViewPosition);
 3343           }
 3344   
 3345           public void stateChanged(ChangeEvent e) {
 3346               updateView();
 3347           }
 3348   
 3349           private void updateView() {
 3350               int tabPlacement = tabPane.getTabPlacement();
 3351               int tabCount = tabPane.getTabCount();
 3352               Rectangle vpRect = viewport.getBounds();
 3353               Dimension viewSize = viewport.getViewSize();
 3354               Rectangle viewRect = viewport.getViewRect();
 3355   
 3356               leadingTabIndex = getClosestTab(viewRect.x, viewRect.y);
 3357   
 3358               // If the tab isn't right aligned, adjust it.
 3359               if (leadingTabIndex + 1 < tabCount) {
 3360                   switch (tabPlacement) {
 3361                   case TOP:
 3362                   case BOTTOM:
 3363                       if (rects[leadingTabIndex].x < viewRect.x) {
 3364                           leadingTabIndex++;
 3365                       }
 3366                       break;
 3367                   case LEFT:
 3368                   case RIGHT:
 3369                       if (rects[leadingTabIndex].y < viewRect.y) {
 3370                           leadingTabIndex++;
 3371                       }
 3372                       break;
 3373                   }
 3374               }
 3375               Insets contentInsets = getContentBorderInsets(tabPlacement);
 3376               switch(tabPlacement) {
 3377                 case LEFT:
 3378                     tabPane.repaint(vpRect.x+vpRect.width, vpRect.y,
 3379                                     contentInsets.left, vpRect.height);
 3380                     scrollBackwardButton.setEnabled(
 3381                             viewRect.y > 0 && leadingTabIndex > 0);
 3382                     scrollForwardButton.setEnabled(
 3383                             leadingTabIndex < tabCount-1 &&
 3384                             viewSize.height-viewRect.y > viewRect.height);
 3385                     break;
 3386                 case RIGHT:
 3387                     tabPane.repaint(vpRect.x-contentInsets.right, vpRect.y,
 3388                                     contentInsets.right, vpRect.height);
 3389                     scrollBackwardButton.setEnabled(
 3390                             viewRect.y > 0 && leadingTabIndex > 0);
 3391                     scrollForwardButton.setEnabled(
 3392                             leadingTabIndex < tabCount-1 &&
 3393                             viewSize.height-viewRect.y > viewRect.height);
 3394                     break;
 3395                 case BOTTOM:
 3396                     tabPane.repaint(vpRect.x, vpRect.y-contentInsets.bottom,
 3397                                     vpRect.width, contentInsets.bottom);
 3398                     scrollBackwardButton.setEnabled(
 3399                             viewRect.x > 0 && leadingTabIndex > 0);
 3400                     scrollForwardButton.setEnabled(
 3401                             leadingTabIndex < tabCount-1 &&
 3402                             viewSize.width-viewRect.x > viewRect.width);
 3403                     break;
 3404                 case TOP:
 3405                 default:
 3406                     tabPane.repaint(vpRect.x, vpRect.y+vpRect.height,
 3407                                     vpRect.width, contentInsets.top);
 3408                     scrollBackwardButton.setEnabled(
 3409                             viewRect.x > 0 && leadingTabIndex > 0);
 3410                     scrollForwardButton.setEnabled(
 3411                             leadingTabIndex < tabCount-1 &&
 3412                             viewSize.width-viewRect.x > viewRect.width);
 3413               }
 3414           }
 3415   
 3416           /**
 3417            * ActionListener for the scroll buttons.
 3418            */
 3419           public void actionPerformed(ActionEvent e) {
 3420               ActionMap map = tabPane.getActionMap();
 3421   
 3422               if (map != null) {
 3423                   String actionKey;
 3424   
 3425                   if (e.getSource() == scrollForwardButton) {
 3426                       actionKey = "scrollTabsForwardAction";
 3427                   }
 3428                   else {
 3429                       actionKey = "scrollTabsBackwardAction";
 3430                   }
 3431                   Action action = map.get(actionKey);
 3432   
 3433                   if (action != null && action.isEnabled()) {
 3434                       action.actionPerformed(new ActionEvent(tabPane,
 3435                           ActionEvent.ACTION_PERFORMED, null, e.getWhen(),
 3436                           e.getModifiers()));
 3437                   }
 3438               }
 3439           }
 3440   
 3441           public String toString() {
 3442               return "viewport.viewSize=" + viewport.getViewSize() + "\n" +
 3443                                 "viewport.viewRectangle="+viewport.getViewRect()+"\n"+
 3444                                 "leadingTabIndex="+leadingTabIndex+"\n"+
 3445                                 "tabViewPosition=" + tabViewPosition;
 3446           }
 3447   
 3448       }
 3449   
 3450       private class ScrollableTabViewport extends JViewport implements UIResource {
 3451           public ScrollableTabViewport() {
 3452               super();
 3453               setName("TabbedPane.scrollableViewport");
 3454               setScrollMode(SIMPLE_SCROLL_MODE);
 3455               setOpaque(tabPane.isOpaque());
 3456               Color bgColor = UIManager.getColor("TabbedPane.tabAreaBackground");
 3457               if (bgColor == null) {
 3458                   bgColor = tabPane.getBackground();
 3459               }
 3460               setBackground(bgColor);
 3461           }
 3462       }
 3463   
 3464       private class ScrollableTabPanel extends JPanel implements UIResource {
 3465           public ScrollableTabPanel() {
 3466               super(null);
 3467               setOpaque(tabPane.isOpaque());
 3468               Color bgColor = UIManager.getColor("TabbedPane.tabAreaBackground");
 3469               if (bgColor == null) {
 3470                   bgColor = tabPane.getBackground();
 3471               }
 3472               setBackground(bgColor);
 3473           }
 3474           public void paintComponent(Graphics g) {
 3475               super.paintComponent(g);
 3476               BasicTabbedPaneUI.this.paintTabArea(g, tabPane.getTabPlacement(),
 3477                                                   tabPane.getSelectedIndex());
 3478               if (tabScroller.croppedEdge.isParamsSet() && tabContainer == null) {
 3479                   Rectangle croppedRect = rects[tabScroller.croppedEdge.getTabIndex()];
 3480                   g.translate(croppedRect.x, croppedRect.y);
 3481                   tabScroller.croppedEdge.paintComponent(g);
 3482                   g.translate(-croppedRect.x, -croppedRect.y);
 3483               }
 3484           }
 3485   
 3486           public void doLayout() {
 3487               if (getComponentCount() > 0) {
 3488                   Component child = getComponent(0);
 3489                   child.setBounds(0, 0, getWidth(), getHeight());
 3490               }
 3491           }
 3492       }
 3493   
 3494       private class ScrollableTabButton extends BasicArrowButton implements UIResource,
 3495                                                                               SwingConstants {
 3496           public ScrollableTabButton(int direction) {
 3497               super(direction,
 3498                     UIManager.getColor("TabbedPane.selected"),
 3499                     UIManager.getColor("TabbedPane.shadow"),
 3500                     UIManager.getColor("TabbedPane.darkShadow"),
 3501                     UIManager.getColor("TabbedPane.highlight"));
 3502           }
 3503       }
 3504   
 3505   
 3506   // Controller: event listeners
 3507   
 3508       private class Handler implements ChangeListener, ContainerListener,
 3509                     FocusListener, MouseListener, MouseMotionListener,
 3510                     PropertyChangeListener {
 3511           //
 3512           // PropertyChangeListener
 3513           //
 3514           public void propertyChange(PropertyChangeEvent e) {
 3515               JTabbedPane pane = (JTabbedPane)e.getSource();
 3516               String name = e.getPropertyName();
 3517               boolean isScrollLayout = scrollableTabLayoutEnabled();
 3518               if (name == "mnemonicAt") {
 3519                   updateMnemonics();
 3520                   pane.repaint();
 3521               }
 3522               else if (name == "displayedMnemonicIndexAt") {
 3523                   pane.repaint();
 3524               }
 3525               else if (name =="indexForTitle") {
 3526                   calculatedBaseline = false;
 3527                   Integer index = (Integer) e.getNewValue();
 3528                   // remove the current index
 3529                   // to let updateHtmlViews() insert the correct one
 3530                   if (htmlViews != null) {
 3531                       htmlViews.removeElementAt(index);
 3532                   }
 3533                   updateHtmlViews(index);
 3534               } else if (name == "tabLayoutPolicy") {
 3535                   BasicTabbedPaneUI.this.uninstallUI(pane);
 3536                   BasicTabbedPaneUI.this.installUI(pane);
 3537                   calculatedBaseline = false;
 3538               } else if (name == "tabPlacement") {
 3539                   if (scrollableTabLayoutEnabled()) {
 3540                       tabScroller.createButtons();
 3541                   }
 3542                   calculatedBaseline = false;
 3543               } else if (name == "opaque" && isScrollLayout) {
 3544                   boolean newVal = ((Boolean)e.getNewValue()).booleanValue();
 3545                   tabScroller.tabPanel.setOpaque(newVal);
 3546                   tabScroller.viewport.setOpaque(newVal);
 3547               } else if (name == "background" && isScrollLayout) {
 3548                   Color newVal = (Color)e.getNewValue();
 3549                   tabScroller.tabPanel.setBackground(newVal);
 3550                   tabScroller.viewport.setBackground(newVal);
 3551                   Color newColor = selectedColor == null ? newVal : selectedColor;
 3552                   tabScroller.scrollForwardButton.setBackground(newColor);
 3553                   tabScroller.scrollBackwardButton.setBackground(newColor);
 3554               } else if (name == "indexForTabComponent") {
 3555                   if (tabContainer != null) {
 3556                       tabContainer.removeUnusedTabComponents();
 3557                   }
 3558                   Component c = tabPane.getTabComponentAt(
 3559                           (Integer)e.getNewValue());
 3560                   if (c != null) {
 3561                       if (tabContainer == null) {
 3562                           installTabContainer();
 3563                       } else {
 3564                           tabContainer.add(c);
 3565                       }
 3566                   }
 3567                   tabPane.revalidate();
 3568                   tabPane.repaint();
 3569                   calculatedBaseline = false;
 3570               } else if (name == "indexForNullComponent") {
 3571                   isRunsDirty = true;
 3572                   updateHtmlViews((Integer)e.getNewValue());
 3573               } else if (name == "font") {
 3574                   calculatedBaseline = false;
 3575               }
 3576           }
 3577   
 3578           private void updateHtmlViews(int index) {
 3579               String title = tabPane.getTitleAt(index);
 3580               boolean isHTML = BasicHTML.isHTMLString(title);
 3581               if (isHTML) {
 3582                   if (htmlViews==null) {    // Initialize vector
 3583                       htmlViews = createHTMLVector();
 3584                   } else {                  // Vector already exists
 3585                       View v = BasicHTML.createHTMLView(tabPane, title);
 3586                       htmlViews.insertElementAt(v, index);
 3587                   }
 3588               } else {                             // Not HTML
 3589                   if (htmlViews!=null) {           // Add placeholder
 3590                       htmlViews.insertElementAt(null, index);
 3591                   }                                // else nada!
 3592               }
 3593               updateMnemonics();
 3594           }
 3595   
 3596           //
 3597           // ChangeListener
 3598           //
 3599           public void stateChanged(ChangeEvent e) {
 3600               JTabbedPane tabPane = (JTabbedPane)e.getSource();
 3601               tabPane.revalidate();
 3602               tabPane.repaint();
 3603   
 3604               setFocusIndex(tabPane.getSelectedIndex(), false);
 3605   
 3606               if (scrollableTabLayoutEnabled()) {
 3607                   int index = tabPane.getSelectedIndex();
 3608                   if (index < rects.length && index != -1) {
 3609                       tabScroller.tabPanel.scrollRectToVisible(
 3610                               (Rectangle)rects[index].clone());
 3611                   }
 3612               }
 3613           }
 3614   
 3615           //
 3616           // MouseListener
 3617           //
 3618           public void mouseClicked(MouseEvent e) {
 3619           }
 3620   
 3621           public void mouseReleased(MouseEvent e) {
 3622           }
 3623   
 3624           public void mouseEntered(MouseEvent e) {
 3625               setRolloverTab(e.getX(), e.getY());
 3626           }
 3627   
 3628           public void mouseExited(MouseEvent e) {
 3629               setRolloverTab(-1);
 3630           }
 3631   
 3632           public void mousePressed(MouseEvent e) {
 3633               if (!tabPane.isEnabled()) {
 3634                   return;
 3635               }
 3636               int tabIndex = tabForCoordinate(tabPane, e.getX(), e.getY());
 3637               if (tabIndex >= 0 && tabPane.isEnabledAt(tabIndex)) {
 3638                   if (tabIndex != tabPane.getSelectedIndex()) {
 3639                       // Clicking on unselected tab, change selection, do NOT
 3640                       // request focus.
 3641                       // This will trigger the focusIndex to change by way
 3642                       // of stateChanged.
 3643                       tabPane.setSelectedIndex(tabIndex);
 3644                   }
 3645                   else if (tabPane.isRequestFocusEnabled()) {
 3646                       // Clicking on selected tab, try and give the tabbedpane
 3647                       // focus.  Repaint will occur in focusGained.
 3648                       tabPane.requestFocus();
 3649                   }
 3650               }
 3651           }
 3652   
 3653           //
 3654           // MouseMotionListener
 3655           //
 3656           public void mouseDragged(MouseEvent e) {
 3657           }
 3658   
 3659           public void mouseMoved(MouseEvent e) {
 3660               setRolloverTab(e.getX(), e.getY());
 3661           }
 3662   
 3663           //
 3664           // FocusListener
 3665           //
 3666           public void focusGained(FocusEvent e) {
 3667              setFocusIndex(tabPane.getSelectedIndex(), true);
 3668           }
 3669           public void focusLost(FocusEvent e) {
 3670              repaintTab(focusIndex);
 3671           }
 3672   
 3673   
 3674           //
 3675           // ContainerListener
 3676           //
 3677       /* GES 2/3/99:
 3678          The container listener code was added to support HTML
 3679          rendering of tab titles.
 3680   
 3681          Ideally, we would be able to listen for property changes
 3682          when a tab is added or its text modified.  At the moment
 3683          there are no such events because the Beans spec doesn't
 3684          allow 'indexed' property changes (i.e. tab 2's text changed
 3685          from A to B).
 3686   
 3687          In order to get around this, we listen for tabs to be added
 3688          or removed by listening for the container events.  we then
 3689          queue up a runnable (so the component has a chance to complete
 3690          the add) which checks the tab title of the new component to see
 3691          if it requires HTML rendering.
 3692   
 3693          The Views (one per tab title requiring HTML rendering) are
 3694          stored in the htmlViews Vector, which is only allocated after
 3695          the first time we run into an HTML tab.  Note that this vector
 3696          is kept in step with the number of pages, and nulls are added
 3697          for those pages whose tab title do not require HTML rendering.
 3698   
 3699          This makes it easy for the paint and layout code to tell
 3700          whether to invoke the HTML engine without having to check
 3701          the string during time-sensitive operations.
 3702   
 3703          When we have added a way to listen for tab additions and
 3704          changes to tab text, this code should be removed and
 3705          replaced by something which uses that.  */
 3706   
 3707           public void componentAdded(ContainerEvent e) {
 3708               JTabbedPane tp = (JTabbedPane)e.getContainer();
 3709               Component child = e.getChild();
 3710               if (child instanceof UIResource) {
 3711                   return;
 3712               }
 3713               isRunsDirty = true;
 3714               updateHtmlViews(tp.indexOfComponent(child));
 3715           }
 3716           public void componentRemoved(ContainerEvent e) {
 3717               JTabbedPane tp = (JTabbedPane)e.getContainer();
 3718               Component child = e.getChild();
 3719               if (child instanceof UIResource) {
 3720                   return;
 3721               }
 3722   
 3723               // NOTE 4/15/2002 (joutwate):
 3724               // This fix is implemented using client properties since there is
 3725               // currently no IndexPropertyChangeEvent.  Once
 3726               // IndexPropertyChangeEvents have been added this code should be
 3727               // modified to use it.
 3728               Integer indexObj =
 3729                   (Integer)tp.getClientProperty("__index_to_remove__");
 3730               if (indexObj != null) {
 3731                   int index = indexObj.intValue();
 3732                   if (htmlViews != null && htmlViews.size() > index) {
 3733                       htmlViews.removeElementAt(index);
 3734                   }
 3735                   tp.putClientProperty("__index_to_remove__", null);
 3736               }
 3737               isRunsDirty = true;
 3738               updateMnemonics();
 3739   
 3740               validateFocusIndex();
 3741           }
 3742       }
 3743   
 3744       /**
 3745        * This class should be treated as a &quot;protected&quot; inner class.
 3746        * Instantiate it only within subclasses of BasicTabbedPaneUI.
 3747        */
 3748       public class PropertyChangeHandler implements PropertyChangeListener {
 3749           // NOTE: This class exists only for backward compatability. All
 3750           // its functionality has been moved into Handler. If you need to add
 3751           // new functionality add it to the Handler, but make sure this
 3752           // class calls into the Handler.
 3753           public void propertyChange(PropertyChangeEvent e) {
 3754               getHandler().propertyChange(e);
 3755           }
 3756       }
 3757   
 3758       /**
 3759        * This class should be treated as a &quot;protected&quot; inner class.
 3760        * Instantiate it only within subclasses of BasicTabbedPaneUI.
 3761        */
 3762       public class TabSelectionHandler implements ChangeListener {
 3763           // NOTE: This class exists only for backward compatability. All
 3764           // its functionality has been moved into Handler. If you need to add
 3765           // new functionality add it to the Handler, but make sure this
 3766           // class calls into the Handler.
 3767           public void stateChanged(ChangeEvent e) {
 3768               getHandler().stateChanged(e);
 3769           }
 3770       }
 3771   
 3772       /**
 3773        * This class should be treated as a &quot;protected&quot; inner class.
 3774        * Instantiate it only within subclasses of BasicTabbedPaneUI.
 3775        */
 3776       public class MouseHandler extends MouseAdapter {
 3777           // NOTE: This class exists only for backward compatability. All
 3778           // its functionality has been moved into Handler. If you need to add
 3779           // new functionality add it to the Handler, but make sure this
 3780           // class calls into the Handler.
 3781           public void mousePressed(MouseEvent e) {
 3782               getHandler().mousePressed(e);
 3783           }
 3784       }
 3785   
 3786       /**
 3787        * This class should be treated as a &quot;protected&quot; inner class.
 3788        * Instantiate it only within subclasses of BasicTabbedPaneUI.
 3789        */
 3790       public class FocusHandler extends FocusAdapter {
 3791           // NOTE: This class exists only for backward compatability. All
 3792           // its functionality has been moved into Handler. If you need to add
 3793           // new functionality add it to the Handler, but make sure this
 3794           // class calls into the Handler.
 3795           public void focusGained(FocusEvent e) {
 3796               getHandler().focusGained(e);
 3797           }
 3798           public void focusLost(FocusEvent e) {
 3799               getHandler().focusLost(e);
 3800           }
 3801       }
 3802   
 3803       private Vector<View> createHTMLVector() {
 3804           Vector<View> htmlViews = new Vector<View>();
 3805           int count = tabPane.getTabCount();
 3806           if (count>0) {
 3807               for (int i=0 ; i<count; i++) {
 3808                   String title = tabPane.getTitleAt(i);
 3809                   if (BasicHTML.isHTMLString(title)) {
 3810                       htmlViews.addElement(BasicHTML.createHTMLView(tabPane, title));
 3811                   } else {
 3812                       htmlViews.addElement(null);
 3813                   }
 3814               }
 3815           }
 3816           return htmlViews;
 3817       }
 3818   
 3819       private class TabContainer extends JPanel implements UIResource {
 3820           private boolean notifyTabbedPane = true;
 3821   
 3822           public TabContainer() {
 3823               super(null);
 3824               setOpaque(false);
 3825           }
 3826   
 3827           public void remove(Component comp) {
 3828               int index = tabPane.indexOfTabComponent(comp);
 3829               super.remove(comp);
 3830               if (notifyTabbedPane && index != -1) {
 3831                   tabPane.setTabComponentAt(index, null);
 3832               }
 3833           }
 3834   
 3835           private void removeUnusedTabComponents() {
 3836               for (Component c : getComponents()) {
 3837                   if (!(c instanceof UIResource)) {
 3838                       int index = tabPane.indexOfTabComponent(c);
 3839                       if (index == -1) {
 3840                           super.remove(c);
 3841                       }
 3842                   }
 3843               }
 3844           }
 3845   
 3846           public boolean isOptimizedDrawingEnabled() {
 3847               return tabScroller != null && !tabScroller.croppedEdge.isParamsSet();
 3848           }
 3849   
 3850           public void doLayout() {
 3851               // We layout tabComponents in JTabbedPane's layout manager
 3852               // and use this method as a hook for repainting tabs
 3853               // to update tabs area e.g. when the size of tabComponent was changed
 3854               if (scrollableTabLayoutEnabled()) {
 3855                   tabScroller.tabPanel.repaint();
 3856                   tabScroller.updateView();
 3857               } else {
 3858                   tabPane.repaint(getBounds());
 3859               }
 3860           }
 3861       }
 3862   
 3863       private class CroppedEdge extends JPanel implements UIResource {
 3864           private Shape shape;
 3865           private int tabIndex;
 3866           private int cropline;
 3867           private int cropx, cropy;
 3868   
 3869           public CroppedEdge() {
 3870               setOpaque(false);
 3871           }
 3872   
 3873           public void setParams(int tabIndex, int cropline, int cropx, int cropy) {
 3874               this.tabIndex = tabIndex;
 3875               this.cropline = cropline;
 3876               this.cropx = cropx;
 3877               this.cropy = cropy;
 3878               Rectangle tabRect = rects[tabIndex];
 3879               setBounds(tabRect);
 3880               shape = createCroppedTabShape(tabPane.getTabPlacement(), tabRect, cropline);
 3881               if (getParent() == null && tabContainer != null) {
 3882                   tabContainer.add(this, 0);
 3883               }
 3884           }
 3885   
 3886           public void resetParams() {
 3887               shape = null;
 3888               if (getParent() == tabContainer && tabContainer != null) {
 3889                   tabContainer.remove(this);
 3890               }
 3891           }
 3892   
 3893           public boolean isParamsSet() {
 3894               return shape != null;
 3895           }
 3896   
 3897           public int getTabIndex() {
 3898               return tabIndex;
 3899           }
 3900   
 3901           public int getCropline() {
 3902               return cropline;
 3903           }
 3904   
 3905           public int getCroppedSideWidth() {
 3906               return 3;
 3907           }
 3908   
 3909           private Color getBgColor() {
 3910               Component parent = tabPane.getParent();
 3911               if (parent != null) {
 3912                   Color bg = parent.getBackground();
 3913                   if (bg != null) {
 3914                       return bg;
 3915                   }
 3916               }
 3917               return UIManager.getColor("control");
 3918           }
 3919   
 3920           protected void paintComponent(Graphics g) {
 3921               super.paintComponent(g);
 3922               if (isParamsSet() && g instanceof Graphics2D) {
 3923                   Graphics2D g2 = (Graphics2D) g;
 3924                   g2.clipRect(0, 0, getWidth(), getHeight());
 3925                   g2.setColor(getBgColor());
 3926                   g2.translate(cropx, cropy);
 3927                   g2.fill(shape);
 3928                   paintCroppedTabEdge(g);
 3929                   g2.translate(-cropx, -cropy);
 3930               }
 3931           }
 3932       }
 3933   }

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