Advertisement
Guest User

Untitled

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