home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 November / Chip_1998-11_cd.bin / tema / Cafe / jfc.bin / BasicTabbedPaneUI.java < prev    next >
Text File  |  1998-02-26  |  61KB  |  1,625 lines

  1. /*
  2.  * @(#)BasicTabbedPaneUI.java    1.51 98/02/02
  3.  *
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  *
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  *
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  *
  19.  */
  20.  
  21. package com.sun.java.swing.plaf.basic;
  22.  
  23. import com.sun.java.swing.*;
  24. import com.sun.java.swing.event.*;
  25. import java.awt.*;
  26. import java.awt.event.*;
  27. import com.sun.java.swing.plaf.*;
  28. import java.io.Serializable; 
  29.  
  30. /**
  31.  * A Windows L&F implementation of TabbedPaneUI.
  32.  * <p>
  33.  * Warning: serialized objects of this class will not be compatible with
  34.  * future swing releases.  The current serialization support is appropriate
  35.  * for short term storage or RMI between Swing1.0 applications.  It will
  36.  * not be possible to load serialized Swing1.0 objects with future releases
  37.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  38.  * baseline for the serialized form of Swing objects.
  39.  *
  40.  * @version 1.51 02/02/98
  41.  * @author Dave Moore
  42.  * @author Philip Milne
  43.  * @author Steve Wilson
  44.  * @author Tom Santos
  45.  * @author Amy Fowler
  46.  */
  47. public class BasicTabbedPaneUI extends TabbedPaneUI
  48.        implements LayoutManager, ChangeListener, Serializable, SwingConstants {
  49.  
  50. // Class variables 
  51.  
  52.     protected static int spacingHeight = 2;
  53.     protected static int spacingWidth = 4;
  54.     protected static int iconSpacingWidth = 4;
  55.     protected static int selectedTabRaisePad = 3;
  56.     protected static int selectedTabWidenPad = 4;
  57.     protected static Insets contentBorderInsets = new Insets(2,2,3,3);
  58.     protected static Insets tabsOnTopTabInsets = new Insets(3,2,0,2);
  59.     protected static Insets tabsOnLeftTabInsets = new Insets(2,3,2,0);
  60.     protected static Insets tabsOnBottomTabInsets = new Insets(0,2,3,2);
  61.     protected static Insets tabsOnRightTabInsets = new Insets(2,0,2,3);
  62.  
  63. // Instance variables
  64.  
  65.     protected int overlay = 2;
  66.     protected int xNudge = 0;
  67.     protected int yNudge = 0;
  68.  
  69.     protected Color tabHighlight;
  70.     protected Color tabShadow;
  71.     protected Color tabDarkShadow;
  72.     protected Color focus;
  73.  
  74. // Transient variables (recalculated as events arrive)
  75.  
  76.     protected int tabRuns[] = new int[10];
  77.     protected int runCount;
  78.     protected int selectedRun;
  79.     protected Rectangle rects[] = new Rectangle[0];
  80.  
  81.     protected int maxTabHeight;
  82.     protected int maxTabWidth;
  83.  
  84.     protected Component visibleComponent;
  85.  
  86. // Listeners
  87.     protected MouseListener mouseGetter;
  88.     protected FocusListener focusGetter;
  89.     protected ChangeListener tabChangeListener;
  90.  
  91. // UI creation
  92.  
  93.     public static ComponentUI createUI(JComponent c) {
  94.         return new BasicTabbedPaneUI();
  95.     }
  96.  
  97. // UI Installation/De-installation
  98.     
  99.     public void installUI(JComponent c) {
  100.         c.setLayout(this);
  101.         installDefaults(c);      
  102.         installListeners(c);
  103.         installKeyboardActions(c);
  104.     }
  105.  
  106.     public void uninstallUI(JComponent c) {
  107.         uninstallKeyboardActions(c);
  108.         uninstallListeners(c);
  109.         uninstallDefaults(c);
  110.         c.setLayout(null); 
  111.     }
  112.  
  113.     protected void installDefaults(JComponent c) {
  114.         LookAndFeel.installColorsAndFont(c, "TabbedPane.tabBackground",
  115.                                     "TabbedPane.tabForeground", "TabbedPane.font");     
  116.         tabHighlight = UIManager.getColor("TabbedPane.tabHighlight");
  117.         tabShadow = UIManager.getColor("TabbedPane.tabShadow");
  118.         tabDarkShadow = UIManager.getColor("TabbedPane.tabDarkShadow");
  119.         focus = UIManager.getColor("TabbedPane.focus");
  120.     }
  121.  
  122.     protected void uninstallDefaults(JComponent c) {
  123.     }
  124.  
  125.     protected void installListeners(JComponent c) {
  126.         if ((mouseGetter = createMouseListener(c)) != null) {
  127.             c.addMouseListener(mouseGetter);
  128.         }        
  129.         if ((focusGetter = createFocusListener(c)) != null) {
  130.             c.addFocusListener(focusGetter);
  131.         }
  132.         if ((tabChangeListener = createChangeListener(c)) != null) {            
  133.             ((JTabbedPane)c).addChangeListener(tabChangeListener);
  134.         }
  135.     }
  136.  
  137.     protected void uninstallListeners(JComponent c) {
  138.         if (mouseGetter != null) {
  139.             c.removeMouseListener(mouseGetter);
  140.             mouseGetter = null;
  141.         }
  142.         if (focusGetter != null) {
  143.             c.removeFocusListener(focusGetter);
  144.             focusGetter = null;
  145.         }
  146.         if (tabChangeListener != null) {
  147.             ((JTabbedPane)c).removeChangeListener(tabChangeListener);
  148.         }        
  149.     }
  150.  
  151.     protected MouseListener createMouseListener(JComponent c) {
  152.         return new MouseGetter();
  153.     }
  154.  
  155.     protected FocusListener createFocusListener(JComponent c) {
  156.         return new FocusGetter();
  157.     }
  158.  
  159.     protected ChangeListener createChangeListener(JComponent c) {
  160.         return this;
  161.     }
  162.  
  163.     protected void installKeyboardActions(JComponent c) {
  164.         final JTabbedPane pane = (JTabbedPane)c;
  165.         c.registerKeyboardAction(
  166.             new AbstractAction() {
  167.                 public void actionPerformed(ActionEvent e) {
  168.                     navigateSelectedTab(pane, pane.getTabPlacement(), EAST);
  169.                 }
  170.                 public boolean isEnabled() { 
  171.                     return true; 
  172.                 }
  173.             },
  174.             KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT,0), 
  175.             JComponent.WHEN_FOCUSED);
  176.         c.registerKeyboardAction(
  177.             new AbstractAction() {
  178.                 public void actionPerformed(ActionEvent e) {
  179.                     navigateSelectedTab(pane, pane.getTabPlacement(), WEST);
  180.                 }
  181.                 public boolean isEnabled() { 
  182.                     return true; 
  183.                 }
  184.             }, 
  185.             KeyStroke.getKeyStroke(KeyEvent.VK_LEFT,0), 
  186.             JComponent.WHEN_FOCUSED);
  187.         c.registerKeyboardAction(
  188.             new AbstractAction() {
  189.                 public void actionPerformed(ActionEvent e) {
  190.                     navigateSelectedTab(pane, pane.getTabPlacement(), NORTH);
  191.                 }
  192.  
  193.                 public boolean isEnabled() { 
  194.                     return true; 
  195.                 }
  196.             },
  197.             KeyStroke.getKeyStroke(KeyEvent.VK_UP,0), 
  198.             JComponent.WHEN_FOCUSED);
  199.         c.registerKeyboardAction(
  200.             new AbstractAction() {
  201.                 public void actionPerformed(ActionEvent e) {
  202.                     navigateSelectedTab(pane, pane.getTabPlacement(), SOUTH);
  203.                 }
  204.  
  205.                 public boolean isEnabled() { 
  206.                     return true; 
  207.                 }
  208.             },
  209.             KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,0), 
  210.             JComponent.WHEN_FOCUSED);
  211.     }
  212.  
  213.     protected void uninstallKeyboardActions(JComponent c) {
  214.         c.resetKeyboardActions();
  215.     }
  216.  
  217.  
  218.  
  219. // UI Rendering 
  220.  
  221.     public void paint(Graphics g, JComponent c) {
  222.         JTabbedPane pane = (JTabbedPane)c;
  223.         int selectedIndex = pane.getSelectedIndex();
  224.         int tabPlacement = pane.getTabPlacement();
  225.         int tabCount = pane.getTabCount();
  226.         if (tabCount != rects.length) {
  227.             calculateLayoutInfo(pane); 
  228.         }
  229.         Rectangle iconRect = new Rectangle(),
  230.                   textRect = new Rectangle();
  231.         Rectangle clipRect = g.getClipBounds();  
  232.  
  233.         Insets insets = pane.getInsets();
  234.  
  235.     //g.translate(insets.left, insets.top);
  236.  
  237.         // Paint tabRuns of tabs from back to front
  238.         for (int i = runCount - 1; i >= 0; i--) {
  239.             int start = tabRuns[i];
  240.             int next = tabRuns[(i == runCount - 1)? 0 : i + 1];
  241.             int end = (next != 0? next - 1: tabCount - 1);
  242.             for (int j = start; j <= end; j++) {
  243.                 if (rects[j].intersects(clipRect)) {
  244.                     paintTab(g, pane, tabPlacement, rects, j, iconRect, textRect);
  245.                 }
  246.             }
  247.         }
  248.  
  249.         // Paint selected tab if its in the front run
  250.         // since it may overlap other tabs
  251.         if (selectedIndex >= 0 && getRunForTab(pane, tabCount, selectedIndex) == 0) {
  252.             if (rects[selectedIndex].intersects(clipRect)) {
  253.                 paintTab(g, pane, tabPlacement, rects, selectedIndex, iconRect, textRect);
  254.             }
  255.         }
  256.  
  257.         // Paint content border
  258.         paintContentBorder(g, pane, tabPlacement, selectedIndex);
  259.  
  260.     //g.translate(-insets.left, -insets.top);
  261.     }
  262.  
  263.     protected void paintTab(Graphics g, JTabbedPane pane, int tabPlacement,
  264.                             Rectangle[] rects, int tabIndex, 
  265.                             Rectangle iconRect, Rectangle textRect) {
  266.         Rectangle tabRect = rects[tabIndex];
  267.         int selectedIndex = pane.getSelectedIndex();
  268.         boolean isSelected = selectedIndex == tabIndex;
  269.  
  270.     xNudge = calculateXNudge(pane, tabPlacement, tabIndex, isSelected);
  271.         yNudge = calculateYNudge(pane, tabPlacement, tabIndex, isSelected);
  272.  
  273.     paintTabBackground(g, pane, tabPlacement, tabIndex, tabRect.x, tabRect.y, 
  274.                            tabRect.width, tabRect.height, isSelected);
  275.         paintTabBorder(g, pane, tabPlacement, tabIndex, tabRect.x, tabRect.y, 
  276.                        tabRect.width, tabRect.height, isSelected);
  277.     
  278.         String title = pane.getTitleAt(tabIndex);
  279.         Font font = pane.getFont();
  280.         FontMetrics metrics = g.getFontMetrics(font);
  281.         Icon icon = getIconForTab(pane, tabIndex);
  282.  
  283.     layoutLabel(tabPlacement, metrics, title, icon, 
  284.                     tabRect, iconRect, textRect, isSelected);
  285.  
  286.     paintText(g, pane, tabPlacement, font, metrics, 
  287.                   tabIndex, title, textRect, isSelected);
  288.  
  289.     paintIcon(g, pane, tabPlacement, tabIndex, icon, iconRect, isSelected );
  290.  
  291.     paintFocusIndicator(g, pane, tabPlacement, rects, tabIndex, iconRect, textRect, isSelected);
  292.     }
  293.  
  294.     protected void layoutLabel(int tabPlacement, FontMetrics metrics, 
  295.                                String title, Icon icon, 
  296.                                Rectangle tabRect, Rectangle iconRect, 
  297.                                Rectangle textRect, boolean isSelected ) {
  298.         textRect.x = textRect.y = iconRect.x = iconRect.y = 0;
  299.         SwingUtilities.layoutCompoundLabel(metrics, title, icon,
  300.                                           SwingUtilities.CENTER,
  301.                                           SwingUtilities.CENTER,
  302.                                           SwingUtilities.CENTER,
  303.                                           SwingUtilities.RIGHT,
  304.                                           tabRect,
  305.                                           iconRect,
  306.                                           textRect,
  307.                                           iconSpacingWidth);
  308.     }
  309.  
  310.     protected void paintIcon(Graphics g, JTabbedPane pane, int tabPlacement,
  311.                              int tabIndex, Icon icon, Rectangle iconRect, 
  312.                              boolean isSelected ) {
  313.         if (icon != null) {
  314.             icon.paintIcon(pane, g, iconRect.x + xNudge, iconRect.y + yNudge);
  315.         }
  316.     }
  317.  
  318.     protected void paintText(Graphics g, JTabbedPane pane, int tabPlacement,
  319.                              Font font, FontMetrics metrics, int tabIndex,
  320.                              String title, Rectangle textRect, 
  321.                              boolean isSelected) {
  322.  
  323.         g.setFont(font);
  324.  
  325.         if (pane.isEnabledAt(tabIndex)) {
  326.             g.setColor(pane.getForegroundAt(tabIndex));
  327.             g.drawString(title,
  328.                      textRect.x + xNudge,
  329.                      textRect.y + metrics.getAscent() + yNudge);
  330.  
  331.         } else { // tab disabled
  332.             g.setColor(pane.getBackgroundAt(tabIndex).brighter());
  333.             g.drawString(title, 
  334.                          textRect.x + xNudge, textRect.y + metrics.getAscent() + yNudge);
  335.             g.setColor(pane.getBackgroundAt(tabIndex).darker());
  336.             g.drawString(title, 
  337.                          textRect.x + xNudge - 1, textRect.y + metrics.getAscent() + yNudge - 1);
  338.         }
  339.     }
  340.  
  341.     protected int calculateXNudge(JTabbedPane pane, int tabPlacement, 
  342.                                   int tabIndex, boolean isSelected) {
  343.         Rectangle tabRect = rects[tabIndex];
  344.         int nudge = 0;
  345.         switch(tabPlacement) {
  346.           case LEFT:
  347.               nudge = isSelected? -1 : 1;
  348.               break;
  349.           case RIGHT:
  350.               nudge = isSelected? 1 : -1;
  351.               break;
  352.           case BOTTOM:
  353.           case TOP:
  354.           default:
  355.               nudge = tabRect.width % 2;
  356.         }
  357.         return nudge;
  358.     }
  359.  
  360.     protected int calculateYNudge(JTabbedPane pane, int tabPlacement,
  361.                                   int tabIndex, boolean isSelected) {
  362.         Rectangle tabRect = rects[tabIndex];
  363.         int nudge = 0;
  364.         switch(tabPlacement) {
  365.            case BOTTOM:
  366.               nudge = isSelected? 1 : -1;
  367.               break;
  368.           case LEFT:
  369.           case RIGHT:
  370.               nudge = tabRect.height % 2;
  371.               break;
  372.           case TOP:
  373.           default:
  374.               nudge = isSelected? -1 : 1;;
  375.         }
  376.         return nudge;
  377.     }
  378.  
  379.     protected void paintFocusIndicator(Graphics g, JTabbedPane pane, int tabPlacement,
  380.                                        Rectangle[] rects, int tabIndex, 
  381.                                        Rectangle iconRect, Rectangle textRect,
  382.                                        boolean isSelected) {
  383.         Rectangle tabRect = rects[tabIndex];
  384.         if (pane.hasFocus() && isSelected) {
  385.             int x, y, w, h;
  386.         g.setColor(focus);
  387.             switch(tabPlacement) {
  388.               case LEFT:
  389.                   x = tabRect.x + 3;
  390.                   y = tabRect.y + 3;
  391.                   w = tabRect.width - 5;
  392.                   h = tabRect.height - 6;
  393.                   break;
  394.               case RIGHT:
  395.                   x = tabRect.x + 2;
  396.                   y = tabRect.y + 3;
  397.                   w = tabRect.width - 5;
  398.                   h = tabRect.height - 6;
  399.                   break;
  400.               case BOTTOM:
  401.                   x = tabRect.x + 3;
  402.                   y = tabRect.y + 2;
  403.                   w = tabRect.width - 6;
  404.                   h = tabRect.height - 5;
  405.                   break;
  406.               case TOP:
  407.               default:
  408.                   x = tabRect.x + 3;
  409.                   y = tabRect.y + 3;
  410.                   w = tabRect.width - 6;
  411.                   h = tabRect.height - 5;
  412.             }
  413.             BasicGraphicsUtils.drawDashedRect(g, x, y, w, h);
  414.         }
  415.     }
  416.  
  417.     Rectangle getFocusedTabBounds(JTabbedPane tp) {
  418.         if(rects.length != tp.getTabCount())
  419.             calculateLayoutInfo(tp);
  420.         return rects[tp.getSelectedIndex()];
  421.     }
  422.  
  423.     /**
  424.       * this function draws the border around each tab
  425.       * note that this function does now draw the background of the tab.
  426.       * that is done elsewhere
  427.       */
  428.     protected void paintTabBorder(Graphics g, JTabbedPane pane, int tabPlacement,
  429.                                   int tabIndex,
  430.                                   int x, int y, int w, int h, 
  431.                   boolean isSelected ) {
  432.         g.setColor(tabHighlight);  
  433.  
  434.         switch (tabPlacement) {
  435.           case LEFT:
  436.               g.drawLine(x+1, y+h-2, x+1, y+h-2); // bottom-left highlight
  437.               g.drawLine(x, y+2, x, y+h-3); // left highlight
  438.               g.drawLine(x+1, y+1, x+1, y+1); // top-left highlight
  439.               g.drawLine(x+2, y, x+w-1, y); // top highlight
  440.  
  441.               g.setColor(tabShadow);
  442.               g.drawLine(x+2, y+h-2, x+w-1, y+h-2); // bottom shadow
  443.  
  444.               g.setColor(tabDarkShadow);
  445.               g.drawLine(x+2, y+h-1, x+w-1, y+h-1); // bottom dark shadow
  446.               break;
  447.           case RIGHT:
  448.               g.drawLine(x, y, x+w-3, y); // top highlight
  449.  
  450.               g.setColor(tabShadow);
  451.               g.drawLine(x, y+h-2, x+w-3, y+h-2); // bottom shadow
  452.               g.drawLine(x+w-2, y+2, x+w-2, y+h-3); // right shadow
  453.  
  454.               g.setColor(tabDarkShadow);
  455.               g.drawLine(x+w-2, y+1, x+w-2, y+1); // top-right dark shadow
  456.               g.drawLine(x+w-2, y+h-2, x+w-2, y+h-2); // bottom-right dark shadow
  457.               g.drawLine(x+w-1, y+2, x+w-1, y+h-3); // right dark shadow
  458.               g.drawLine(x, y+h-1, x+w-3, y+h-1); // bottom dark shadow
  459.               break;              
  460.           case BOTTOM:
  461.               g.drawLine(x, y, x, y+h-3); // left highlight
  462.               g.drawLine(x+1, y+h-2, x+1, y+h-2); // bottom-left highlight
  463.  
  464.               g.setColor(tabShadow);
  465.               g.drawLine(x+2, y+h-2, x+w-3, y+h-2); // bottom shadow
  466.               g.drawLine(x+w-2, y, x+w-2, y+h-3); // right shadow
  467.  
  468.               g.setColor(tabDarkShadow);
  469.               g.drawLine(x+2, y+h-1, x+w-3, y+h-1); // bottom dark shadow
  470.               g.drawLine(x+w-2, y+h-2, x+w-2, y+h-2); // bottom-right dark shadow
  471.               g.drawLine(x+w-1, y, x+w-1, y+h-3); // right dark shadow
  472.               break;
  473.           case TOP:
  474.           default:           
  475.               g.drawLine(x, y+2, x, y+h-1); // left highlight
  476.               g.drawLine(x+1, y+1, x+1, y+1); // top-left highlight
  477.               g.drawLine(x+2, y, x+w-3, y); // top highlight
  478.  
  479.               g.setColor(tabShadow);  
  480.               g.drawLine(x+w-2, y+2, x+w-2, y+h-1); // right shadow
  481.  
  482.               g.setColor(tabDarkShadow); 
  483.               g.drawLine(x+w-1, y+2, x+w-1, y+h-1); // right dark-shadow
  484.               g.drawLine(x+w-2, y+1, x+w-2, y+1); // top-right shadow
  485.         }
  486.     }
  487.  
  488.     protected void paintTabBackground(Graphics g, JTabbedPane pane, int tabPlacement,
  489.                                       int tabIndex,
  490.                                       int x, int y, int w, int h, 
  491.                       boolean isSelected ) {
  492.         g.setColor(pane.getBackgroundAt(tabIndex));
  493.         switch(tabPlacement) {
  494.           case LEFT:
  495.               g.fillRect(x+1, y+1, w-2, h-3);
  496.               break;
  497.           case RIGHT:
  498.               g.fillRect(x, y+1, w-2, h-3);
  499.               break;
  500.           case BOTTOM:
  501.               g.fillRect(x+1, y, w-3, h-1);
  502.               break;
  503.           case TOP:
  504.           default:
  505.               g.fillRect(x+1, y+1, w-3, h-1);
  506.         }
  507.     }
  508.  
  509.     protected void paintContentBorder(Graphics g, JTabbedPane pane, int tabPlacement,
  510.                                       int selectedIndex) {
  511.         Rectangle bounds = pane.getBounds();
  512.         Insets insets = pane.getInsets();
  513.         int height = bounds.height - (insets.top + insets.bottom);
  514.         int width = bounds.width - (insets.left + insets.right);
  515.  
  516.         int x = 0;
  517.         int y = 0;
  518.         int w = width;
  519.         int h = height;
  520.  
  521.         switch(tabPlacement) {
  522.           case LEFT:
  523.               x = totalTabWidth(pane, tabPlacement, runCount);
  524.               w -= x;
  525.               break;
  526.           case RIGHT:
  527.               w -= totalTabWidth(pane, tabPlacement, runCount);
  528.               break;            
  529.           case BOTTOM: 
  530.               h -= totalTabHeight(pane, tabPlacement, runCount);
  531.               break;
  532.           case TOP:
  533.           default:
  534.               y = totalTabHeight(pane, tabPlacement, runCount);
  535.               h -= y;
  536.         }                
  537.         paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h);
  538.         paintContentBorderLeftEdge(g, tabPlacement, selectedIndex, x, y, w, h); 
  539.         paintContentBorderBottomEdge(g, tabPlacement, selectedIndex, x, y, w, h);
  540.         paintContentBorderRightEdge(g, tabPlacement, selectedIndex, x, y, w, h); 
  541.  
  542.     }
  543.      
  544.     protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
  545.                                          int selectedIndex, 
  546.                                          int x, int y, int w, int h) {
  547.  
  548.         g.setColor(tabHighlight);
  549.  
  550.         if (tabPlacement != TOP || selectedIndex < 0 || 
  551.             (rects[selectedIndex].y + rects[selectedIndex].height + 1 < y)) {
  552.             g.drawLine(x, y, x+w-2, y);
  553.         } else {
  554.             Rectangle selRect = rects[selectedIndex];
  555.  
  556.             g.drawLine(x, y, selRect.x - 1, y);
  557.             if (selRect.x + selRect.width < x + w - 2) {
  558.                 g.drawLine(selRect.x + selRect.width, y, 
  559.                            x+w-2, y);
  560.             } else {
  561.             g.setColor(tabShadow); 
  562.                 g.drawLine(x+w-2, y, x+w-2, y);
  563.             }
  564.         }
  565.     }
  566.  
  567.     protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
  568.                                                int selectedIndex,
  569.                                                int x, int y, int w, int h) { 
  570.         g.setColor(tabHighlight); 
  571.         if (tabPlacement != LEFT || selectedIndex < 0 ||
  572.            (rects[selectedIndex].x + rects[selectedIndex].width + 1< x)) {
  573.             g.drawLine(x, y, x, y+h-2);
  574.         } else {
  575.             Rectangle selRect = rects[selectedIndex];
  576.  
  577.             g.drawLine(x, y, x, selRect.y - 1);
  578.             if (selRect.y + selRect.height < y + h - 2) {
  579.                 g.drawLine(x, selRect.y + selRect.height, 
  580.                            x, y+h-2);
  581.             } 
  582.         }
  583.     }
  584.  
  585.     protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
  586.                                                int selectedIndex,
  587.                                                int x, int y, int w, int h) { 
  588.         g.setColor(tabShadow);
  589.         if (tabPlacement != BOTTOM || selectedIndex < 0 ||
  590.             (rects[selectedIndex].y - 1 > h)) {
  591.             g.drawLine(x+1, y+h-2, x+w-2, y+h-2);
  592.             g.setColor(tabDarkShadow);
  593.             g.drawLine(x, y+h-1, x+w-1, y+h-1);
  594.         } else {
  595.             Rectangle selRect = rects[selectedIndex];
  596.  
  597.             g.drawLine(x+1, y+h-2, selRect.x - 1, y+h-2);
  598.             g.setColor(tabDarkShadow);
  599.             g.drawLine(x, y+h-1, selRect.x - 1, y+h-1);
  600.             if (selRect.x + selRect.width < x + w - 2) {
  601.                 g.setColor(tabShadow);
  602.                 g.drawLine(selRect.x + selRect.width, y+h-2, x+w-2, y+h-2);
  603.                 g.setColor(tabDarkShadow);
  604.                 g.drawLine(selRect.x + selRect.width, y+h-1, x+w-1, y+h-1);
  605.             } 
  606.         }
  607.  
  608.     }
  609.  
  610.     protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
  611.                                                int selectedIndex,
  612.                                                int x, int y, int w, int h) { 
  613.  
  614.         g.setColor(tabShadow);
  615.         if (tabPlacement != RIGHT || selectedIndex < 0 ||
  616.             rects[selectedIndex].x - 1 > w) {
  617.             g.drawLine(x+w-2, y+1, x+w-2, y+h-3);
  618.             g.setColor(tabDarkShadow);
  619.             g.drawLine(x+w-1, y, x+w-1, y+h-1);
  620.         } else {
  621.             Rectangle selRect = rects[selectedIndex];
  622.  
  623.             g.drawLine(x+w-2, y+1, x+w-2, selRect.y - 1);
  624.             g.setColor(tabDarkShadow);
  625.             g.drawLine(x+w-1, y, x+w-1, selRect.y - 1);
  626.  
  627.             if (selRect.y + selRect.height < y + h - 2) {
  628.                 g.setColor(tabShadow);
  629.                 g.drawLine(x+w-2, selRect.y + selRect.height, 
  630.                            x+w-2, y+h-2);
  631.                 g.setColor(tabDarkShadow);
  632.                 g.drawLine(x+w-1, selRect.y + selRect.height, 
  633.                            x+w-1, y+h-2);
  634.             } 
  635.         }
  636.     }
  637.     
  638.  
  639. // LayoutManager methods
  640.  
  641.     public Dimension getPreferredSize(JComponent c) {
  642.         JTabbedPane pane = (JTabbedPane)c; 
  643.         return calculateSize(pane, pane.getTabPlacement(), false);
  644.     }
  645.  
  646. /* It is difficfult to find useful minima for the Tab dimensions.  If
  647. we try to answer the question "how much room is required to draw all
  648. of the Tabs" then the answer is a minimumm *area* and this cannot be
  649. written as a minimum height or width. If we could assume or be passed
  650. a maximum height then we could compute a minimum width and vice versa
  651. but neither of these are useful as they both prevent certain useful
  652. arranngements from being displayed even when the drawing code would be
  653. able to render them. Instead, we return "the minimum height and width
  654. needed to display the largest of all of the buttons above the largest
  655. of all the components".  */
  656.  
  657.     public Dimension getMinimumSize(JComponent c) {
  658.         JTabbedPane pane = (JTabbedPane)c; 
  659.         return calculateSize(pane, pane.getTabPlacement(), true);
  660.     }
  661.  
  662.  
  663.     protected Dimension calculateSize(JTabbedPane pane, int tabPlacement, boolean minimum) { 
  664.         Insets insets = pane.getInsets();
  665.         Insets borderInsets = getContentBorderInsets(pane, tabPlacement);
  666.  
  667.     Dimension zeroSize = new Dimension(0,0);
  668.         int height = borderInsets.top + borderInsets.bottom; 
  669.         int width = borderInsets.left + borderInsets.right;
  670.         int cWidth = 0;
  671.         int cHeight = 0;
  672.  
  673.         for(int i = 0; i < pane.getTabCount(); i++) {
  674.             Component component = pane.getComponentAt(i);
  675.             Dimension size = zeroSize;
  676.             if (component instanceof JComponent) { 
  677.                 /* There is currently some question over what
  678.                    Components should do when asked for sizing
  679.                    information in the case where the Component has no
  680.                    mechanism to find it's font. As of 9.1.97, some
  681.                    Components throw NullPointer exceptions when sent
  682.                    getMinimumSize().  Normally a component can ask
  683.                    it's parent for it's font but the TabbedPane
  684.                    currently does not add these components to their
  685.                    superview: so they are orphans unless they are the
  686.                    selected item.  JComponents have the right behavior
  687.                    anyway, but for those that might not, set the fonts
  688.                    of all children to our Font when their Font is
  689.                    null. 
  690.                    */
  691.                  
  692.                 if (component.getFont() == null) { 
  693.                     component.setFont(pane.getFont()); 
  694.                 }
  695.                 size = minimum? component.getMinimumSize() : 
  696.                                 component.getPreferredSize();
  697.                       
  698.                 if (size != null) {
  699.                     cHeight = Math.max(size.height, cHeight);
  700.                     cWidth = Math.max(size.width, cWidth);
  701.                 }
  702.             }
  703.         }
  704.         width += cWidth;
  705.         height += cHeight;
  706.         int tabExtent = 0;
  707.  
  708.         switch(tabPlacement) {
  709.           case LEFT:
  710.           case RIGHT:
  711.               tabExtent = preferredTotalTabWidth(pane, tabPlacement,
  712.                                                  getMetrics(pane), height);
  713.               width += tabExtent;
  714.               break;
  715.           case TOP:
  716.           case BOTTOM:
  717.           default:
  718.               tabExtent = preferredTotalTabHeight(pane, tabPlacement,
  719.                                                   getMetrics(pane), width);
  720.               height += tabExtent;
  721.               break;
  722.         }
  723. /*
  724. System.out.println("calculateSize: cSize="+cWidth+"x"+cHeight+
  725. " insets="+insets+" borderInsets="+borderInsets+" tabExtent="+tabExtent+
  726. " TOTAL="+(width + insets.left + insets.right)+"x"+(height + insets.bottom + insets.top)); 
  727.               
  728. */
  729.         return new Dimension(width + insets.left + insets.right, 
  730.                              height + insets.bottom + insets.top);
  731.  
  732.     }
  733.     
  734.     public Dimension getMaximumSize(JComponent c) {
  735.         return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
  736.     }
  737.  
  738.     public void addLayoutComponent(String name, Component comp) {}
  739.     
  740.     public void removeLayoutComponent(Component comp) {}
  741.     
  742.     public Dimension preferredLayoutSize(Container parent) {
  743.         return new Dimension(parent.getBounds().width, 
  744.                              parent.getBounds().height);
  745.     }
  746.  
  747.     public Dimension minimumLayoutSize(Container parent) {
  748.         return new Dimension(0,0);
  749.     }
  750.  
  751.     public void layoutContainer(Container parent) {
  752.         JTabbedPane pane = (JTabbedPane)parent;
  753.         layoutTabbedPane(pane, pane.getTabPlacement());
  754.     }
  755.  
  756.     protected void layoutTabbedPane(JTabbedPane pane, int tabPlacement) {
  757.         Insets insets = pane.getInsets();
  758.         Insets borderInsets = getContentBorderInsets(pane, tabPlacement);
  759.         int selectedIndex = pane.getSelectedIndex();
  760.         Window window = null;
  761.         Component focusedComponent;
  762.  
  763.         if (selectedIndex < 0) {
  764.             if (visibleComponent != null) {
  765.                 // The last tab was removed, so remove teh component
  766.                 setVisibleComponent(pane, null);
  767.             }
  768.         } else {
  769.             int cx, cy, cw, ch;
  770.             int totalTabWidth = 0;
  771.             int totalTabHeight = 0;
  772.  
  773.             Component selectedComponent = pane.getComponentAt(selectedIndex);
  774.             boolean shouldChangeFocus = false;
  775.  
  776.             if (selectedComponent != null) {
  777.                 if (selectedComponent.getParent() == null) {
  778.                     if(visibleComponent != null) {
  779.                         if (SwingUtilities.findFocusOwner(visibleComponent) != null) {
  780.                             shouldChangeFocus = true;
  781.                         }
  782.                     }
  783.                 }
  784.                 calculateLayoutInfo(pane); 
  785.                 Rectangle bounds = pane.getBounds();
  786.  
  787.                 setVisibleComponent(pane, selectedComponent); 
  788.  
  789.                 switch(tabPlacement) {
  790.                   case LEFT:
  791.                     totalTabWidth = totalTabWidth(pane, tabPlacement, runCount);
  792.                     cx = insets.left + totalTabWidth + borderInsets.left;
  793.                     cy = insets.top + borderInsets.top;
  794.                     break;
  795.                   case RIGHT:
  796.                     totalTabWidth = totalTabWidth(pane, tabPlacement, runCount);
  797.                     cx = insets.left + borderInsets.left;
  798.                     cy = insets.top + borderInsets.top;
  799.                     break;
  800.                   case BOTTOM:
  801.                     totalTabHeight = totalTabHeight(pane, tabPlacement, runCount);
  802.                     cx = insets.left + borderInsets.left;
  803.                     cy = insets.top + borderInsets.top;
  804.                     break;                   
  805.                   case TOP:
  806.                   default:
  807.                     totalTabHeight = totalTabHeight(pane, tabPlacement, runCount);
  808.                     cx = insets.left + borderInsets.left;
  809.                     cy = insets.top + totalTabHeight + borderInsets.top;
  810.                 }
  811.                 
  812.                 cw = bounds.width - totalTabWidth -
  813.                                  insets.left - insets.right -
  814.                                  borderInsets.left - borderInsets.right;
  815.                 ch = bounds.height - totalTabHeight -
  816.                                  insets.top - insets.bottom -
  817.                                  borderInsets.top - borderInsets.bottom;
  818.  
  819.                 selectedComponent.setBounds(cx, cy, cw, ch);
  820.                 selectedComponent.validate();
  821.                 if (shouldChangeFocus) {
  822.                     if (selectedComponent.isFocusTraversable()) {
  823.                         selectedComponent.requestFocus();
  824.                         shouldChangeFocus = false;
  825.                     } else if (selectedComponent instanceof JComponent) {
  826.                         if (((JComponent)selectedComponent).requestDefaultFocus())
  827.                             shouldChangeFocus = false;
  828.                     }
  829.  
  830.                     if (shouldChangeFocus) {
  831.                         /* Need to change the focus but we have no candidate.
  832.                          * Let's set the focus on the tabbed pane itself
  833.                          */
  834.                         pane.requestFocus();
  835.                     }
  836.                 }
  837.             }
  838.         }
  839.     }
  840.  
  841.  
  842. // TabbedPaneUI methods
  843.  
  844.     public void stateChanged(ChangeEvent e) {
  845.         JTabbedPane pane = (JTabbedPane)e.getSource();
  846.         pane.doLayout();
  847.         pane.repaint();
  848.     }
  849.  
  850.     protected Component getVisibleComponent(JTabbedPane pane) {
  851.         int count = pane.getTabCount();
  852.  
  853.         while (count-- > 0) {
  854.             Component component = pane.getComponentAt(count);
  855.  
  856.             if (component != null && component.getParent() == pane) {
  857.                 return component;
  858.             }
  859.         }
  860.         return null;
  861.     }
  862.  
  863.     protected void setVisibleComponent(JTabbedPane pane, Component component) {
  864.         if (visibleComponent == component) {
  865.             return;
  866.         }
  867.         if (visibleComponent != null) {
  868.             pane.remove(visibleComponent);
  869.         }
  870.         if (component != null) {
  871.             pane.add(component);
  872.         }
  873.         visibleComponent = component;
  874.     }
  875.  
  876.     protected void calculateLayoutInfo(JTabbedPane pane) {
  877.         calculateLayoutInfo(pane, pane.getTabPlacement());
  878.     }
  879.  
  880.     protected void calculateLayoutInfo(JTabbedPane pane, int tabPlacement) {
  881.         Font font = pane.getFont();
  882.         int tabCount = pane.getTabCount();
  883.         int selectedIndex = pane.getSelectedIndex();
  884.  
  885.         assureRectsCreated(tabCount);
  886.  
  887.         arrangeTabs(pane, tabPlacement, tabCount, font); 
  888.             
  889.     padSelectedTab(pane, tabPlacement, selectedIndex);
  890.     }
  891.  
  892.     protected void assureRectsCreated(int tabCount) {
  893.     int rectArrayLen = rects.length; 
  894.     if (tabCount != rectArrayLen ) {
  895.         Rectangle[] tempRectArray = new Rectangle[tabCount];
  896.         System.arraycopy(rects, 0, tempRectArray, 0, 
  897.                              Math.min(rectArrayLen, tabCount));
  898.         rects = tempRectArray;
  899.         for (int rectIndex = rectArrayLen; rectIndex < tabCount; rectIndex++) {
  900.             rects[rectIndex] = new Rectangle();
  901.         }
  902.     } 
  903.  
  904.     }
  905.  
  906.     protected void expandTabRunsArray() {
  907.         int rectLen = tabRuns.length;
  908.         int[] newArray = new int[rectLen+10];
  909.         System.arraycopy(tabRuns, 0, newArray, 0, runCount);
  910.         tabRuns = newArray;
  911.     }
  912.  
  913.     protected void arrangeTabs(JTabbedPane pane, int tabPlacement, int tabCount,
  914.                                Font font) {
  915.         FontMetrics metrics = Toolkit.getDefaultToolkit().getFontMetrics(font);
  916.         Dimension size = pane.getSize();
  917.         Insets insets = pane.getInsets(); 
  918.         Insets tabInsets = getTabAreaInsets(pane, tabPlacement);
  919.         int fontHeight = metrics.getHeight();
  920.         int selectedIndex = pane.getSelectedIndex();
  921.         int overlay;
  922.         int i, j;
  923.         int x, y;
  924.         int returnAt;
  925.         boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
  926.  
  927.         switch(tabPlacement) {
  928.           case LEFT:
  929.               maxTabWidth = maxTabWidth(pane, metrics);
  930.               x = insets.left + tabInsets.left;
  931.               y = insets.top + tabInsets.top;
  932.               returnAt = size.height - (insets.bottom + tabInsets.bottom);
  933.               break;
  934.           case RIGHT:
  935.               maxTabWidth = maxTabWidth(pane, metrics);
  936.               x = size.width - insets.right - tabInsets.right - maxTabWidth;
  937.               y = insets.top + tabInsets.top;
  938.               returnAt = size.height - (insets.bottom + tabInsets.bottom);
  939.               break;
  940.           case BOTTOM:
  941.               maxTabHeight = maxTabHeight(pane, metrics);
  942.               x = insets.left + tabInsets.left;
  943.               y = size.height - insets.bottom - tabInsets.bottom - maxTabHeight;
  944.               returnAt = size.width - (insets.right + tabInsets.right);
  945.               break;
  946.           case TOP:
  947.           default:
  948.               maxTabHeight = maxTabHeight(pane, metrics);
  949.               x = insets.left + tabInsets.left;
  950.               y = insets.top + tabInsets.top;
  951.               returnAt = size.width - (insets.right + tabInsets.right);
  952.         }
  953.  
  954.         overlay = getTabOverlay(pane, tabPlacement);
  955.  
  956.         runCount = 0;
  957.         selectedRun = -1;
  958.  
  959.         Rectangle rect;
  960.         // Run through tabs and partition them into runs
  961.         for (i = 0; i < tabCount; i++) {
  962.         rect = rects[i];
  963.  
  964.             if (!verticalTabRuns) {
  965.                // Tabs on TOP or BOTTOM....
  966.                 if (i > 0) {
  967.                     rect.x = rects[i-1].x + rects[i-1].width;
  968.                 } else {
  969.                     tabRuns[0] = 0;
  970.                     runCount = 1;
  971.                     maxTabWidth = 0;
  972.                     rect.x = x;
  973.                 }
  974.                 rect.width = tabWidth(pane, i, metrics);
  975.                 maxTabWidth = Math.max(maxTabWidth, rect.width);
  976.  
  977.                 // Never move a TAB down a run if it is in the first column. 
  978.                 // Even if there isn't enough room, moving it to a fresh 
  979.                 // line won't help.
  980.                 if (rect.x != 2 + insets.left && rect.x + rect.width > returnAt) {
  981.                     if (runCount > tabRuns.length - 1) {
  982.                         expandTabRunsArray();
  983.                     }
  984.                     tabRuns[runCount] = i;
  985.                     runCount++;
  986.                     rect.x = x;
  987.                 }
  988.                 // Initialize y position in case there's just one run
  989.                 rect.y = y;
  990.                 rect.height = maxTabHeight/* - 2*/;
  991.  
  992.             } else {
  993.                 // Tabs on LEFT or RIGHT...
  994.                 if (i > 0) {
  995.                     rect.y = rects[i-1].y + rects[i-1].height;
  996.                 } else {
  997.                     tabRuns[0] = 0;
  998.                     runCount = 1;
  999.                     maxTabHeight = 0;
  1000.                     rect.y = y;
  1001.                 }
  1002.                 rect.height = tabHeight(pane, i, fontHeight);
  1003.                 maxTabHeight = Math.max(maxTabHeight, rect.height);
  1004.  
  1005.                 // Never move a TAB over a run if it is in the first run. 
  1006.                 // Even if there isn't enough room, moving it to a fresh 
  1007.                 // column won't help.
  1008.                 if (rect.y != 2 + insets.top && rect.y + rect.height > returnAt) {
  1009.                     if (runCount > tabRuns.length - 1) {
  1010.                         expandTabRunsArray();
  1011.                     }
  1012.                     tabRuns[runCount] = i;
  1013.                     runCount++;
  1014.                     rect.y = y;
  1015.                 }
  1016.                 // Initialize x position in case there's just one column
  1017.                 rect.x = x;
  1018.                 rect.width = maxTabWidth/* - 2*/;
  1019.  
  1020.             }            
  1021.             if (i == selectedIndex) {
  1022.                 selectedRun = runCount - 1;
  1023.             }
  1024.         }
  1025.  
  1026.  
  1027.         if (runCount > 1) {
  1028.             // Re-distribute tabs in case last run has leftover space
  1029.             normalizeTabRuns(pane, tabPlacement, tabCount, verticalTabRuns? y : x, returnAt);
  1030.  
  1031.             selectedRun = getRunForTab(pane, tabCount, selectedIndex);
  1032.  
  1033.             // Rotate run array so that selected run is first
  1034.             rotateTabRuns(pane, tabPlacement, selectedRun);
  1035.         }
  1036.  
  1037.         // Step through runs from back to front to calculate
  1038.         // tab y locations and to pad runs appropriately
  1039.         for (i = runCount - 1; i >= 0; i--) {
  1040.             int start = tabRuns[i];
  1041.             int next = tabRuns[i == (runCount - 1)? 0 : i + 1];
  1042.             int end = (next != 0? next - 1 : tabCount - 1);
  1043.             if (!verticalTabRuns) {
  1044.                 for (j = start; j <= end; j++) {
  1045.                     rect = rects[j];
  1046.                     rect.y = y;
  1047.                     rect.x += getRunIndent(pane, tabPlacement, i);
  1048.                 }
  1049.                 if (shouldPadRun(pane, tabPlacement, i)) {
  1050.                     padRun(pane, tabPlacement, start, end, returnAt);
  1051.                 }
  1052.                 if (tabPlacement == BOTTOM) {
  1053.                     y -= (maxTabHeight - overlay);
  1054.                 } else {
  1055.                     y += (maxTabHeight - overlay);
  1056.                 }
  1057.             } else {
  1058.                 for (j = start; j <= end; j++) {
  1059.                     rect = rects[j];
  1060.                     rect.x = x;
  1061.                     rect.y += getRunIndent(pane, tabPlacement, i);
  1062.                 }
  1063.                 if (shouldPadRun(pane, tabPlacement, i)) {
  1064.                     padRun(pane, tabPlacement, start, end, returnAt);
  1065.                 }
  1066.                 if (tabPlacement == RIGHT) {
  1067.                     x -= (maxTabWidth - overlay);
  1068.                 } else {
  1069.                     x += (maxTabWidth - overlay);
  1070.                 }
  1071.             }           
  1072.         }
  1073.     }
  1074.  
  1075.     protected int getTabOverlay(JTabbedPane pane, int tabPlacement) {
  1076.         return overlay;
  1077.     }
  1078.  
  1079.     protected int getRunForTab(JTabbedPane pane, int tabCount, int tabIndex) {
  1080.         for (int i = 0; i < runCount; i++) {
  1081.             int first = tabRuns[i];
  1082.             int last = lastIndexInRun(i, tabCount);
  1083.             if (tabIndex >= first && tabIndex <= last) {
  1084.                 return i;
  1085.             }
  1086.         }
  1087.         return 0;
  1088.     }
  1089.  
  1090.    /* 
  1091.     * Rotates the run-index array so that the selected run is run[0]
  1092.     */
  1093.     protected void rotateTabRuns(JTabbedPane pane, int tabPlacement, int selectedRun) {
  1094.         for (int i = 0; i < selectedRun; i++) {
  1095.             int save = tabRuns[0];
  1096.             for (int j = 1; j < runCount; j++) { 
  1097.                 tabRuns[j - 1] = tabRuns[j];
  1098.             }
  1099.             tabRuns[runCount-1] = save;
  1100.         }        
  1101.     }
  1102.  
  1103.     protected int getRunIndent(JTabbedPane pane, int tabPlacement, int run) {
  1104.         return 0;
  1105.     }
  1106.  
  1107.     protected void normalizeTabRuns(JTabbedPane pane, int tabPlacement, int tabCount, 
  1108.                                      int start, int max) {
  1109.         boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
  1110.         int run = runCount - 1;
  1111.         boolean keepAdjusting = true;
  1112.         double weight = 1.25;
  1113.  
  1114.         // At this point the tab runs are packed to fit as many
  1115.         // tabs as possible, which can leave the last run with a lot
  1116.         // of extra space (resulting in very fat tabs on the last run).
  1117.         // So we'll attempt to distribute this extra space more evenly
  1118.         // across the runs in order to make the runs look more consistent.
  1119.         //
  1120.         // Starting with the last run, determine whether the last tab in
  1121.         // the previous run would fit (generously) in this run; if so,
  1122.         // move tab to current run and shift tabs accordingly.  Cycle
  1123.         // through remaining runs using the same algorithm.  
  1124.         //
  1125.         while (keepAdjusting) {
  1126.             int last = lastIndexInRun(run, tabCount);
  1127.             int prevLast = lastIndexInRun(run-1, tabCount);
  1128.             int end;
  1129.             int prevLastLen;
  1130.  
  1131.             if (!verticalTabRuns) {
  1132.                 end = rects[last].x + rects[last].width;
  1133.                 prevLastLen = (int)(maxTabWidth*weight);
  1134.             } else {
  1135.                 end = rects[last].y + rects[last].height;
  1136.                 prevLastLen = (int)(maxTabHeight*weight*2);
  1137.             }
  1138.  
  1139.             // Check if the run has enough extra space to fit the last tab
  1140.             // from the previous row...
  1141.             if (max - end > prevLastLen) {
  1142.  
  1143.                 // Insert tab from previous row and shift rest over
  1144.                 tabRuns[run] = prevLast;
  1145.                 if (!verticalTabRuns) {
  1146.                     rects[prevLast].x = start;
  1147.                 } else {
  1148.                     rects[prevLast].y = start;
  1149.                 }
  1150.                 for (int i = prevLast+1; i <= last; i++) {
  1151.                     if (!verticalTabRuns) {
  1152.                         rects[i].x = rects[i-1].x + rects[i-1].width;
  1153.                     } else {
  1154.                         rects[i].y = rects[i-1].y + rects[i-1].height;
  1155.                     }
  1156.                 } 
  1157.  
  1158.             } else if (run == runCount - 1) {
  1159.                 // no more room left in last run, so we're done!
  1160.                 keepAdjusting = false;
  1161.             }
  1162.             if (run - 1 > 0) {
  1163.                 // check previous run next...
  1164.                 run -= 1;
  1165.             } else {
  1166.                 // check last run again...but require a higher ratio
  1167.                 // of extraspace-to-tabsize because we don't want to
  1168.                 // end up with too many tabs on the last run!
  1169.                 run = runCount - 1;
  1170.                 weight += .25;
  1171.             }
  1172.         }                       
  1173.     }
  1174.  
  1175.     protected int lastIndexInRun(int run, int tabCount) {
  1176.         if (runCount == 1) {
  1177.             return tabCount - 1;
  1178.         }
  1179.         int nextRun = (run == runCount - 1? 0 : run + 1);
  1180.         if (tabRuns[nextRun] == 0) {
  1181.             return tabCount - 1;
  1182.         }
  1183.         return tabRuns[nextRun]-1;
  1184.     }
  1185.  
  1186.     protected boolean shouldPadRun(JTabbedPane pane, int tabPlacement, int run) {
  1187.         return runCount > 1;
  1188.     }
  1189.  
  1190.     protected void padRun(JTabbedPane pane, int tabPlacement, 
  1191.                           int start, int end, int max) {
  1192.         Rectangle lastRect = rects[end];
  1193.         if (tabPlacement == TOP || tabPlacement == BOTTOM) {
  1194.             int runWidth = (lastRect.x + lastRect.width) - rects[start].x;
  1195.             int deltaWidth = max - (lastRect.x + lastRect.width);
  1196.             float factor = (float)deltaWidth / (float)runWidth;
  1197.  
  1198.             for (int j = start; j <= end; j++) {
  1199.                 Rectangle pastRect = rects[j];
  1200.                 if (j > start) {
  1201.                     pastRect.x = rects[j-1].x + rects[j-1].width;
  1202.                 }
  1203.                 pastRect.width += Math.round((float)pastRect.width * factor);
  1204.             }
  1205.             lastRect.width = max - lastRect.x;
  1206.         } else {
  1207.             int runHeight = (lastRect.y + lastRect.height) - rects[start].y;
  1208.             int deltaHeight = max - (lastRect.y + lastRect.height);
  1209.             float factor = (float)deltaHeight / (float)runHeight;
  1210.  
  1211.             for (int j = start; j <= end; j++) {
  1212.                 Rectangle pastRect = rects[j];
  1213.                 if (j > start) {
  1214.                     pastRect.y = rects[j-1].y + rects[j-1].height;
  1215.                 }
  1216.                 pastRect.height += Math.round((float)pastRect.height * factor);
  1217.             }
  1218.             lastRect.height = max - lastRect.y;
  1219.         }
  1220.     } 
  1221.  
  1222.     protected void padSelectedTab(JTabbedPane pane, int tabPlacement, int selectedIndex) {
  1223.  
  1224.         if (selectedIndex >= 0) {
  1225.             Rectangle selRect = rects[selectedIndex];
  1226.  
  1227.             switch(tabPlacement) {
  1228.               case LEFT:
  1229.                   selRect.x -= (selectedTabRaisePad-1);            
  1230.                   selRect.width += selectedTabRaisePad;
  1231.                   selRect.y -= (selectedTabWidenPad/2);
  1232.                   selRect.height += selectedTabWidenPad;
  1233.                   break;
  1234.               case RIGHT:
  1235.                   selRect.x -= 1;
  1236.                   selRect.width += selectedTabRaisePad;
  1237.                   selRect.y -= (selectedTabWidenPad/2);
  1238.                   selRect.height += selectedTabWidenPad;
  1239.                   break;
  1240.               case BOTTOM:
  1241.                   selRect.y -= 1;
  1242.                   selRect.height += selectedTabRaisePad;
  1243.                   selRect.x -= (selectedTabWidenPad/2);
  1244.                   selRect.width += selectedTabWidenPad;
  1245.                   break;
  1246.               case TOP:
  1247.               default:
  1248.                   selRect.y -= (selectedTabRaisePad-1);            
  1249.                   selRect.height += selectedTabRaisePad;
  1250.                   selRect.x -= (selectedTabWidenPad/2);
  1251.                   selRect.width += selectedTabWidenPad;
  1252.             }
  1253.         }
  1254.     } 
  1255.  
  1256.     protected int tabHeight(JTabbedPane pane, int tabIndex, int fontHeight) {
  1257.         int height = fontHeight;
  1258.         Icon icon = getIconForTab(pane, tabIndex);
  1259.  
  1260.         if (icon != null) {
  1261.             height = Math.max(height, icon.getIconHeight());
  1262.         }
  1263.         height += 2*spacingHeight+2;
  1264.  
  1265.         return height;
  1266.     } 
  1267.  
  1268.     protected int maxTabHeight(JTabbedPane pane) { 
  1269.         return maxTabHeight(pane, getMetrics(pane));
  1270.     }
  1271.  
  1272.     protected int maxTabHeight(JTabbedPane pane, FontMetrics metrics) {
  1273.         int tabCount = pane.getTabCount();
  1274.         int result = 0; 
  1275.         int fontHeight = metrics.getHeight();
  1276.         for(int i = 0; i < tabCount; i++) {
  1277.             result = Math.max(tabHeight(pane, i, fontHeight), result);
  1278.         }
  1279.         return result; 
  1280.     }
  1281.  
  1282.     protected int tabWidth(JTabbedPane pane, int tabIndex, FontMetrics metrics) {
  1283.         String title = pane.getTitleAt(tabIndex);
  1284.         Icon icon = getIconForTab(pane, tabIndex);
  1285.         int width = 2*spacingWidth + 3;
  1286.  
  1287.         if (icon != null) {
  1288.             width += icon.getIconWidth() + iconSpacingWidth;
  1289.         }
  1290.         width += metrics.stringWidth(title);
  1291.  
  1292.         return width;
  1293.     }
  1294.  
  1295.     protected int maxTabWidth(JTabbedPane pane) {
  1296.         return maxTabWidth(pane, getMetrics(pane));
  1297.     }
  1298.     
  1299.     protected int maxTabWidth(JTabbedPane pane, FontMetrics metrics) {
  1300.         int tabCount = pane.getTabCount();
  1301.         int result = 0; 
  1302.         for(int i = 0; i < tabCount; i++) {
  1303.             result = Math.max(tabWidth(pane, i, metrics), result);
  1304.         }
  1305.         return result; 
  1306.     }
  1307.  
  1308.     protected Icon getIconForTab(JTabbedPane pane, int tabIndex) {
  1309.         return (!pane.isEnabledAt(tabIndex) && 
  1310.                           pane.getDisabledIconAt(tabIndex) != null)?
  1311.                           pane.getDisabledIconAt(tabIndex) : pane.getIconAt(tabIndex);
  1312.     }
  1313.  
  1314.     protected Insets getContentBorderInsets(JTabbedPane pane, int tabPlacement) {
  1315.         return contentBorderInsets;
  1316.     }
  1317.  
  1318.     protected Insets getTabAreaInsets(JTabbedPane pane, int tabPlacement) {
  1319.         Insets insets;
  1320.         switch(tabPlacement) {
  1321.           case LEFT:
  1322.               insets = tabsOnLeftTabInsets;
  1323.               break;
  1324.           case BOTTOM:
  1325.               insets = tabsOnBottomTabInsets;
  1326.               break;
  1327.           case RIGHT:
  1328.               insets = tabsOnRightTabInsets;
  1329.               break;
  1330.           case TOP:
  1331.           default:
  1332.               insets = tabsOnTopTabInsets;
  1333.         }
  1334.         return insets;
  1335.     }
  1336.     
  1337.     protected FontMetrics getMetrics(JTabbedPane pane) {
  1338.         Font font = pane.getFont();
  1339.         return Toolkit.getDefaultToolkit().getFontMetrics(font);
  1340.     }
  1341.     
  1342.     public Rectangle getTabBounds(JTabbedPane pane, int i) {  
  1343.         calculateLayoutInfo(pane); 
  1344.     return rects[i];
  1345.     }
  1346.  
  1347.     public int getTabRunCount(JTabbedPane pane) {
  1348.         return runCount;
  1349.     }
  1350.  
  1351.     protected int totalTabHeight(JTabbedPane pane, int tabPlacement, int rowCount) {
  1352.         Insets tabInsets = getTabAreaInsets(pane, tabPlacement);
  1353.         int overlay = getTabOverlay(pane, tabPlacement);
  1354.         return rowCount * (maxTabHeight-overlay) + overlay + 
  1355.             tabInsets.top + tabInsets.bottom;
  1356.     }
  1357.  
  1358.     protected int totalTabWidth(JTabbedPane pane, int tabPlacement, int columnCount) { 
  1359.         Insets tabInsets = getTabAreaInsets(pane, tabPlacement);
  1360.         int overlay = getTabOverlay(pane, tabPlacement);
  1361.         return columnCount * (maxTabWidth-overlay) + overlay + 
  1362.             tabInsets.left + tabInsets.right;
  1363.     }
  1364.  
  1365.     protected int preferredTotalTabHeight(JTabbedPane pane, int tabPlacement,
  1366.                                           FontMetrics metrics, int width) {
  1367.         int tabCount = pane.getTabCount();
  1368.         int total = 0;
  1369.         if (tabCount > 0) {
  1370.             int rows = 1;
  1371.             int x = 0;
  1372.  
  1373.             maxTabHeight = maxTabHeight(pane, metrics);
  1374.         
  1375.             for (int i = 0; i < tabCount; i++) {
  1376.                 int tabWidth = tabWidth(pane, i, metrics);
  1377.  
  1378.                 if (x != 0 && x + tabWidth > width) { 
  1379.                     rows++;
  1380.                     x = 0;
  1381.                 }            
  1382.             }
  1383.             total = totalTabHeight(pane, tabPlacement, rows);
  1384.         }
  1385.         return total;
  1386.     }
  1387.  
  1388.     protected int preferredTotalTabWidth(JTabbedPane pane, int tabPlacement,
  1389.                                          FontMetrics metrics, int height) {
  1390.         int tabCount = pane.getTabCount();
  1391.         int total = 0;
  1392.         if (tabCount > 0) {
  1393.             int columns = 1;
  1394.             int y = 0;
  1395.             int fontHeight = metrics.getHeight();
  1396.  
  1397.             maxTabWidth = maxTabWidth(pane, metrics);
  1398.         
  1399.             for (int i = 0; i < tabCount; i++) {
  1400.                 int tabHeight = tabHeight(pane, i, fontHeight);
  1401.  
  1402.                 if (y != 0 && y + tabHeight > height) { 
  1403.                     columns++;
  1404.                     y = 0;
  1405.                 }
  1406.             }                    
  1407.             total = totalTabWidth(pane, tabPlacement, columns);
  1408.         }
  1409.         return total;
  1410.     }
  1411.  
  1412. // Tab Navigation methods
  1413.  
  1414.     protected void navigateSelectedTab(JTabbedPane pane, int tabPlacement, int direction) {
  1415.         int current = pane.getSelectedIndex();
  1416.         int tabCount = pane.getTabCount();
  1417.         int offset;
  1418.         switch(tabPlacement) {
  1419.           case LEFT:
  1420.           case RIGHT:
  1421.               switch(direction) {
  1422.                 case NORTH:
  1423.                     selectPrevTab(pane, current);
  1424.                     break;
  1425.                 case SOUTH:
  1426.                     selectNextTab(pane, current);
  1427.                     break;
  1428.                 case WEST:
  1429.                     offset = getTabRunOffset(pane, tabPlacement, tabCount, current, false);
  1430.                     selectAdjacentRunTab(pane, tabPlacement, current, offset);
  1431.                     break;
  1432.                 case EAST:
  1433.                     offset = getTabRunOffset(pane, tabPlacement, tabCount, current, true);
  1434.                     selectAdjacentRunTab(pane, tabPlacement, current, offset);
  1435.                     break;
  1436.                 default:
  1437.               }
  1438.               break;
  1439.           case BOTTOM:
  1440.           case TOP:
  1441.           default:
  1442.               switch(direction) {
  1443.                 case NORTH:
  1444.                     offset = getTabRunOffset(pane, tabPlacement, tabCount, current, false);
  1445.                     selectAdjacentRunTab(pane, tabPlacement, current, offset);
  1446.                     break;
  1447.                 case SOUTH:
  1448.                     offset = getTabRunOffset(pane, tabPlacement, tabCount, current, true);
  1449.                     selectAdjacentRunTab(pane, tabPlacement, current, offset);
  1450.                     break;
  1451.                 case EAST:
  1452.                     selectNextTab(pane, current);
  1453.                     break;
  1454.                 case WEST:
  1455.                     selectPrevTab(pane, current);
  1456.                     break;
  1457.                 default:
  1458.               }
  1459.         }
  1460.     }
  1461.  
  1462.     public int getPrevTabIndex(JTabbedPane pane, int base) {
  1463.         int tabIndex = base - 1;
  1464.         if (tabIndex < 0) {
  1465.             tabIndex = pane.getTabCount() - 1;
  1466.         }
  1467.         return tabIndex;            
  1468.     }
  1469.  
  1470.     public int getNextTabIndex(JTabbedPane pane, int base) {
  1471.         return (base+1)%pane.getTabCount();
  1472.     }
  1473.  
  1474.     public void selectNextTab(JTabbedPane pane, int current) {
  1475.         int tabIndex = getNextTabIndex(pane, current);
  1476.         
  1477.         while (!pane.isEnabledAt(tabIndex) && tabIndex != current) {
  1478.             tabIndex = getNextTabIndex(pane, tabIndex);
  1479.         }
  1480.         pane.setSelectedIndex(tabIndex);
  1481.     }
  1482.  
  1483.     public void selectPrevTab(JTabbedPane pane, int current) {
  1484.         int tabIndex = getPrevTabIndex(pane, current);
  1485.         
  1486.         while (!pane.isEnabledAt(tabIndex) && tabIndex != current) {
  1487.             tabIndex = getPrevTabIndex(pane, tabIndex);
  1488.         }
  1489.         pane.setSelectedIndex(tabIndex);
  1490.     }
  1491.  
  1492.     protected void selectAdjacentRunTab(JTabbedPane pane, int tabPlacement, 
  1493.                                         int tabIndex, int offset) {
  1494.         if ( runCount < 2 ) {
  1495.             return; 
  1496.         }
  1497.         int newIndex;
  1498.         Rectangle r = rects[tabIndex]; 
  1499.         switch(tabPlacement) {
  1500.           case LEFT:
  1501.           case RIGHT:
  1502.               newIndex = tabForCoordinate(pane, r.x + r.width/2 + offset,
  1503.                                           r.y + r.height/2);
  1504.               break;
  1505.           case BOTTOM:  
  1506.           case TOP:
  1507.           default:
  1508.               newIndex = tabForCoordinate(pane, r.x + r.width/2, 
  1509.                                         r.y + r.height/2 + offset);
  1510.         }
  1511.         if (newIndex != -1) {
  1512.             while (!pane.isEnabledAt(newIndex) && newIndex != tabIndex) {
  1513.                 newIndex = getNextTabIndex(pane, newIndex);
  1514.             }        
  1515.             pane.setSelectedIndex(newIndex);
  1516.         }
  1517.     }
  1518.  
  1519.     protected int getTabRunOffset(JTabbedPane pane, int tabPlacement, int tabCount, 
  1520.                                   int tabIndex, boolean forward) {
  1521.         int run = getRunForTab(pane, tabCount, tabIndex);
  1522.         int offset;
  1523.         switch(tabPlacement) {
  1524.           case LEFT:
  1525.               if (run == 0) {
  1526.                   offset = (forward? 
  1527.                             -(totalTabWidth(pane, tabPlacement, runCount)-maxTabWidth(pane)) :
  1528.                             -(maxTabWidth(pane)));
  1529.               } else if (run == runCount - 1) {
  1530.                   offset = (forward?
  1531.                             maxTabWidth(pane) :
  1532.                             totalTabWidth(pane, tabPlacement, runCount)-maxTabWidth(pane));
  1533.               } else {
  1534.                   offset = (forward? maxTabWidth(pane) : -(maxTabWidth(pane)));
  1535.               }
  1536.               break;
  1537.           case RIGHT:
  1538.               if (run == 0) {
  1539.                   offset = (forward? 
  1540.                             maxTabWidth(pane) :
  1541.                             totalTabWidth(pane, tabPlacement, runCount)-maxTabWidth(pane));
  1542.               } else if (run == runCount - 1) {
  1543.                   offset = (forward?
  1544.                             -(totalTabWidth(pane, tabPlacement, runCount)-maxTabWidth(pane)) :
  1545.                             -(maxTabWidth(pane)));
  1546.               } else {
  1547.                   offset = (forward? maxTabWidth(pane) : -(maxTabWidth(pane)));
  1548.               } 
  1549.               break;
  1550.           case BOTTOM: 
  1551.               if (run == 0) {
  1552.                   offset = (forward? 
  1553.                             maxTabHeight(pane) :
  1554.                             totalTabHeight(pane, tabPlacement, runCount)-maxTabHeight(pane));
  1555.               } else if (run == runCount - 1) {
  1556.                   offset = (forward?
  1557.                             -(totalTabHeight(pane, tabPlacement, runCount)-maxTabHeight(pane)) :
  1558.                             -(maxTabHeight(pane)));
  1559.               } else {
  1560.                   offset = (forward? maxTabHeight(pane) : -(maxTabHeight(pane)));
  1561.               } 
  1562.               break;
  1563.           case TOP:
  1564.           default:
  1565.               if (run == 0) {
  1566.                   offset = (forward? 
  1567.                             -(totalTabHeight(pane, tabPlacement, runCount)-maxTabHeight(pane)) :
  1568.                             -(maxTabHeight(pane)));
  1569.               } else if (run == runCount - 1) {
  1570.                   offset = (forward?
  1571.                             maxTabHeight(pane) :
  1572.                             totalTabHeight(pane, tabPlacement, runCount)-maxTabHeight(pane));
  1573.               } else {
  1574.                   offset = (forward? maxTabHeight(pane) : -(maxTabHeight(pane)));
  1575.               }
  1576.         }
  1577.         return offset;
  1578.     }
  1579.     
  1580.     public int tabForCoordinate(JTabbedPane pane, int x, int y) {
  1581.         int tabCount = pane.getTabCount();
  1582.         if (tabCount != rects.length) { 
  1583.               calculateLayoutInfo(pane); 
  1584.         }
  1585.  
  1586.         for (int i = 0; i < tabCount; i++) {
  1587.             if (rects[i].contains(x, y)) {
  1588.                 return i;
  1589.             }
  1590.         }
  1591.  
  1592.         return -1;
  1593.     }
  1594.         
  1595.  
  1596. // Controller: event listeners
  1597.  
  1598.     class MouseGetter extends MouseAdapter implements Serializable {
  1599.         public void mousePressed(MouseEvent e) {
  1600.             JTabbedPane pane = (JTabbedPane)e.getSource();
  1601.             int tabIndex = tabForCoordinate(pane, e.getX(), e.getY());
  1602.  
  1603.             if (tabIndex >= 0 && pane.isEnabledAt(tabIndex)) {
  1604.                 if (tabIndex == pane.getSelectedIndex()) {
  1605.                     pane.requestFocus();
  1606.                     pane.repaint(rects[tabIndex]);
  1607.                 } else {
  1608.                     pane.setSelectedIndex(tabIndex);
  1609.                 }
  1610.             }
  1611.         }
  1612.     }
  1613.  
  1614. // Focus control
  1615.     class FocusGetter extends FocusAdapter implements Serializable {
  1616.        public void focusGained(FocusEvent e) { 
  1617.            ((JTabbedPane)e.getSource()).repaint(getFocusedTabBounds((JTabbedPane)e.getSource()));
  1618.         }
  1619.             
  1620.         public void focusLost(FocusEvent e) {
  1621.             ((JTabbedPane)e.getSource()).repaint(getFocusedTabBounds((JTabbedPane)e.getSource()));
  1622.         }
  1623.     }
  1624. }
  1625.