< prev index next >

src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTabbedPaneUI.java

Print this page




  39 import java.util.Vector;
  40 import java.util.Hashtable;
  41 
  42 import sun.swing.DefaultLookup;
  43 import sun.swing.UIAction;
  44 
  45 /**
  46  * A Basic L&amp;F implementation of TabbedPaneUI.
  47  *
  48  * @author Amy Fowler
  49  * @author Philip Milne
  50  * @author Steve Wilson
  51  * @author Tom Santos
  52  * @author Dave Moore
  53  */
  54 public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants {
  55 
  56 
  57 // Instance variables initialized at installation
  58 

  59     protected JTabbedPane tabPane;
  60 

  61     protected Color highlight;

  62     protected Color lightHighlight;

  63     protected Color shadow;

  64     protected Color darkShadow;

  65     protected Color focus;
  66     private   Color selectedColor;
  67 

  68     protected int textIconGap;
  69 
  70     protected int tabRunOverlay;
  71 

  72     protected Insets tabInsets;

  73     protected Insets selectedTabPadInsets;

  74     protected Insets tabAreaInsets;

  75     protected Insets contentBorderInsets;
  76     private boolean tabsOverlapBorder;
  77     private boolean tabsOpaque = true;
  78     private boolean contentOpaque = true;
  79 
  80     /**
  81      * As of Java 2 platform v1.3 this previously undocumented field is no
  82      * longer used.
  83      * Key bindings are now defined by the LookAndFeel, please refer to
  84      * the key bindings specification for further details.
  85      *
  86      * @deprecated As of Java 2 platform v1.3.
  87      */
  88     @Deprecated
  89     protected KeyStroke upKey;
  90     /**
  91      * As of Java 2 platform v1.3 this previously undocumented field is no
  92      * longer used.
  93      * Key bindings are now defined by the LookAndFeel, please refer to
  94      * the key bindings specification for further details.


 103      * Key bindings are now defined by the LookAndFeel, please refer to
 104      * the key bindings specification for further details.
 105      *
 106      * @deprecated As of Java 2 platform v1.3.
 107      */
 108     @Deprecated
 109     protected KeyStroke leftKey;
 110     /**
 111      * As of Java 2 platform v1.3 this previously undocumented field is no
 112      * longer used.
 113      * Key bindings are now defined by the LookAndFeel, please refer to
 114      * the key bindings specification for further details.
 115      *
 116      * @deprecated As of Java 2 platform v1.3.
 117      */
 118     @Deprecated
 119     protected KeyStroke rightKey;
 120 
 121 
 122 // Transient variables (recalculated each time TabbedPane is layed out)
 123 
 124     protected int tabRuns[] = new int[10];

 125     protected int runCount = 0;

 126     protected int selectedRun = -1;

 127     protected Rectangle rects[] = new Rectangle[0];

 128     protected int maxTabHeight;

 129     protected int maxTabWidth;
 130 
 131 // Listeners
 132 

 133     protected ChangeListener tabChangeListener;

 134     protected PropertyChangeListener propertyChangeListener;

 135     protected MouseListener mouseListener;

 136     protected FocusListener focusListener;
 137 
 138 // Private instance data
 139 
 140     private Insets currentPadInsets = new Insets(0,0,0,0);
 141     private Insets currentTabAreaInsets = new Insets(0,0,0,0);
 142 
 143     private Component visibleComponent;
 144     // PENDING(api): See comment for ContainerHandler
 145     private Vector<View> htmlViews;
 146 
 147     private Hashtable<Integer, Integer> mnemonicToIndexMap;
 148 
 149     /**
 150      * InputMap used for mnemonics. Only non-null if the JTabbedPane has
 151      * mnemonics associated with it. Lazily created in initMnemonics.
 152      */
 153     private InputMap mnemonicInputMap;
 154 
 155     // For use when tabLayoutPolicy = SCROLL_TAB_LAYOUT


 173      */
 174     private Handler handler;
 175 
 176     /**
 177      * Index of the tab the mouse is over.
 178      */
 179     private int rolloverTabIndex;
 180 
 181     /**
 182      * This is set to true when a component is added/removed from the tab
 183      * pane and set to false when layout happens.  If true it indicates that
 184      * tabRuns is not valid and shouldn't be used.
 185      */
 186     private boolean isRunsDirty;
 187 
 188     private boolean calculatedBaseline;
 189     private int baseline;
 190 
 191 // UI creation
 192 





 193     public static ComponentUI createUI(JComponent c) {
 194         return new BasicTabbedPaneUI();
 195     }
 196 
 197     static void loadActionMap(LazyActionMap map) {
 198         map.put(new Actions(Actions.NEXT));
 199         map.put(new Actions(Actions.PREVIOUS));
 200         map.put(new Actions(Actions.RIGHT));
 201         map.put(new Actions(Actions.LEFT));
 202         map.put(new Actions(Actions.UP));
 203         map.put(new Actions(Actions.DOWN));
 204         map.put(new Actions(Actions.PAGE_UP));
 205         map.put(new Actions(Actions.PAGE_DOWN));
 206         map.put(new Actions(Actions.REQUEST_FOCUS));
 207         map.put(new Actions(Actions.REQUEST_FOCUS_FOR_VISIBLE));
 208         map.put(new Actions(Actions.SET_SELECTED));
 209         map.put(new Actions(Actions.SELECT_FOCUSED));
 210         map.put(new Actions(Actions.SCROLL_FORWARD));
 211         map.put(new Actions(Actions.SCROLL_BACKWARD));
 212     }


 338         }
 339     }
 340 
 341     private void uninstallTabContainer() {
 342          if(tabContainer == null) {
 343              return;
 344          }
 345          // Remove all the tabComponents, making sure not to notify
 346          // the tabbedpane.
 347          tabContainer.notifyTabbedPane = false;
 348          tabContainer.removeAll();
 349          if(scrollableTabLayoutEnabled()) {
 350              tabContainer.remove(tabScroller.croppedEdge);
 351              tabScroller.tabPanel.remove(tabContainer);
 352          } else {
 353            tabPane.remove(tabContainer);
 354          }
 355          tabContainer = null;
 356     }
 357 



 358     protected void installDefaults() {
 359         LookAndFeel.installColorsAndFont(tabPane, "TabbedPane.background",
 360                                     "TabbedPane.foreground", "TabbedPane.font");
 361         highlight = UIManager.getColor("TabbedPane.light");
 362         lightHighlight = UIManager.getColor("TabbedPane.highlight");
 363         shadow = UIManager.getColor("TabbedPane.shadow");
 364         darkShadow = UIManager.getColor("TabbedPane.darkShadow");
 365         focus = UIManager.getColor("TabbedPane.focus");
 366         selectedColor = UIManager.getColor("TabbedPane.selected");
 367 
 368         textIconGap = UIManager.getInt("TabbedPane.textIconGap");
 369         tabInsets = UIManager.getInsets("TabbedPane.tabInsets");
 370         selectedTabPadInsets = UIManager.getInsets("TabbedPane.selectedTabPadInsets");
 371         tabAreaInsets = UIManager.getInsets("TabbedPane.tabAreaInsets");
 372         tabsOverlapBorder = UIManager.getBoolean("TabbedPane.tabsOverlapBorder");
 373         contentBorderInsets = UIManager.getInsets("TabbedPane.contentBorderInsets");
 374         tabRunOverlay = UIManager.getInt("TabbedPane.tabRunOverlay");
 375         tabsOpaque = UIManager.getBoolean("TabbedPane.tabsOpaque");
 376         contentOpaque = UIManager.getBoolean("TabbedPane.contentOpaque");
 377         Object opaque = UIManager.get("TabbedPane.opaque");
 378         if (opaque == null) {
 379             opaque = Boolean.FALSE;
 380         }
 381         LookAndFeel.installProperty(tabPane, "opaque", opaque);
 382 
 383         // Fix for 6711145 BasicTabbedPanuUI should not throw a NPE if these
 384         // keys are missing. So we are setting them to there default values here
 385         // if the keys are missing.
 386         if (tabInsets == null) tabInsets = new Insets(0,4,1,4);
 387         if (selectedTabPadInsets == null) selectedTabPadInsets = new Insets(2,2,2,1);
 388         if (tabAreaInsets == null) tabAreaInsets = new Insets(3,2,0,2);
 389         if (contentBorderInsets == null) contentBorderInsets = new Insets(2,2,3,3);
 390     }
 391 



 392     protected void uninstallDefaults() {
 393         highlight = null;
 394         lightHighlight = null;
 395         shadow = null;
 396         darkShadow = null;
 397         focus = null;
 398         tabInsets = null;
 399         selectedTabPadInsets = null;
 400         tabAreaInsets = null;
 401         contentBorderInsets = null;
 402     }
 403 



 404     protected void installListeners() {
 405         if ((propertyChangeListener = createPropertyChangeListener()) != null) {
 406             tabPane.addPropertyChangeListener(propertyChangeListener);
 407         }
 408         if ((tabChangeListener = createChangeListener()) != null) {
 409             tabPane.addChangeListener(tabChangeListener);
 410         }
 411         if ((mouseListener = createMouseListener()) != null) {
 412             tabPane.addMouseListener(mouseListener);
 413         }
 414         tabPane.addMouseMotionListener(getHandler());
 415         if ((focusListener = createFocusListener()) != null) {
 416             tabPane.addFocusListener(focusListener);
 417         }
 418         tabPane.addContainerListener(getHandler());
 419         if (tabPane.getTabCount()>0) {
 420             htmlViews = createHTMLVector();
 421         }
 422     }
 423 



 424     protected void uninstallListeners() {
 425         if (mouseListener != null) {
 426             tabPane.removeMouseListener(mouseListener);
 427             mouseListener = null;
 428         }
 429         tabPane.removeMouseMotionListener(getHandler());
 430         if (focusListener != null) {
 431             tabPane.removeFocusListener(focusListener);
 432             focusListener = null;
 433         }
 434 
 435         tabPane.removeContainerListener(getHandler());
 436         if (htmlViews!=null) {
 437             htmlViews.removeAllElements();
 438             htmlViews = null;
 439         }
 440         if (tabChangeListener != null) {
 441             tabPane.removeChangeListener(tabChangeListener);
 442             tabChangeListener = null;
 443         }
 444         if (propertyChangeListener != null) {
 445             tabPane.removePropertyChangeListener(propertyChangeListener);
 446             propertyChangeListener = null;
 447         }
 448         handler = null;
 449     }
 450 




 451     protected MouseListener createMouseListener() {
 452         return getHandler();
 453     }
 454 




 455     protected FocusListener createFocusListener() {
 456         return getHandler();
 457     }
 458 




 459     protected ChangeListener createChangeListener() {
 460         return getHandler();
 461     }
 462 




 463     protected PropertyChangeListener createPropertyChangeListener() {
 464         return getHandler();
 465     }
 466 
 467     private Handler getHandler() {
 468         if (handler == null) {
 469             handler = new Handler();
 470         }
 471         return handler;
 472     }
 473 



 474     protected void installKeyboardActions() {
 475         InputMap km = getInputMap(JComponent.
 476                                   WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
 477 
 478         SwingUtilities.replaceUIInputMap(tabPane, JComponent.
 479                                          WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
 480                                          km);
 481         km = getInputMap(JComponent.WHEN_FOCUSED);
 482         SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, km);
 483 
 484         LazyActionMap.installLazyActionMap(tabPane, BasicTabbedPaneUI.class,
 485                                            "TabbedPane.actionMap");
 486         updateMnemonics();
 487     }
 488 
 489     InputMap getInputMap(int condition) {
 490         if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
 491             return (InputMap)DefaultLookup.get(tabPane, this,
 492                                                "TabbedPane.ancestorInputMap");
 493         }
 494         else if (condition == JComponent.WHEN_FOCUSED) {
 495             return (InputMap)DefaultLookup.get(tabPane, this,
 496                                                "TabbedPane.focusInputMap");
 497         }
 498         return null;
 499     }
 500 



 501     protected void uninstallKeyboardActions() {
 502         SwingUtilities.replaceUIActionMap(tabPane, null);
 503         SwingUtilities.replaceUIInputMap(tabPane, JComponent.
 504                                          WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
 505                                          null);
 506         SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED,
 507                                          null);
 508         SwingUtilities.replaceUIInputMap(tabPane,
 509                                          JComponent.WHEN_IN_FOCUSED_WINDOW,
 510                                          null);
 511         mnemonicToIndexMap = null;
 512         mnemonicInputMap = null;
 513     }
 514 
 515     /**
 516      * Reloads the mnemonics. This should be invoked when a memonic changes,
 517      * when the title of a mnemonic changes, or when tabs are added/removed.
 518      */
 519     private void updateMnemonics() {
 520         resetMnemonics();


 827 
 828         // Paint tabRuns of tabs from back to front
 829         for (int i = runCount - 1; i >= 0; i--) {
 830             int start = tabRuns[i];
 831             int next = tabRuns[(i == runCount - 1)? 0 : i + 1];
 832             int end = (next != 0? next - 1: tabCount - 1);
 833             for (int j = start; j <= end; j++) {
 834                 if (j != selectedIndex && rects[j].intersects(clipRect)) {
 835                     paintTab(g, tabPlacement, rects, j, iconRect, textRect);
 836                 }
 837             }
 838         }
 839 
 840         // Paint selected tab if its in the front run
 841         // since it may overlap other tabs
 842         if (selectedIndex >= 0 && rects[selectedIndex].intersects(clipRect)) {
 843             paintTab(g, tabPlacement, rects, selectedIndex, iconRect, textRect);
 844         }
 845     }
 846 









 847     protected void paintTab(Graphics g, int tabPlacement,
 848                             Rectangle[] rects, int tabIndex,
 849                             Rectangle iconRect, Rectangle textRect) {
 850         Rectangle tabRect = rects[tabIndex];
 851         int selectedIndex = tabPane.getSelectedIndex();
 852         boolean isSelected = selectedIndex == tabIndex;
 853 
 854         if (tabsOpaque || tabPane.isOpaque()) {
 855             paintTabBackground(g, tabPlacement, tabIndex, tabRect.x, tabRect.y,
 856                     tabRect.width, tabRect.height, isSelected);
 857         }
 858 
 859         paintTabBorder(g, tabPlacement, tabIndex, tabRect.x, tabRect.y,
 860                        tabRect.width, tabRect.height, isSelected);
 861 
 862         String title = tabPane.getTitleAt(tabIndex);
 863         Font font = tabPane.getFont();
 864         FontMetrics metrics = SwingUtilities2.getFontMetrics(tabPane, g, font);
 865         Icon icon = getIconForTab(tabIndex);
 866 


 995                 xx+=CROP_SEGMENT;
 996             }
 997             break;
 998           case TOP:
 999           case BOTTOM:
1000           default:
1001             x = cropline;
1002             y = rects[tabIndex].y;
1003             int yy = y;
1004             g.setColor(shadow);
1005             while(yy <= y+rects[tabIndex].height) {
1006                 for (int i=0; i < xCropLen.length; i+=2) {
1007                     g.drawLine(x-xCropLen[i],yy+yCropLen[i],
1008                                x-xCropLen[i+1],yy+yCropLen[i+1]-1);
1009                 }
1010                 yy+=CROP_SEGMENT;
1011             }
1012         }
1013     }
1014 












1015     protected void layoutLabel(int tabPlacement,
1016                                FontMetrics metrics, int tabIndex,
1017                                String title, Icon icon,
1018                                Rectangle tabRect, Rectangle iconRect,
1019                                Rectangle textRect, boolean isSelected ) {
1020         textRect.x = textRect.y = iconRect.x = iconRect.y = 0;
1021 
1022         View v = getTextViewForTab(tabIndex);
1023         if (v != null) {
1024             tabPane.putClientProperty("html", v);
1025         }
1026 
1027         SwingUtilities.layoutCompoundLabel(tabPane,
1028                                            metrics, title, icon,
1029                                            SwingUtilities.CENTER,
1030                                            SwingUtilities.CENTER,
1031                                            SwingUtilities.CENTER,
1032                                            SwingUtilities.TRAILING,
1033                                            tabRect,
1034                                            iconRect,
1035                                            textRect,
1036                                            textIconGap);
1037 
1038         tabPane.putClientProperty("html", null);
1039 
1040         int xNudge = getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
1041         int yNudge = getTabLabelShiftY(tabPlacement, tabIndex, isSelected);
1042         iconRect.x += xNudge;
1043         iconRect.y += yNudge;
1044         textRect.x += xNudge;
1045         textRect.y += yNudge;
1046     }
1047 









1048     protected void paintIcon(Graphics g, int tabPlacement,
1049                              int tabIndex, Icon icon, Rectangle iconRect,
1050                              boolean isSelected ) {
1051         if (icon != null) {
1052             icon.paintIcon(tabPane, g, iconRect.x, iconRect.y);
1053         }
1054     }
1055 











1056     protected void paintText(Graphics g, int tabPlacement,
1057                              Font font, FontMetrics metrics, int tabIndex,
1058                              String title, Rectangle textRect,
1059                              boolean isSelected) {
1060 
1061         g.setFont(font);
1062 
1063         View v = getTextViewForTab(tabIndex);
1064         if (v != null) {
1065             // html
1066             v.paint(g, textRect);
1067         } else {
1068             // plain text
1069             int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
1070 
1071             if (tabPane.isEnabled() && tabPane.isEnabledAt(tabIndex)) {
1072                 Color fg = tabPane.getForegroundAt(tabIndex);
1073                 if (isSelected && (fg instanceof UIResource)) {
1074                     Color selectedFG = UIManager.getColor(
1075                                   "TabbedPane.selectedForeground");


1079                 }
1080                 g.setColor(fg);
1081                 SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
1082                              title, mnemIndex,
1083                              textRect.x, textRect.y + metrics.getAscent());
1084 
1085             } else { // tab disabled
1086                 g.setColor(tabPane.getBackgroundAt(tabIndex).brighter());
1087                 SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
1088                              title, mnemIndex,
1089                              textRect.x, textRect.y + metrics.getAscent());
1090                 g.setColor(tabPane.getBackgroundAt(tabIndex).darker());
1091                 SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
1092                              title, mnemIndex,
1093                              textRect.x - 1, textRect.y + metrics.getAscent() - 1);
1094 
1095             }
1096         }
1097     }
1098 
1099 






1100     protected int getTabLabelShiftX(int tabPlacement, int tabIndex, boolean isSelected) {
1101         Rectangle tabRect = rects[tabIndex];
1102         String propKey = (isSelected ? "selectedLabelShift" : "labelShift");
1103         int nudge = DefaultLookup.getInt(
1104                 tabPane, this, "TabbedPane." + propKey, 1);
1105 
1106         switch (tabPlacement) {
1107             case LEFT:
1108                 return nudge;
1109             case RIGHT:
1110                 return -nudge;
1111             case BOTTOM:
1112             case TOP:
1113             default:
1114                 return tabRect.width % 2;
1115         }
1116     }
1117 







1118     protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) {
1119         Rectangle tabRect = rects[tabIndex];
1120         int nudge = (isSelected ? DefaultLookup.getInt(tabPane, this, "TabbedPane.selectedLabelShift", -1) :
1121                 DefaultLookup.getInt(tabPane, this, "TabbedPane.labelShift", 1));
1122 
1123         switch (tabPlacement) {
1124             case BOTTOM:
1125                 return -nudge;
1126             case LEFT:
1127             case RIGHT:
1128                 return tabRect.height % 2;
1129             case TOP:
1130             default:
1131                 return nudge;
1132         }
1133     }
1134 










1135     protected void paintFocusIndicator(Graphics g, int tabPlacement,
1136                                        Rectangle[] rects, int tabIndex,
1137                                        Rectangle iconRect, Rectangle textRect,
1138                                        boolean isSelected) {
1139         Rectangle tabRect = rects[tabIndex];
1140         if (tabPane.hasFocus() && isSelected) {
1141             int x, y, w, h;
1142             g.setColor(focus);
1143             switch(tabPlacement) {
1144               case LEFT:
1145                   x = tabRect.x + 3;
1146                   y = tabRect.y + 3;
1147                   w = tabRect.width - 5;
1148                   h = tabRect.height - 6;
1149                   break;
1150               case RIGHT:
1151                   x = tabRect.x + 2;
1152                   y = tabRect.y + 3;
1153                   w = tabRect.width - 5;
1154                   h = tabRect.height - 6;


1228               g.setColor(darkShadow);
1229               g.drawLine(x+2, y+h-1, x+w-3, y+h-1); // bottom dark shadow
1230               g.drawLine(x+w-2, y+h-2, x+w-2, y+h-2); // bottom-right dark shadow
1231               g.drawLine(x+w-1, y, x+w-1, y+h-3); // right dark shadow
1232               break;
1233           case TOP:
1234           default:
1235               g.drawLine(x, y+2, x, y+h-1); // left highlight
1236               g.drawLine(x+1, y+1, x+1, y+1); // top-left highlight
1237               g.drawLine(x+2, y, x+w-3, y); // top highlight
1238 
1239               g.setColor(shadow);
1240               g.drawLine(x+w-2, y+2, x+w-2, y+h-1); // right shadow
1241 
1242               g.setColor(darkShadow);
1243               g.drawLine(x+w-1, y+2, x+w-1, y+h-1); // right dark-shadow
1244               g.drawLine(x+w-2, y+1, x+w-2, y+1); // top-right shadow
1245         }
1246     }
1247 












1248     protected void paintTabBackground(Graphics g, int tabPlacement,
1249                                       int tabIndex,
1250                                       int x, int y, int w, int h,
1251                                       boolean isSelected ) {
1252         g.setColor(!isSelected || selectedColor == null?
1253                    tabPane.getBackgroundAt(tabIndex) : selectedColor);
1254         switch(tabPlacement) {
1255           case LEFT:
1256               g.fillRect(x+1, y+1, w-1, h-3);
1257               break;
1258           case RIGHT:
1259               g.fillRect(x, y+1, w-2, h-3);
1260               break;
1261           case BOTTOM:
1262               g.fillRect(x+1, y, w-3, h-1);
1263               break;
1264           case TOP:
1265           default:
1266               g.fillRect(x+1, y+1, w-3, h-1);
1267         }
1268     }
1269 






1270     protected void paintContentBorder(Graphics g, int tabPlacement, int selectedIndex) {
1271         int width = tabPane.getWidth();
1272         int height = tabPane.getHeight();
1273         Insets insets = tabPane.getInsets();
1274         Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
1275 
1276         int x = insets.left;
1277         int y = insets.top;
1278         int w = width - insets.right - insets.left;
1279         int h = height - insets.top - insets.bottom;
1280 
1281         switch(tabPlacement) {
1282           case LEFT:
1283               x += calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
1284               if (tabsOverlapBorder) {
1285                   x -= tabAreaInsets.right;
1286               }
1287               w -= (x - insets.left);
1288               break;
1289           case RIGHT:


1312             Color color = UIManager.getColor("TabbedPane.contentAreaColor");
1313             if (color != null) {
1314                 g.setColor(color);
1315             }
1316             else if ( selectedColor == null || selectedIndex == -1 ) {
1317                 g.setColor(tabPane.getBackground());
1318             }
1319             else {
1320                 g.setColor(selectedColor);
1321             }
1322             g.fillRect(x,y,w,h);
1323         }
1324 
1325         paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h);
1326         paintContentBorderLeftEdge(g, tabPlacement, selectedIndex, x, y, w, h);
1327         paintContentBorderBottomEdge(g, tabPlacement, selectedIndex, x, y, w, h);
1328         paintContentBorderRightEdge(g, tabPlacement, selectedIndex, x, y, w, h);
1329 
1330     }
1331 










1332     protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
1333                                          int selectedIndex,
1334                                          int x, int y, int w, int h) {
1335         Rectangle selRect = selectedIndex < 0? null :
1336                                getTabBounds(selectedIndex, calcRect);
1337 
1338         g.setColor(lightHighlight);
1339 
1340         // Draw unbroken line if tabs are not on TOP, OR
1341         // selected tab is not in run adjacent to content, OR
1342         // selected tab is not visible (SCROLL_TAB_LAYOUT)
1343         //
1344         if (tabPlacement != TOP || selectedIndex < 0 ||
1345             (selRect.y + selRect.height + 1 < y) ||
1346             (selRect.x < x || selRect.x > x + w)) {
1347             g.drawLine(x, y, x+w-2, y);
1348         } else {
1349             // Break line to show visual connection to selected tab
1350             g.drawLine(x, y, selRect.x - 1, y);
1351             if (selRect.x + selRect.width < x + w - 2) {
1352                 g.drawLine(selRect.x + selRect.width, y,
1353                            x+w-2, y);
1354             } else {
1355                 g.setColor(shadow);
1356                 g.drawLine(x+w-2, y, x+w-2, y);
1357             }
1358         }
1359     }
1360 










1361     protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
1362                                                int selectedIndex,
1363                                                int x, int y, int w, int h) {
1364         Rectangle selRect = selectedIndex < 0? null :
1365                                getTabBounds(selectedIndex, calcRect);
1366 
1367         g.setColor(lightHighlight);
1368 
1369         // Draw unbroken line if tabs are not on LEFT, OR
1370         // selected tab is not in run adjacent to content, OR
1371         // selected tab is not visible (SCROLL_TAB_LAYOUT)
1372         //
1373         if (tabPlacement != LEFT || selectedIndex < 0 ||
1374             (selRect.x + selRect.width + 1 < x) ||
1375             (selRect.y < y || selRect.y > y + h)) {
1376             g.drawLine(x, y, x, y+h-2);
1377         } else {
1378             // Break line to show visual connection to selected tab
1379             g.drawLine(x, y, x, selRect.y - 1);
1380             if (selRect.y + selRect.height < y + h - 2) {
1381                 g.drawLine(x, selRect.y + selRect.height,
1382                            x, y+h-2);
1383             }
1384         }
1385     }
1386 










1387     protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
1388                                                int selectedIndex,
1389                                                int x, int y, int w, int h) {
1390         Rectangle selRect = selectedIndex < 0? null :
1391                                getTabBounds(selectedIndex, calcRect);
1392 
1393         g.setColor(shadow);
1394 
1395         // Draw unbroken line if tabs are not on BOTTOM, OR
1396         // selected tab is not in run adjacent to content, OR
1397         // selected tab is not visible (SCROLL_TAB_LAYOUT)
1398         //
1399         if (tabPlacement != BOTTOM || selectedIndex < 0 ||
1400              (selRect.y - 1 > h) ||
1401              (selRect.x < x || selRect.x > x + w)) {
1402             g.drawLine(x+1, y+h-2, x+w-2, y+h-2);
1403             g.setColor(darkShadow);
1404             g.drawLine(x, y+h-1, x+w-1, y+h-1);
1405         } else {
1406             // Break line to show visual connection to selected tab
1407             g.drawLine(x+1, y+h-2, selRect.x - 1, y+h-2);
1408             g.setColor(darkShadow);
1409             g.drawLine(x, y+h-1, selRect.x - 1, y+h-1);
1410             if (selRect.x + selRect.width < x + w - 2) {
1411                 g.setColor(shadow);
1412                 g.drawLine(selRect.x + selRect.width, y+h-2, x+w-2, y+h-2);
1413                 g.setColor(darkShadow);
1414                 g.drawLine(selRect.x + selRect.width, y+h-1, x+w-1, y+h-1);
1415             }
1416         }
1417 
1418     }
1419 










1420     protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
1421                                                int selectedIndex,
1422                                                int x, int y, int w, int h) {
1423         Rectangle selRect = selectedIndex < 0? null :
1424                                getTabBounds(selectedIndex, calcRect);
1425 
1426         g.setColor(shadow);
1427 
1428         // Draw unbroken line if tabs are not on RIGHT, OR
1429         // selected tab is not in run adjacent to content, OR
1430         // selected tab is not visible (SCROLL_TAB_LAYOUT)
1431         //
1432         if (tabPlacement != RIGHT || selectedIndex < 0 ||
1433              (selRect.x - 1 > w) ||
1434              (selRect.y < y || selRect.y > y + h)) {
1435             g.drawLine(x+w-2, y+1, x+w-2, y+h-3);
1436             g.setColor(darkShadow);
1437             g.drawLine(x+w-1, y, x+w-1, y+h-1);
1438         } else {
1439             // Break line to show visual connection to selected tab


1601             }
1602         }
1603         return min;
1604     }
1605 
1606     /**
1607      * Returns a point which is translated from the specified point in the
1608      * JTabbedPane's coordinate space to the coordinate space of the
1609      * ScrollableTabPanel.  This is used for SCROLL_TAB_LAYOUT ONLY.
1610      */
1611     private Point translatePointToTabPanel(int srcx, int srcy, Point dest) {
1612         Point vpp = tabScroller.viewport.getLocation();
1613         Point viewp = tabScroller.viewport.getViewPosition();
1614         dest.x = srcx - vpp.x + viewp.x;
1615         dest.y = srcy - vpp.y + viewp.y;
1616         return dest;
1617     }
1618 
1619 // BasicTabbedPaneUI methods
1620 




1621     protected Component getVisibleComponent() {
1622         return visibleComponent;
1623     }
1624 




1625     protected void setVisibleComponent(Component component) {
1626         if (visibleComponent != null
1627                 && visibleComponent != component
1628                 && visibleComponent.getParent() == tabPane
1629                 && visibleComponent.isVisible()) {
1630 
1631             visibleComponent.setVisible(false);
1632         }
1633         if (component != null && !component.isVisible()) {
1634             component.setVisible(true);
1635         }
1636         visibleComponent = component;
1637     }
1638 




1639     protected void assureRectsCreated(int tabCount) {
1640         int rectArrayLen = rects.length;
1641         if (tabCount != rectArrayLen ) {
1642             Rectangle[] tempRectArray = new Rectangle[tabCount];
1643             System.arraycopy(rects, 0, tempRectArray, 0,
1644                              Math.min(rectArrayLen, tabCount));
1645             rects = tempRectArray;
1646             for (int rectIndex = rectArrayLen; rectIndex < tabCount; rectIndex++) {
1647                 rects[rectIndex] = new Rectangle();
1648             }
1649         }
1650 
1651     }
1652 



1653     protected void expandTabRunsArray() {
1654         int rectLen = tabRuns.length;
1655         int[] newArray = new int[rectLen+10];
1656         System.arraycopy(tabRuns, 0, newArray, 0, runCount);
1657         tabRuns = newArray;
1658     }
1659 






1660     protected int getRunForTab(int tabCount, int tabIndex) {
1661         for (int i = 0; i < runCount; i++) {
1662             int first = tabRuns[i];
1663             int last = lastTabInRun(tabCount, i);
1664             if (tabIndex >= first && tabIndex <= last) {
1665                 return i;
1666             }
1667         }
1668         return 0;
1669     }
1670 






1671     protected int lastTabInRun(int tabCount, int run) {
1672         if (runCount == 1) {
1673             return tabCount - 1;
1674         }
1675         int nextRun = (run == runCount - 1? 0 : run + 1);
1676         if (tabRuns[nextRun] == 0) {
1677             return tabCount - 1;
1678         }
1679         return tabRuns[nextRun]-1;
1680     }
1681 





1682     protected int getTabRunOverlay(int tabPlacement) {
1683         return tabRunOverlay;
1684     }
1685 






1686     protected int getTabRunIndent(int tabPlacement, int run) {
1687         return 0;
1688     }
1689 






1690     protected boolean shouldPadTabRun(int tabPlacement, int run) {
1691         return runCount > 1;
1692     }
1693 





1694     protected boolean shouldRotateTabRuns(int tabPlacement) {
1695         return true;
1696     }
1697 





1698     protected Icon getIconForTab(int tabIndex) {
1699         return (!tabPane.isEnabled() || !tabPane.isEnabledAt(tabIndex))?
1700                           tabPane.getDisabledIconAt(tabIndex) : tabPane.getIconAt(tabIndex);
1701     }
1702 
1703     /**
1704      * Returns the text View object required to render stylized text (HTML) for
1705      * the specified tab or null if no specialized text rendering is needed
1706      * for this tab. This is provided to support html rendering inside tabs.
1707      *
1708      * @param tabIndex the index of the tab
1709      * @return the text view to render the tab's text or null if no
1710      *         specialized rendering is required
1711      *
1712      * @since 1.4
1713      */
1714     protected View getTextViewForTab(int tabIndex) {
1715         if (htmlViews != null) {
1716             return htmlViews.elementAt(tabIndex);
1717         }
1718         return null;
1719     }
1720 







1721     protected int calculateTabHeight(int tabPlacement, int tabIndex, int fontHeight) {
1722         int height = 0;
1723         Component c = tabPane.getTabComponentAt(tabIndex);
1724         if (c != null) {
1725             height = c.getPreferredSize().height;
1726         } else {
1727             View v = getTextViewForTab(tabIndex);
1728             if (v != null) {
1729                 // html
1730                 height += (int) v.getPreferredSpan(View.Y_AXIS);
1731             } else {
1732                 // plain text
1733                 height += fontHeight;
1734             }
1735             Icon icon = getIconForTab(tabIndex);
1736 
1737             if (icon != null) {
1738                 height = Math.max(height, icon.getIconHeight());
1739             }
1740         }
1741         Insets tabInsets = getTabInsets(tabPlacement, tabIndex);
1742         height += tabInsets.top + tabInsets.bottom + 2;
1743         return height;
1744     }
1745 





1746     protected int calculateMaxTabHeight(int tabPlacement) {
1747         FontMetrics metrics = getFontMetrics();
1748         int tabCount = tabPane.getTabCount();
1749         int result = 0;
1750         int fontHeight = metrics.getHeight();
1751         for(int i = 0; i < tabCount; i++) {
1752             result = Math.max(calculateTabHeight(tabPlacement, i, fontHeight), result);
1753         }
1754         return result;
1755     }
1756 







1757     protected int calculateTabWidth(int tabPlacement, int tabIndex, FontMetrics metrics) {
1758         Insets tabInsets = getTabInsets(tabPlacement, tabIndex);
1759         int width = tabInsets.left + tabInsets.right + 3;
1760         Component tabComponent = tabPane.getTabComponentAt(tabIndex);
1761         if (tabComponent != null) {
1762             width += tabComponent.getPreferredSize().width;
1763         } else {
1764             Icon icon = getIconForTab(tabIndex);
1765             if (icon != null) {
1766                 width += icon.getIconWidth() + textIconGap;
1767             }
1768             View v = getTextViewForTab(tabIndex);
1769             if (v != null) {
1770                 // html
1771                 width += (int) v.getPreferredSpan(View.X_AXIS);
1772             } else {
1773                 // plain text
1774                 String title = tabPane.getTitleAt(tabIndex);
1775                 width += SwingUtilities2.stringWidth(tabPane, metrics, title);
1776             }
1777         }
1778         return width;
1779     }
1780 





1781     protected int calculateMaxTabWidth(int tabPlacement) {
1782         FontMetrics metrics = getFontMetrics();
1783         int tabCount = tabPane.getTabCount();
1784         int result = 0;
1785         for(int i = 0; i < tabCount; i++) {
1786             result = Math.max(calculateTabWidth(tabPlacement, i, metrics), result);
1787         }
1788         return result;
1789     }
1790 







1791     protected int calculateTabAreaHeight(int tabPlacement, int horizRunCount, int maxTabHeight) {
1792         Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
1793         int tabRunOverlay = getTabRunOverlay(tabPlacement);
1794         return (horizRunCount > 0?
1795                 horizRunCount * (maxTabHeight-tabRunOverlay) + tabRunOverlay +
1796                 tabAreaInsets.top + tabAreaInsets.bottom :
1797                 0);
1798     }
1799 







1800     protected int calculateTabAreaWidth(int tabPlacement, int vertRunCount, int maxTabWidth) {
1801         Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
1802         int tabRunOverlay = getTabRunOverlay(tabPlacement);
1803         return (vertRunCount > 0?
1804                 vertRunCount * (maxTabWidth-tabRunOverlay) + tabRunOverlay +
1805                 tabAreaInsets.left + tabAreaInsets.right :
1806                 0);
1807     }
1808 






1809     protected Insets getTabInsets(int tabPlacement, int tabIndex) {
1810         return tabInsets;
1811     }
1812 





1813     protected Insets getSelectedTabPadInsets(int tabPlacement) {
1814         rotateInsets(selectedTabPadInsets, currentPadInsets, tabPlacement);
1815         return currentPadInsets;
1816     }
1817 





1818     protected Insets getTabAreaInsets(int tabPlacement) {
1819         rotateInsets(tabAreaInsets, currentTabAreaInsets, tabPlacement);
1820         return currentTabAreaInsets;
1821     }
1822 





1823     protected Insets getContentBorderInsets(int tabPlacement) {
1824         return contentBorderInsets;
1825     }
1826 




1827     protected FontMetrics getFontMetrics() {
1828         Font font = tabPane.getFont();
1829         return tabPane.getFontMetrics(font);
1830     }
1831 
1832 
1833 // Tab Navigation methods
1834 




1835     protected void navigateSelectedTab(int direction) {
1836         int tabPlacement = tabPane.getTabPlacement();
1837         int current = DefaultLookup.getBoolean(tabPane, this,
1838                              "TabbedPane.selectionFollowsFocus", true) ?
1839                              tabPane.getSelectedIndex() : getFocusIndex();
1840         int tabCount = tabPane.getTabCount();
1841         boolean leftToRight = BasicGraphicsUtils.isLeftToRight(tabPane);
1842 
1843         // If we have no tabs then don't navigate.
1844         if (tabCount <= 0) {
1845             return;
1846         }
1847 
1848         int offset;
1849         switch(tabPlacement) {
1850           case LEFT:
1851           case RIGHT:
1852               switch(direction) {
1853                  case NEXT:
1854                      selectNextTab(current);


1893                     break;
1894                 case EAST:
1895                     if (leftToRight) {
1896                         selectNextTabInRun(current);
1897                     } else {
1898                         selectPreviousTabInRun(current);
1899                     }
1900                     break;
1901                 case WEST:
1902                     if (leftToRight) {
1903                         selectPreviousTabInRun(current);
1904                     } else {
1905                         selectNextTabInRun(current);
1906                     }
1907                     break;
1908                 default:
1909               }
1910         }
1911     }
1912 




1913     protected void selectNextTabInRun(int current) {
1914         int tabCount = tabPane.getTabCount();
1915         int tabIndex = getNextTabIndexInRun(tabCount, current);
1916 
1917         while(tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
1918             tabIndex = getNextTabIndexInRun(tabCount, tabIndex);
1919         }
1920         navigateTo(tabIndex);
1921     }
1922 




1923     protected void selectPreviousTabInRun(int current) {
1924         int tabCount = tabPane.getTabCount();
1925         int tabIndex = getPreviousTabIndexInRun(tabCount, current);
1926 
1927         while(tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
1928             tabIndex = getPreviousTabIndexInRun(tabCount, tabIndex);
1929         }
1930         navigateTo(tabIndex);
1931     }
1932 




1933     protected void selectNextTab(int current) {
1934         int tabIndex = getNextTabIndex(current);
1935 
1936         while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
1937             tabIndex = getNextTabIndex(tabIndex);
1938         }
1939         navigateTo(tabIndex);
1940     }
1941 




1942     protected void selectPreviousTab(int current) {
1943         int tabIndex = getPreviousTabIndex(current);
1944 
1945         while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
1946             tabIndex = getPreviousTabIndex(tabIndex);
1947         }
1948         navigateTo(tabIndex);
1949     }
1950 






1951     protected void selectAdjacentRunTab(int tabPlacement,
1952                                         int tabIndex, int offset) {
1953         if ( runCount < 2 ) {
1954             return;
1955         }
1956         int newIndex;
1957         Rectangle r = rects[tabIndex];
1958         switch(tabPlacement) {
1959           case LEFT:
1960           case RIGHT:
1961               newIndex = tabForCoordinate(tabPane, r.x + r.width/2 + offset,
1962                                        r.y + r.height/2);
1963               break;
1964           case BOTTOM:
1965           case TOP:
1966           default:
1967               newIndex = tabForCoordinate(tabPane, r.x + r.width/2,
1968                                        r.y + r.height/2 + offset);
1969         }
1970         if (newIndex != -1) {


2009 
2010     /**
2011      * Makes sure the focusIndex is valid.
2012      */
2013     private void validateFocusIndex() {
2014         if (focusIndex >= tabPane.getTabCount()) {
2015             setFocusIndex(tabPane.getSelectedIndex(), false);
2016         }
2017     }
2018 
2019     /**
2020      * Returns the index of the tab that has focus.
2021      *
2022      * @return index of tab that has focus
2023      * @since 1.5
2024      */
2025     protected int getFocusIndex() {
2026         return focusIndex;
2027     }
2028 








2029     protected int getTabRunOffset(int tabPlacement, int tabCount,
2030                                   int tabIndex, boolean forward) {
2031         int run = getRunForTab(tabCount, tabIndex);
2032         int offset;
2033         switch(tabPlacement) {
2034           case LEFT: {
2035               if (run == 0) {
2036                   offset = (forward?
2037                             -(calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth) :
2038                             -maxTabWidth);
2039 
2040               } else if (run == runCount - 1) {
2041                   offset = (forward?
2042                             maxTabWidth :
2043                             calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth);
2044               } else {
2045                   offset = (forward? maxTabWidth : -maxTabWidth);
2046               }
2047               break;
2048           }


2075               break;
2076           }
2077           case TOP:
2078           default: {
2079               if (run == 0) {
2080                   offset = (forward?
2081                             -(calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight) :
2082                             -maxTabHeight);
2083               } else if (run == runCount - 1) {
2084                   offset = (forward?
2085                             maxTabHeight :
2086                             calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight);
2087               } else {
2088                   offset = (forward? maxTabHeight : -maxTabHeight);
2089               }
2090           }
2091         }
2092         return offset;
2093     }
2094 





2095     protected int getPreviousTabIndex(int base) {
2096         int tabIndex = (base - 1 >= 0? base - 1 : tabPane.getTabCount() - 1);
2097         return (tabIndex >= 0? tabIndex : 0);
2098     }
2099 





2100     protected int getNextTabIndex(int base) {
2101         return (base+1)%tabPane.getTabCount();
2102     }
2103 






2104     protected int getNextTabIndexInRun(int tabCount, int base) {
2105         if (runCount < 2) {
2106             return getNextTabIndex(base);
2107         }
2108         int currentRun = getRunForTab(tabCount, base);
2109         int next = getNextTabIndex(base);
2110         if (next == tabRuns[getNextTabRun(currentRun)]) {
2111             return tabRuns[currentRun];
2112         }
2113         return next;
2114     }
2115 






2116     protected int getPreviousTabIndexInRun(int tabCount, int base) {
2117         if (runCount < 2) {
2118             return getPreviousTabIndex(base);
2119         }
2120         int currentRun = getRunForTab(tabCount, base);
2121         if (base == tabRuns[currentRun]) {
2122             int previous = tabRuns[getNextTabRun(currentRun)]-1;
2123             return (previous != -1? previous : tabCount-1);
2124         }
2125         return getPreviousTabIndex(base);
2126     }
2127 





2128     protected int getPreviousTabRun(int baseRun) {
2129         int runIndex = (baseRun - 1 >= 0? baseRun - 1 : runCount - 1);
2130         return (runIndex >= 0? runIndex : 0);
2131     }
2132 





2133     protected int getNextTabRun(int baseRun) {
2134         return (baseRun+1)%runCount;
2135     }
2136 






2137     protected static void rotateInsets(Insets topInsets, Insets targetInsets, int targetPlacement) {
2138 
2139         switch(targetPlacement) {
2140           case LEFT:
2141               targetInsets.top = topInsets.left;
2142               targetInsets.left = topInsets.top;
2143               targetInsets.bottom = topInsets.right;
2144               targetInsets.right = topInsets.bottom;
2145               break;
2146           case BOTTOM:
2147               targetInsets.top = topInsets.bottom;
2148               targetInsets.left = topInsets.left;
2149               targetInsets.bottom = topInsets.top;
2150               targetInsets.right = topInsets.right;
2151               break;
2152           case RIGHT:
2153               targetInsets.top = topInsets.left;
2154               targetInsets.left = topInsets.bottom;
2155               targetInsets.bottom = topInsets.right;
2156               targetInsets.right = topInsets.top;


2275     }
2276 
2277     /**
2278      * This class should be treated as a &quot;protected&quot; inner class.
2279      * Instantiate it only within subclasses of BasicTabbedPaneUI.
2280      */
2281     public class TabbedPaneLayout implements LayoutManager {
2282 
2283         public void addLayoutComponent(String name, Component comp) {}
2284 
2285         public void removeLayoutComponent(Component comp) {}
2286 
2287         public Dimension preferredLayoutSize(Container parent) {
2288             return calculateSize(false);
2289         }
2290 
2291         public Dimension minimumLayoutSize(Container parent) {
2292             return calculateSize(true);
2293         }
2294 





2295         protected Dimension calculateSize(boolean minimum) {
2296             int tabPlacement = tabPane.getTabPlacement();
2297             Insets insets = tabPane.getInsets();
2298             Insets contentInsets = getContentBorderInsets(tabPlacement);
2299             Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
2300 
2301             Dimension zeroSize = new Dimension(0,0);
2302             int height = 0;
2303             int width = 0;
2304             int cWidth = 0;
2305             int cHeight = 0;
2306 
2307             // Determine minimum size required to display largest
2308             // child in each dimension
2309             //
2310             for (int i = 0; i < tabPane.getTabCount(); i++) {
2311                 Component component = tabPane.getComponentAt(i);
2312                 if (component != null) {
2313                     Dimension size = minimum ? component.getMinimumSize() :
2314                                 component.getPreferredSize();


2329             //
2330             switch(tabPlacement) {
2331               case LEFT:
2332               case RIGHT:
2333                   height = Math.max(height, calculateMaxTabHeight(tabPlacement));
2334                   tabExtent = preferredTabAreaWidth(tabPlacement, height - tabAreaInsets.top - tabAreaInsets.bottom);
2335                   width += tabExtent;
2336                   break;
2337               case TOP:
2338               case BOTTOM:
2339               default:
2340                   width = Math.max(width, calculateMaxTabWidth(tabPlacement));
2341                   tabExtent = preferredTabAreaHeight(tabPlacement, width - tabAreaInsets.left - tabAreaInsets.right);
2342                   height += tabExtent;
2343             }
2344             return new Dimension(width + insets.left + insets.right + contentInsets.left + contentInsets.right,
2345                              height + insets.bottom + insets.top + contentInsets.top + contentInsets.bottom);
2346 
2347         }
2348 






2349         protected int preferredTabAreaHeight(int tabPlacement, int width) {
2350             FontMetrics metrics = getFontMetrics();
2351             int tabCount = tabPane.getTabCount();
2352             int total = 0;
2353             if (tabCount > 0) {
2354                 int rows = 1;
2355                 int x = 0;
2356 
2357                 int maxTabHeight = calculateMaxTabHeight(tabPlacement);
2358 
2359                 for (int i = 0; i < tabCount; i++) {
2360                     int tabWidth = calculateTabWidth(tabPlacement, i, metrics);
2361 
2362                     if (x != 0 && x + tabWidth > width) {
2363                         rows++;
2364                         x = 0;
2365                     }
2366                     x += tabWidth;
2367                 }
2368                 total = calculateTabAreaHeight(tabPlacement, rows, maxTabHeight);
2369             }
2370             return total;
2371         }
2372 






2373         protected int preferredTabAreaWidth(int tabPlacement, int height) {
2374             FontMetrics metrics = getFontMetrics();
2375             int tabCount = tabPane.getTabCount();
2376             int total = 0;
2377             if (tabCount > 0) {
2378                 int columns = 1;
2379                 int y = 0;
2380                 int fontHeight = metrics.getHeight();
2381 
2382                 maxTabWidth = calculateMaxTabWidth(tabPlacement);
2383 
2384                 for (int i = 0; i < tabCount; i++) {
2385                     int tabHeight = calculateTabHeight(tabPlacement, i, fontHeight);
2386 
2387                     if (y != 0 && y + tabHeight > height) {
2388                         columns++;
2389                         y = 0;
2390                     }
2391                     y += tabHeight;
2392                 }
2393                 total = calculateTabAreaWidth(tabPlacement, columns, maxTabWidth);
2394             }
2395             return total;
2396         }
2397 

2398         @SuppressWarnings("deprecation")
2399         public void layoutContainer(Container parent) {
2400             /* Some of the code in this method deals with changing the
2401             * visibility of components to hide and show the contents for the
2402             * selected tab. This is older code that has since been duplicated
2403             * in JTabbedPane.fireStateChanged(), so as to allow visibility
2404             * changes to happen sooner (see the note there). This code remains
2405             * for backward compatibility as there are some cases, such as
2406             * subclasses that don't fireStateChanged() where it may be used.
2407             * Any changes here need to be kept in synch with
2408             * JTabbedPane.fireStateChanged().
2409             */
2410 
2411             setRolloverTab(-1);
2412 
2413             int tabPlacement = tabPane.getTabPlacement();
2414             Insets insets = tabPane.getInsets();
2415             int selectedIndex = tabPane.getSelectedIndex();
2416             Component visibleComponent = getVisibleComponent();
2417 


2500                         int tabContainerY = 0;
2501                         if(tabPlacement == BOTTOM) {
2502                             tabContainerY = bounds.height - tabContainerHeight;
2503                         } else if(tabPlacement == RIGHT) {
2504                             tabContainerX = bounds.width - tabContainerWidth;
2505                         }
2506                         child.setBounds(tabContainerX, tabContainerY, tabContainerWidth, tabContainerHeight);
2507                     } else {
2508                         child.setBounds(cx, cy, cw, ch);
2509                     }
2510                 }
2511             }
2512             layoutTabComponents();
2513             if(shouldChangeFocus) {
2514                 if(!requestFocusForVisibleComponent()) {
2515                     tabPane.requestFocus();
2516                 }
2517             }
2518         }
2519 



2520         public void calculateLayoutInfo() {
2521             int tabCount = tabPane.getTabCount();
2522             assureRectsCreated(tabCount);
2523             calculateTabRects(tabPane.getTabPlacement(), tabCount);
2524             isRunsDirty = false;
2525         }
2526 
2527         private void layoutTabComponents() {
2528             if (tabContainer == null) {
2529                 return;
2530             }
2531             Rectangle rect = new Rectangle();
2532             Point delta = new Point(-tabContainer.getX(), -tabContainer.getY());
2533             if (scrollableTabLayoutEnabled()) {
2534                 translatePointToTabPanel(0, 0, delta);
2535             }
2536             for (int i = 0; i < tabPane.getTabCount(); i++) {
2537                 Component c = tabPane.getTabComponentAt(i);
2538                 if (c == null) {
2539                     continue;
2540                 }
2541                 getTabBounds(i, rect);
2542                 Dimension preferredSize = c.getPreferredSize();
2543                 Insets insets = getTabInsets(tabPane.getTabPlacement(), i);
2544                 int outerX = rect.x + insets.left + delta.x;
2545                 int outerY = rect.y + insets.top + delta.y;
2546                 int outerWidth = rect.width - insets.left - insets.right;
2547                 int outerHeight = rect.height - insets.top - insets.bottom;
2548                 //centralize component
2549                 int x = outerX + (outerWidth - preferredSize.width) / 2;
2550                 int y = outerY + (outerHeight - preferredSize.height) / 2;
2551                 int tabPlacement = tabPane.getTabPlacement();
2552                 boolean isSeleceted = i == tabPane.getSelectedIndex();
2553                 c.setBounds(x + getTabLabelShiftX(tabPlacement, i, isSeleceted),
2554                             y + getTabLabelShiftY(tabPlacement, i, isSeleceted),
2555                         preferredSize.width, preferredSize.height);
2556             }
2557         }
2558 





2559         protected void calculateTabRects(int tabPlacement, int tabCount) {
2560             FontMetrics metrics = getFontMetrics();
2561             Dimension size = tabPane.getSize();
2562             Insets insets = tabPane.getInsets();
2563             Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
2564             int fontHeight = metrics.getHeight();
2565             int selectedIndex = tabPane.getSelectedIndex();
2566             int tabRunOverlay;
2567             int i, j;
2568             int x, y;
2569             int returnAt;
2570             boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
2571             boolean leftToRight = BasicGraphicsUtils.isLeftToRight(tabPane);
2572 
2573             //
2574             // Calculate bounds within which a tab run must fit
2575             //
2576             switch(tabPlacement) {
2577               case LEFT:
2578                   maxTabWidth = calculateMaxTabWidth(tabPlacement);


2724                         x += (maxTabWidth - tabRunOverlay);
2725                     }
2726                 }
2727             }
2728 
2729             // Pad the selected tab so that it appears raised in front
2730             padSelectedTab(tabPlacement, selectedIndex);
2731 
2732             // if right to left and tab placement on the top or
2733             // the bottom, flip x positions and adjust by widths
2734             if (!leftToRight && !verticalTabRuns) {
2735                 int rightMargin = size.width
2736                                   - (insets.right + tabAreaInsets.right);
2737                 for (i = 0; i < tabCount; i++) {
2738                     rects[i].x = rightMargin - rects[i].x - rects[i].width;
2739                 }
2740             }
2741         }
2742 
2743 
2744        /*
2745        * Rotates the run-index array so that the selected run is run[0]


2746        */
2747         protected void rotateTabRuns(int tabPlacement, int selectedRun) {
2748             for (int i = 0; i < selectedRun; i++) {
2749                 int save = tabRuns[0];
2750                 for (int j = 1; j < runCount; j++) {
2751                     tabRuns[j - 1] = tabRuns[j];
2752                 }
2753                 tabRuns[runCount-1] = save;
2754             }
2755         }
2756 







2757         protected void normalizeTabRuns(int tabPlacement, int tabCount,
2758                                      int start, int max) {
2759             boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
2760             int run = runCount - 1;
2761             boolean keepAdjusting = true;
2762             double weight = 1.25;
2763 
2764             // At this point the tab runs are packed to fit as many
2765             // tabs as possible, which can leave the last run with a lot
2766             // of extra space (resulting in very fat tabs on the last run).
2767             // So we'll attempt to distribute this extra space more evenly
2768             // across the runs in order to make the runs look more consistent.
2769             //
2770             // Starting with the last run, determine whether the last tab in
2771             // the previous run would fit (generously) in this run; if so,
2772             // move tab to current run and shift tabs accordingly.  Cycle
2773             // through remaining runs using the same algorithm.
2774             //
2775             while (keepAdjusting) {
2776                 int last = lastTabInRun(tabCount, run);


2805                         }
2806                     }
2807 
2808                 } else if (run == runCount - 1) {
2809                     // no more room left in last run, so we're done!
2810                     keepAdjusting = false;
2811                 }
2812                 if (run - 1 > 0) {
2813                     // check previous run next...
2814                     run -= 1;
2815                 } else {
2816                     // check last run again...but require a higher ratio
2817                     // of extraspace-to-tabsize because we don't want to
2818                     // end up with too many tabs on the last run!
2819                     run = runCount - 1;
2820                     weight += .25;
2821                 }
2822             }
2823         }
2824 







2825         protected void padTabRun(int tabPlacement, int start, int end, int max) {
2826             Rectangle lastRect = rects[end];
2827             if (tabPlacement == TOP || tabPlacement == BOTTOM) {
2828                 int runWidth = (lastRect.x + lastRect.width) - rects[start].x;
2829                 int deltaWidth = max - (lastRect.x + lastRect.width);
2830                 float factor = (float)deltaWidth / (float)runWidth;
2831 
2832                 for (int j = start; j <= end; j++) {
2833                     Rectangle pastRect = rects[j];
2834                     if (j > start) {
2835                         pastRect.x = rects[j-1].x + rects[j-1].width;
2836                     }
2837                     pastRect.width += Math.round((float)pastRect.width * factor);
2838                 }
2839                 lastRect.width = max - lastRect.x;
2840             } else {
2841                 int runHeight = (lastRect.y + lastRect.height) - rects[start].y;
2842                 int deltaHeight = max - (lastRect.y + lastRect.height);
2843                 float factor = (float)deltaHeight / (float)runHeight;
2844 
2845                 for (int j = start; j <= end; j++) {
2846                     Rectangle pastRect = rects[j];
2847                     if (j > start) {
2848                         pastRect.y = rects[j-1].y + rects[j-1].height;
2849                     }
2850                     pastRect.height += Math.round((float)pastRect.height * factor);
2851                 }
2852                 lastRect.height = max - lastRect.y;
2853             }
2854         }
2855 





2856         protected void padSelectedTab(int tabPlacement, int selectedIndex) {
2857 
2858             if (selectedIndex >= 0) {
2859                 Rectangle selRect = rects[selectedIndex];
2860                 Insets padInsets = getSelectedTabPadInsets(tabPlacement);
2861                 selRect.x -= padInsets.left;
2862                 selRect.width += (padInsets.left + padInsets.right);
2863                 selRect.y -= padInsets.top;
2864                 selRect.height += (padInsets.top + padInsets.bottom);
2865 
2866                 if (!scrollableTabLayoutEnabled()) { // WRAP_TAB_LAYOUT
2867                     // do not expand selected tab more then necessary
2868                     Dimension size = tabPane.getSize();
2869                     Insets insets = tabPane.getInsets();
2870 
2871                     if ((tabPlacement == LEFT) || (tabPlacement == RIGHT)) {
2872                         int top = insets.top - selRect.y;
2873                         if (top > 0) {
2874                             selRect.y += top;
2875                             selRect.height -= top;




  39 import java.util.Vector;
  40 import java.util.Hashtable;
  41 
  42 import sun.swing.DefaultLookup;
  43 import sun.swing.UIAction;
  44 
  45 /**
  46  * A Basic L&amp;F implementation of TabbedPaneUI.
  47  *
  48  * @author Amy Fowler
  49  * @author Philip Milne
  50  * @author Steve Wilson
  51  * @author Tom Santos
  52  * @author Dave Moore
  53  */
  54 public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants {
  55 
  56 
  57 // Instance variables initialized at installation
  58 
  59     /** The tab pane */
  60     protected JTabbedPane tabPane;
  61 
  62     /** Highlight color */
  63     protected Color highlight;
  64     /** Light highlight color */
  65     protected Color lightHighlight;
  66     /** Shadow color */
  67     protected Color shadow;
  68     /** Dark shadow color */
  69     protected Color darkShadow;
  70     /** Focus color */
  71     protected Color focus;
  72     private   Color selectedColor;
  73 
  74     /** Text icon gap */
  75     protected int textIconGap;
  76     /** Tab run overlay */
  77     protected int tabRunOverlay;
  78 
  79     /** Tab insets */
  80     protected Insets tabInsets;
  81     /** Selected tab insets */
  82     protected Insets selectedTabPadInsets;
  83     /** Tab area insets */
  84     protected Insets tabAreaInsets;
  85     /** Content border insets */
  86     protected Insets contentBorderInsets;
  87     private boolean tabsOverlapBorder;
  88     private boolean tabsOpaque = true;
  89     private boolean contentOpaque = true;
  90 
  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 upKey;
 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.


 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 leftKey;
 121     /**
 122      * As of Java 2 platform v1.3 this previously undocumented field is no
 123      * longer used.
 124      * Key bindings are now defined by the LookAndFeel, please refer to
 125      * the key bindings specification for further details.
 126      *
 127      * @deprecated As of Java 2 platform v1.3.
 128      */
 129     @Deprecated
 130     protected KeyStroke rightKey;
 131 
 132 
 133 // Transient variables (recalculated each time TabbedPane is layed out)
 134     /** Tab runs */
 135     protected int tabRuns[] = new int[10];
 136     /** Run count */
 137     protected int runCount = 0;
 138     /** Selected run */
 139     protected int selectedRun = -1;
 140     /** Tab rects */
 141     protected Rectangle rects[] = new Rectangle[0];
 142     /** Maximum tab height */
 143     protected int maxTabHeight;
 144     /** Maximum tab width */
 145     protected int maxTabWidth;
 146 
 147 // Listeners
 148 
 149     /** Tab change listener */
 150     protected ChangeListener tabChangeListener;
 151     /** Property change listener */
 152     protected PropertyChangeListener propertyChangeListener;
 153     /** Mouse change listener */
 154     protected MouseListener mouseListener;
 155     /** Focus change listener */
 156     protected FocusListener focusListener;
 157 
 158 // Private instance data
 159 
 160     private Insets currentPadInsets = new Insets(0,0,0,0);
 161     private Insets currentTabAreaInsets = new Insets(0,0,0,0);
 162 
 163     private Component visibleComponent;
 164     // PENDING(api): See comment for ContainerHandler
 165     private Vector<View> htmlViews;
 166 
 167     private Hashtable<Integer, Integer> mnemonicToIndexMap;
 168 
 169     /**
 170      * InputMap used for mnemonics. Only non-null if the JTabbedPane has
 171      * mnemonics associated with it. Lazily created in initMnemonics.
 172      */
 173     private InputMap mnemonicInputMap;
 174 
 175     // For use when tabLayoutPolicy = SCROLL_TAB_LAYOUT


 193      */
 194     private Handler handler;
 195 
 196     /**
 197      * Index of the tab the mouse is over.
 198      */
 199     private int rolloverTabIndex;
 200 
 201     /**
 202      * This is set to true when a component is added/removed from the tab
 203      * pane and set to false when layout happens.  If true it indicates that
 204      * tabRuns is not valid and shouldn't be used.
 205      */
 206     private boolean isRunsDirty;
 207 
 208     private boolean calculatedBaseline;
 209     private int baseline;
 210 
 211 // UI creation
 212 
 213     /**
 214      * Create a UI.
 215      * @param c a component
 216      * @return a UI
 217      */
 218     public static ComponentUI createUI(JComponent c) {
 219         return new BasicTabbedPaneUI();
 220     }
 221 
 222     static void loadActionMap(LazyActionMap map) {
 223         map.put(new Actions(Actions.NEXT));
 224         map.put(new Actions(Actions.PREVIOUS));
 225         map.put(new Actions(Actions.RIGHT));
 226         map.put(new Actions(Actions.LEFT));
 227         map.put(new Actions(Actions.UP));
 228         map.put(new Actions(Actions.DOWN));
 229         map.put(new Actions(Actions.PAGE_UP));
 230         map.put(new Actions(Actions.PAGE_DOWN));
 231         map.put(new Actions(Actions.REQUEST_FOCUS));
 232         map.put(new Actions(Actions.REQUEST_FOCUS_FOR_VISIBLE));
 233         map.put(new Actions(Actions.SET_SELECTED));
 234         map.put(new Actions(Actions.SELECT_FOCUSED));
 235         map.put(new Actions(Actions.SCROLL_FORWARD));
 236         map.put(new Actions(Actions.SCROLL_BACKWARD));
 237     }


 363         }
 364     }
 365 
 366     private void uninstallTabContainer() {
 367          if(tabContainer == null) {
 368              return;
 369          }
 370          // Remove all the tabComponents, making sure not to notify
 371          // the tabbedpane.
 372          tabContainer.notifyTabbedPane = false;
 373          tabContainer.removeAll();
 374          if(scrollableTabLayoutEnabled()) {
 375              tabContainer.remove(tabScroller.croppedEdge);
 376              tabScroller.tabPanel.remove(tabContainer);
 377          } else {
 378            tabPane.remove(tabContainer);
 379          }
 380          tabContainer = null;
 381     }
 382 
 383     /**
 384      * Install the defaults.
 385      */
 386     protected void installDefaults() {
 387         LookAndFeel.installColorsAndFont(tabPane, "TabbedPane.background",
 388                                     "TabbedPane.foreground", "TabbedPane.font");
 389         highlight = UIManager.getColor("TabbedPane.light");
 390         lightHighlight = UIManager.getColor("TabbedPane.highlight");
 391         shadow = UIManager.getColor("TabbedPane.shadow");
 392         darkShadow = UIManager.getColor("TabbedPane.darkShadow");
 393         focus = UIManager.getColor("TabbedPane.focus");
 394         selectedColor = UIManager.getColor("TabbedPane.selected");
 395 
 396         textIconGap = UIManager.getInt("TabbedPane.textIconGap");
 397         tabInsets = UIManager.getInsets("TabbedPane.tabInsets");
 398         selectedTabPadInsets = UIManager.getInsets("TabbedPane.selectedTabPadInsets");
 399         tabAreaInsets = UIManager.getInsets("TabbedPane.tabAreaInsets");
 400         tabsOverlapBorder = UIManager.getBoolean("TabbedPane.tabsOverlapBorder");
 401         contentBorderInsets = UIManager.getInsets("TabbedPane.contentBorderInsets");
 402         tabRunOverlay = UIManager.getInt("TabbedPane.tabRunOverlay");
 403         tabsOpaque = UIManager.getBoolean("TabbedPane.tabsOpaque");
 404         contentOpaque = UIManager.getBoolean("TabbedPane.contentOpaque");
 405         Object opaque = UIManager.get("TabbedPane.opaque");
 406         if (opaque == null) {
 407             opaque = Boolean.FALSE;
 408         }
 409         LookAndFeel.installProperty(tabPane, "opaque", opaque);
 410 
 411         // Fix for 6711145 BasicTabbedPanuUI should not throw a NPE if these
 412         // keys are missing. So we are setting them to there default values here
 413         // if the keys are missing.
 414         if (tabInsets == null) tabInsets = new Insets(0,4,1,4);
 415         if (selectedTabPadInsets == null) selectedTabPadInsets = new Insets(2,2,2,1);
 416         if (tabAreaInsets == null) tabAreaInsets = new Insets(3,2,0,2);
 417         if (contentBorderInsets == null) contentBorderInsets = new Insets(2,2,3,3);
 418     }
 419 
 420     /**
 421      * Uninstall the defaults.
 422      */
 423     protected void uninstallDefaults() {
 424         highlight = null;
 425         lightHighlight = null;
 426         shadow = null;
 427         darkShadow = null;
 428         focus = null;
 429         tabInsets = null;
 430         selectedTabPadInsets = null;
 431         tabAreaInsets = null;
 432         contentBorderInsets = null;
 433     }
 434 
 435     /**
 436      * Install the listeners.
 437      */
 438     protected void installListeners() {
 439         if ((propertyChangeListener = createPropertyChangeListener()) != null) {
 440             tabPane.addPropertyChangeListener(propertyChangeListener);
 441         }
 442         if ((tabChangeListener = createChangeListener()) != null) {
 443             tabPane.addChangeListener(tabChangeListener);
 444         }
 445         if ((mouseListener = createMouseListener()) != null) {
 446             tabPane.addMouseListener(mouseListener);
 447         }
 448         tabPane.addMouseMotionListener(getHandler());
 449         if ((focusListener = createFocusListener()) != null) {
 450             tabPane.addFocusListener(focusListener);
 451         }
 452         tabPane.addContainerListener(getHandler());
 453         if (tabPane.getTabCount()>0) {
 454             htmlViews = createHTMLVector();
 455         }
 456     }
 457 
 458     /**
 459      * Uninstall the listeners.
 460      */
 461     protected void uninstallListeners() {
 462         if (mouseListener != null) {
 463             tabPane.removeMouseListener(mouseListener);
 464             mouseListener = null;
 465         }
 466         tabPane.removeMouseMotionListener(getHandler());
 467         if (focusListener != null) {
 468             tabPane.removeFocusListener(focusListener);
 469             focusListener = null;
 470         }
 471 
 472         tabPane.removeContainerListener(getHandler());
 473         if (htmlViews!=null) {
 474             htmlViews.removeAllElements();
 475             htmlViews = null;
 476         }
 477         if (tabChangeListener != null) {
 478             tabPane.removeChangeListener(tabChangeListener);
 479             tabChangeListener = null;
 480         }
 481         if (propertyChangeListener != null) {
 482             tabPane.removePropertyChangeListener(propertyChangeListener);
 483             propertyChangeListener = null;
 484         }
 485         handler = null;
 486     }
 487 
 488     /**
 489      * Creates a mouse listener.
 490      * @return a mouse listener
 491      */
 492     protected MouseListener createMouseListener() {
 493         return getHandler();
 494     }
 495 
 496     /**
 497      * Creates a focus listener.
 498      * @return a focus listener
 499      */
 500     protected FocusListener createFocusListener() {
 501         return getHandler();
 502     }
 503 
 504     /**
 505      * Creates a change listener.
 506      * @return a change listener
 507      */
 508     protected ChangeListener createChangeListener() {
 509         return getHandler();
 510     }
 511 
 512     /**
 513      * Creates a property change listener.
 514      * @return a property change listener
 515      */
 516     protected PropertyChangeListener createPropertyChangeListener() {
 517         return getHandler();
 518     }
 519 
 520     private Handler getHandler() {
 521         if (handler == null) {
 522             handler = new Handler();
 523         }
 524         return handler;
 525     }
 526 
 527     /**
 528      * Installs the keyboard actions.
 529      */
 530     protected void installKeyboardActions() {
 531         InputMap km = getInputMap(JComponent.
 532                                   WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
 533 
 534         SwingUtilities.replaceUIInputMap(tabPane, JComponent.
 535                                          WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
 536                                          km);
 537         km = getInputMap(JComponent.WHEN_FOCUSED);
 538         SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, km);
 539 
 540         LazyActionMap.installLazyActionMap(tabPane, BasicTabbedPaneUI.class,
 541                                            "TabbedPane.actionMap");
 542         updateMnemonics();
 543     }
 544 
 545     InputMap getInputMap(int condition) {
 546         if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
 547             return (InputMap)DefaultLookup.get(tabPane, this,
 548                                                "TabbedPane.ancestorInputMap");
 549         }
 550         else if (condition == JComponent.WHEN_FOCUSED) {
 551             return (InputMap)DefaultLookup.get(tabPane, this,
 552                                                "TabbedPane.focusInputMap");
 553         }
 554         return null;
 555     }
 556 
 557     /**
 558      * Uninstalls the keyboard actions.
 559      */
 560     protected void uninstallKeyboardActions() {
 561         SwingUtilities.replaceUIActionMap(tabPane, null);
 562         SwingUtilities.replaceUIInputMap(tabPane, JComponent.
 563                                          WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
 564                                          null);
 565         SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED,
 566                                          null);
 567         SwingUtilities.replaceUIInputMap(tabPane,
 568                                          JComponent.WHEN_IN_FOCUSED_WINDOW,
 569                                          null);
 570         mnemonicToIndexMap = null;
 571         mnemonicInputMap = null;
 572     }
 573 
 574     /**
 575      * Reloads the mnemonics. This should be invoked when a memonic changes,
 576      * when the title of a mnemonic changes, or when tabs are added/removed.
 577      */
 578     private void updateMnemonics() {
 579         resetMnemonics();


 886 
 887         // Paint tabRuns of tabs from back to front
 888         for (int i = runCount - 1; i >= 0; i--) {
 889             int start = tabRuns[i];
 890             int next = tabRuns[(i == runCount - 1)? 0 : i + 1];
 891             int end = (next != 0? next - 1: tabCount - 1);
 892             for (int j = start; j <= end; j++) {
 893                 if (j != selectedIndex && rects[j].intersects(clipRect)) {
 894                     paintTab(g, tabPlacement, rects, j, iconRect, textRect);
 895                 }
 896             }
 897         }
 898 
 899         // Paint selected tab if its in the front run
 900         // since it may overlap other tabs
 901         if (selectedIndex >= 0 && rects[selectedIndex].intersects(clipRect)) {
 902             paintTab(g, tabPlacement, rects, selectedIndex, iconRect, textRect);
 903         }
 904     }
 905 
 906     /**
 907      * Paints a tab.
 908      * @param g the graphics
 909      * @param tabPlacement the tab placement
 910      * @param rects rectangles
 911      * @param tabIndex the tab index
 912      * @param iconRect the icon rectangle
 913      * @param textRect the text rectangle
 914      */
 915     protected void paintTab(Graphics g, int tabPlacement,
 916                             Rectangle[] rects, int tabIndex,
 917                             Rectangle iconRect, Rectangle textRect) {
 918         Rectangle tabRect = rects[tabIndex];
 919         int selectedIndex = tabPane.getSelectedIndex();
 920         boolean isSelected = selectedIndex == tabIndex;
 921 
 922         if (tabsOpaque || tabPane.isOpaque()) {
 923             paintTabBackground(g, tabPlacement, tabIndex, tabRect.x, tabRect.y,
 924                     tabRect.width, tabRect.height, isSelected);
 925         }
 926 
 927         paintTabBorder(g, tabPlacement, tabIndex, tabRect.x, tabRect.y,
 928                        tabRect.width, tabRect.height, isSelected);
 929 
 930         String title = tabPane.getTitleAt(tabIndex);
 931         Font font = tabPane.getFont();
 932         FontMetrics metrics = SwingUtilities2.getFontMetrics(tabPane, g, font);
 933         Icon icon = getIconForTab(tabIndex);
 934 


1063                 xx+=CROP_SEGMENT;
1064             }
1065             break;
1066           case TOP:
1067           case BOTTOM:
1068           default:
1069             x = cropline;
1070             y = rects[tabIndex].y;
1071             int yy = y;
1072             g.setColor(shadow);
1073             while(yy <= y+rects[tabIndex].height) {
1074                 for (int i=0; i < xCropLen.length; i+=2) {
1075                     g.drawLine(x-xCropLen[i],yy+yCropLen[i],
1076                                x-xCropLen[i+1],yy+yCropLen[i+1]-1);
1077                 }
1078                 yy+=CROP_SEGMENT;
1079             }
1080         }
1081     }
1082 
1083     /**
1084      * Laysout a label.
1085      * @param tabPlacement the tab placement
1086      * @param metrics the font metric
1087      * @param tabIndex the tab index
1088      * @param title the title
1089      * @param icon the icon
1090      * @param tabRect the tab rectangle
1091      * @param iconRect the icon rectangle
1092      * @param textRect the text rectangle
1093      * @param isSelected selection status
1094      */
1095     protected void layoutLabel(int tabPlacement,
1096                                FontMetrics metrics, int tabIndex,
1097                                String title, Icon icon,
1098                                Rectangle tabRect, Rectangle iconRect,
1099                                Rectangle textRect, boolean isSelected ) {
1100         textRect.x = textRect.y = iconRect.x = iconRect.y = 0;
1101 
1102         View v = getTextViewForTab(tabIndex);
1103         if (v != null) {
1104             tabPane.putClientProperty("html", v);
1105         }
1106 
1107         SwingUtilities.layoutCompoundLabel(tabPane,
1108                                            metrics, title, icon,
1109                                            SwingUtilities.CENTER,
1110                                            SwingUtilities.CENTER,
1111                                            SwingUtilities.CENTER,
1112                                            SwingUtilities.TRAILING,
1113                                            tabRect,
1114                                            iconRect,
1115                                            textRect,
1116                                            textIconGap);
1117 
1118         tabPane.putClientProperty("html", null);
1119 
1120         int xNudge = getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
1121         int yNudge = getTabLabelShiftY(tabPlacement, tabIndex, isSelected);
1122         iconRect.x += xNudge;
1123         iconRect.y += yNudge;
1124         textRect.x += xNudge;
1125         textRect.y += yNudge;
1126     }
1127 
1128     /**
1129      * Paints an icon.
1130      * @param g the graphics
1131      * @param tabPlacement the tab placement
1132      * @param tabIndex the tab index
1133      * @param icon the icon
1134      * @param iconRect the icon rectangle
1135      * @param isSelected selection status
1136      */
1137     protected void paintIcon(Graphics g, int tabPlacement,
1138                              int tabIndex, Icon icon, Rectangle iconRect,
1139                              boolean isSelected ) {
1140         if (icon != null) {
1141             icon.paintIcon(tabPane, g, iconRect.x, iconRect.y);
1142         }
1143     }
1144 
1145     /**
1146      * Paints text.
1147      * @param g the graphics
1148      * @param tabPlacement the tab placement
1149      * @param font the font
1150      * @param metrics the font metrics
1151      * @param tabIndex the tab index
1152      * @param title the title
1153      * @param textRect the text rectangle
1154      * @param isSelected selection status
1155      */
1156     protected void paintText(Graphics g, int tabPlacement,
1157                              Font font, FontMetrics metrics, int tabIndex,
1158                              String title, Rectangle textRect,
1159                              boolean isSelected) {
1160 
1161         g.setFont(font);
1162 
1163         View v = getTextViewForTab(tabIndex);
1164         if (v != null) {
1165             // html
1166             v.paint(g, textRect);
1167         } else {
1168             // plain text
1169             int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
1170 
1171             if (tabPane.isEnabled() && tabPane.isEnabledAt(tabIndex)) {
1172                 Color fg = tabPane.getForegroundAt(tabIndex);
1173                 if (isSelected && (fg instanceof UIResource)) {
1174                     Color selectedFG = UIManager.getColor(
1175                                   "TabbedPane.selectedForeground");


1179                 }
1180                 g.setColor(fg);
1181                 SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
1182                              title, mnemIndex,
1183                              textRect.x, textRect.y + metrics.getAscent());
1184 
1185             } else { // tab disabled
1186                 g.setColor(tabPane.getBackgroundAt(tabIndex).brighter());
1187                 SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
1188                              title, mnemIndex,
1189                              textRect.x, textRect.y + metrics.getAscent());
1190                 g.setColor(tabPane.getBackgroundAt(tabIndex).darker());
1191                 SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
1192                              title, mnemIndex,
1193                              textRect.x - 1, textRect.y + metrics.getAscent() - 1);
1194 
1195             }
1196         }
1197     }
1198 
1199     /**
1200      * Returns the tab label shift x.
1201      * @param tabPlacement the tab placement
1202      * @param tabIndex the tab index
1203      * @param isSelected selection status
1204      * @return the tab label shift x
1205      */
1206     protected int getTabLabelShiftX(int tabPlacement, int tabIndex, boolean isSelected) {
1207         Rectangle tabRect = rects[tabIndex];
1208         String propKey = (isSelected ? "selectedLabelShift" : "labelShift");
1209         int nudge = DefaultLookup.getInt(
1210                 tabPane, this, "TabbedPane." + propKey, 1);
1211 
1212         switch (tabPlacement) {
1213             case LEFT:
1214                 return nudge;
1215             case RIGHT:
1216                 return -nudge;
1217             case BOTTOM:
1218             case TOP:
1219             default:
1220                 return tabRect.width % 2;
1221         }
1222     }
1223 
1224     /**
1225      * Returns the tab label shift y.
1226      * @param tabPlacement the tab placement
1227      * @param tabIndex the tab index
1228      * @param isSelected selection status
1229      * @return the tab label shift y
1230      */
1231     protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) {
1232         Rectangle tabRect = rects[tabIndex];
1233         int nudge = (isSelected ? DefaultLookup.getInt(tabPane, this, "TabbedPane.selectedLabelShift", -1) :
1234                 DefaultLookup.getInt(tabPane, this, "TabbedPane.labelShift", 1));
1235 
1236         switch (tabPlacement) {
1237             case BOTTOM:
1238                 return -nudge;
1239             case LEFT:
1240             case RIGHT:
1241                 return tabRect.height % 2;
1242             case TOP:
1243             default:
1244                 return nudge;
1245         }
1246     }
1247 
1248     /**
1249      * Paints the focus indicator.
1250      * @param g the graphics
1251      * @param tabPlacement the tab placement
1252      * @param rects rectangles
1253      * @param tabIndex the tab index
1254      * @param iconRect the icon rectangle
1255      * @param textRect the text rectangle
1256      * @param isSelected selection status
1257      */
1258     protected void paintFocusIndicator(Graphics g, int tabPlacement,
1259                                        Rectangle[] rects, int tabIndex,
1260                                        Rectangle iconRect, Rectangle textRect,
1261                                        boolean isSelected) {
1262         Rectangle tabRect = rects[tabIndex];
1263         if (tabPane.hasFocus() && isSelected) {
1264             int x, y, w, h;
1265             g.setColor(focus);
1266             switch(tabPlacement) {
1267               case LEFT:
1268                   x = tabRect.x + 3;
1269                   y = tabRect.y + 3;
1270                   w = tabRect.width - 5;
1271                   h = tabRect.height - 6;
1272                   break;
1273               case RIGHT:
1274                   x = tabRect.x + 2;
1275                   y = tabRect.y + 3;
1276                   w = tabRect.width - 5;
1277                   h = tabRect.height - 6;


1351               g.setColor(darkShadow);
1352               g.drawLine(x+2, y+h-1, x+w-3, y+h-1); // bottom dark shadow
1353               g.drawLine(x+w-2, y+h-2, x+w-2, y+h-2); // bottom-right dark shadow
1354               g.drawLine(x+w-1, y, x+w-1, y+h-3); // right dark shadow
1355               break;
1356           case TOP:
1357           default:
1358               g.drawLine(x, y+2, x, y+h-1); // left highlight
1359               g.drawLine(x+1, y+1, x+1, y+1); // top-left highlight
1360               g.drawLine(x+2, y, x+w-3, y); // top highlight
1361 
1362               g.setColor(shadow);
1363               g.drawLine(x+w-2, y+2, x+w-2, y+h-1); // right shadow
1364 
1365               g.setColor(darkShadow);
1366               g.drawLine(x+w-1, y+2, x+w-1, y+h-1); // right dark-shadow
1367               g.drawLine(x+w-2, y+1, x+w-2, y+1); // top-right shadow
1368         }
1369     }
1370 
1371     /**
1372      * Paints the tab background.
1373      * @param g             the graphics context in which to paint
1374      * @param tabPlacement  the placement (left, right, bottom, top) of the tab
1375      * @param tabIndex      the index of the tab with respect to other tabs
1376      * @param x             the x coordinate of tab
1377      * @param y             the y coordinate of tab
1378      * @param w             the width of the tab
1379      * @param h             the height of the tab
1380      * @param isSelected    a {@code boolean} which determines whether or not
1381      * the tab is selected
1382      */
1383     protected void paintTabBackground(Graphics g, int tabPlacement,
1384                                       int tabIndex,
1385                                       int x, int y, int w, int h,
1386                                       boolean isSelected ) {
1387         g.setColor(!isSelected || selectedColor == null?
1388                    tabPane.getBackgroundAt(tabIndex) : selectedColor);
1389         switch(tabPlacement) {
1390           case LEFT:
1391               g.fillRect(x+1, y+1, w-1, h-3);
1392               break;
1393           case RIGHT:
1394               g.fillRect(x, y+1, w-2, h-3);
1395               break;
1396           case BOTTOM:
1397               g.fillRect(x+1, y, w-3, h-1);
1398               break;
1399           case TOP:
1400           default:
1401               g.fillRect(x+1, y+1, w-3, h-1);
1402         }
1403     }
1404 
1405     /**
1406      * Paints the content border.
1407      * @param g             the graphics context in which to paint
1408      * @param tabPlacement  the placement (left, right, bottom, top) of the tab
1409      * @param selectedIndex the tab index of the selected component
1410      */
1411     protected void paintContentBorder(Graphics g, int tabPlacement, int selectedIndex) {
1412         int width = tabPane.getWidth();
1413         int height = tabPane.getHeight();
1414         Insets insets = tabPane.getInsets();
1415         Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
1416 
1417         int x = insets.left;
1418         int y = insets.top;
1419         int w = width - insets.right - insets.left;
1420         int h = height - insets.top - insets.bottom;
1421 
1422         switch(tabPlacement) {
1423           case LEFT:
1424               x += calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
1425               if (tabsOverlapBorder) {
1426                   x -= tabAreaInsets.right;
1427               }
1428               w -= (x - insets.left);
1429               break;
1430           case RIGHT:


1453             Color color = UIManager.getColor("TabbedPane.contentAreaColor");
1454             if (color != null) {
1455                 g.setColor(color);
1456             }
1457             else if ( selectedColor == null || selectedIndex == -1 ) {
1458                 g.setColor(tabPane.getBackground());
1459             }
1460             else {
1461                 g.setColor(selectedColor);
1462             }
1463             g.fillRect(x,y,w,h);
1464         }
1465 
1466         paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h);
1467         paintContentBorderLeftEdge(g, tabPlacement, selectedIndex, x, y, w, h);
1468         paintContentBorderBottomEdge(g, tabPlacement, selectedIndex, x, y, w, h);
1469         paintContentBorderRightEdge(g, tabPlacement, selectedIndex, x, y, w, h);
1470 
1471     }
1472 
1473     /**
1474      * Paints the content border top edge.
1475      * @param g             the graphics context in which to paint
1476      * @param tabPlacement  the placement (left, right, bottom, top) of the tab
1477      * @param selectedIndex the tab index of the selected component
1478      * @param x             the x coordinate of tab
1479      * @param y             the y coordinate of tab
1480      * @param w             the width of the tab
1481      * @param h             the height of the tab
1482      */
1483     protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
1484                                          int selectedIndex,
1485                                          int x, int y, int w, int h) {
1486         Rectangle selRect = selectedIndex < 0? null :
1487                                getTabBounds(selectedIndex, calcRect);
1488 
1489         g.setColor(lightHighlight);
1490 
1491         // Draw unbroken line if tabs are not on TOP, OR
1492         // selected tab is not in run adjacent to content, OR
1493         // selected tab is not visible (SCROLL_TAB_LAYOUT)
1494         //
1495         if (tabPlacement != TOP || selectedIndex < 0 ||
1496             (selRect.y + selRect.height + 1 < y) ||
1497             (selRect.x < x || selRect.x > x + w)) {
1498             g.drawLine(x, y, x+w-2, y);
1499         } else {
1500             // Break line to show visual connection to selected tab
1501             g.drawLine(x, y, selRect.x - 1, y);
1502             if (selRect.x + selRect.width < x + w - 2) {
1503                 g.drawLine(selRect.x + selRect.width, y,
1504                            x+w-2, y);
1505             } else {
1506                 g.setColor(shadow);
1507                 g.drawLine(x+w-2, y, x+w-2, y);
1508             }
1509         }
1510     }
1511 
1512     /**
1513      * Paints the content border left edge.
1514      * @param g             the graphics context in which to paint
1515      * @param tabPlacement  the placement (left, right, bottom, top) of the tab
1516      * @param selectedIndex the tab index of the selected component
1517      * @param x             the x coordinate of tab
1518      * @param y             the y coordinate of tab
1519      * @param w             the width of the tab
1520      * @param h             the height of the tab
1521      */
1522     protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
1523                                                int selectedIndex,
1524                                                int x, int y, int w, int h) {
1525         Rectangle selRect = selectedIndex < 0? null :
1526                                getTabBounds(selectedIndex, calcRect);
1527 
1528         g.setColor(lightHighlight);
1529 
1530         // Draw unbroken line if tabs are not on LEFT, OR
1531         // selected tab is not in run adjacent to content, OR
1532         // selected tab is not visible (SCROLL_TAB_LAYOUT)
1533         //
1534         if (tabPlacement != LEFT || selectedIndex < 0 ||
1535             (selRect.x + selRect.width + 1 < x) ||
1536             (selRect.y < y || selRect.y > y + h)) {
1537             g.drawLine(x, y, x, y+h-2);
1538         } else {
1539             // Break line to show visual connection to selected tab
1540             g.drawLine(x, y, x, selRect.y - 1);
1541             if (selRect.y + selRect.height < y + h - 2) {
1542                 g.drawLine(x, selRect.y + selRect.height,
1543                            x, y+h-2);
1544             }
1545         }
1546     }
1547 
1548     /**
1549      * Paints the content border bottom edge.
1550      * @param g             the graphics context in which to paint
1551      * @param tabPlacement  the placement (left, right, bottom, top) of the tab
1552      * @param selectedIndex the tab index of the selected component
1553      * @param x             the x coordinate of tab
1554      * @param y             the y coordinate of tab
1555      * @param w             the width of the tab
1556      * @param h             the height of the tab
1557      */
1558     protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
1559                                                int selectedIndex,
1560                                                int x, int y, int w, int h) {
1561         Rectangle selRect = selectedIndex < 0? null :
1562                                getTabBounds(selectedIndex, calcRect);
1563 
1564         g.setColor(shadow);
1565 
1566         // Draw unbroken line if tabs are not on BOTTOM, OR
1567         // selected tab is not in run adjacent to content, OR
1568         // selected tab is not visible (SCROLL_TAB_LAYOUT)
1569         //
1570         if (tabPlacement != BOTTOM || selectedIndex < 0 ||
1571              (selRect.y - 1 > h) ||
1572              (selRect.x < x || selRect.x > x + w)) {
1573             g.drawLine(x+1, y+h-2, x+w-2, y+h-2);
1574             g.setColor(darkShadow);
1575             g.drawLine(x, y+h-1, x+w-1, y+h-1);
1576         } else {
1577             // Break line to show visual connection to selected tab
1578             g.drawLine(x+1, y+h-2, selRect.x - 1, y+h-2);
1579             g.setColor(darkShadow);
1580             g.drawLine(x, y+h-1, selRect.x - 1, y+h-1);
1581             if (selRect.x + selRect.width < x + w - 2) {
1582                 g.setColor(shadow);
1583                 g.drawLine(selRect.x + selRect.width, y+h-2, x+w-2, y+h-2);
1584                 g.setColor(darkShadow);
1585                 g.drawLine(selRect.x + selRect.width, y+h-1, x+w-1, y+h-1);
1586             }
1587         }
1588 
1589     }
1590 
1591     /**
1592      * Paints the content border right edge.
1593      * @param g             the graphics context in which to paint
1594      * @param tabPlacement  the placement (left, right, bottom, top) of the tab
1595      * @param selectedIndex the tab index of the selected component
1596      * @param x             the x coordinate of tab
1597      * @param y             the y coordinate of tab
1598      * @param w             the width of the tab
1599      * @param h             the height of the tab
1600      */
1601     protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
1602                                                int selectedIndex,
1603                                                int x, int y, int w, int h) {
1604         Rectangle selRect = selectedIndex < 0? null :
1605                                getTabBounds(selectedIndex, calcRect);
1606 
1607         g.setColor(shadow);
1608 
1609         // Draw unbroken line if tabs are not on RIGHT, OR
1610         // selected tab is not in run adjacent to content, OR
1611         // selected tab is not visible (SCROLL_TAB_LAYOUT)
1612         //
1613         if (tabPlacement != RIGHT || selectedIndex < 0 ||
1614              (selRect.x - 1 > w) ||
1615              (selRect.y < y || selRect.y > y + h)) {
1616             g.drawLine(x+w-2, y+1, x+w-2, y+h-3);
1617             g.setColor(darkShadow);
1618             g.drawLine(x+w-1, y, x+w-1, y+h-1);
1619         } else {
1620             // Break line to show visual connection to selected tab


1782             }
1783         }
1784         return min;
1785     }
1786 
1787     /**
1788      * Returns a point which is translated from the specified point in the
1789      * JTabbedPane's coordinate space to the coordinate space of the
1790      * ScrollableTabPanel.  This is used for SCROLL_TAB_LAYOUT ONLY.
1791      */
1792     private Point translatePointToTabPanel(int srcx, int srcy, Point dest) {
1793         Point vpp = tabScroller.viewport.getLocation();
1794         Point viewp = tabScroller.viewport.getViewPosition();
1795         dest.x = srcx - vpp.x + viewp.x;
1796         dest.y = srcy - vpp.y + viewp.y;
1797         return dest;
1798     }
1799 
1800 // BasicTabbedPaneUI methods
1801 
1802     /**
1803      * Returns the visible component.
1804      * @return the visible component
1805      */
1806     protected Component getVisibleComponent() {
1807         return visibleComponent;
1808     }
1809 
1810     /**
1811      * Sets the visible component.
1812      * @param component the component
1813      */
1814     protected void setVisibleComponent(Component component) {
1815         if (visibleComponent != null
1816                 && visibleComponent != component
1817                 && visibleComponent.getParent() == tabPane
1818                 && visibleComponent.isVisible()) {
1819 
1820             visibleComponent.setVisible(false);
1821         }
1822         if (component != null && !component.isVisible()) {
1823             component.setVisible(true);
1824         }
1825         visibleComponent = component;
1826     }
1827 
1828     /**
1829      * Assure the rectangles are created.
1830      * @param tabCount the tab count
1831      */
1832     protected void assureRectsCreated(int tabCount) {
1833         int rectArrayLen = rects.length;
1834         if (tabCount != rectArrayLen ) {
1835             Rectangle[] tempRectArray = new Rectangle[tabCount];
1836             System.arraycopy(rects, 0, tempRectArray, 0,
1837                              Math.min(rectArrayLen, tabCount));
1838             rects = tempRectArray;
1839             for (int rectIndex = rectArrayLen; rectIndex < tabCount; rectIndex++) {
1840                 rects[rectIndex] = new Rectangle();
1841             }
1842         }
1843 
1844     }
1845 
1846     /**
1847      * Expands the tab runs array.
1848      */
1849     protected void expandTabRunsArray() {
1850         int rectLen = tabRuns.length;
1851         int[] newArray = new int[rectLen+10];
1852         System.arraycopy(tabRuns, 0, newArray, 0, runCount);
1853         tabRuns = newArray;
1854     }
1855 
1856     /**
1857      * Returns the run for a tab.
1858      * @param tabCount the tab count
1859      * @param tabIndex the tab index.
1860      * @return the run for a tab
1861      */
1862     protected int getRunForTab(int tabCount, int tabIndex) {
1863         for (int i = 0; i < runCount; i++) {
1864             int first = tabRuns[i];
1865             int last = lastTabInRun(tabCount, i);
1866             if (tabIndex >= first && tabIndex <= last) {
1867                 return i;
1868             }
1869         }
1870         return 0;
1871     }
1872 
1873     /**
1874      * Returns the last tab in a run.
1875      * @param tabCount the tab count
1876      * @param run the run
1877      * @return the last tab in a run
1878      */
1879     protected int lastTabInRun(int tabCount, int run) {
1880         if (runCount == 1) {
1881             return tabCount - 1;
1882         }
1883         int nextRun = (run == runCount - 1? 0 : run + 1);
1884         if (tabRuns[nextRun] == 0) {
1885             return tabCount - 1;
1886         }
1887         return tabRuns[nextRun]-1;
1888     }
1889 
1890     /**
1891      * Returns the tab run overlay.
1892      * @param tabPlacement the placement (left, right, bottom, top) of the tab
1893      * @return the tab run overlay
1894      */
1895     protected int getTabRunOverlay(int tabPlacement) {
1896         return tabRunOverlay;
1897     }
1898 
1899     /**
1900      * Returns the tab run indent.
1901      * @param tabPlacement the placement (left, right, bottom, top) of the tab
1902      * @param run the tab run
1903      * @return the tab run indent
1904      */
1905     protected int getTabRunIndent(int tabPlacement, int run) {
1906         return 0;
1907     }
1908 
1909     /**
1910      * Returns whether or not the tab run should be padded.
1911      * @param tabPlacement the placement (left, right, bottom, top) of the tab
1912      * @param run the tab run
1913      * @return whether or not the tab run should be padded
1914      */
1915     protected boolean shouldPadTabRun(int tabPlacement, int run) {
1916         return runCount > 1;
1917     }
1918 
1919     /**
1920      * Returns whether or not the tab run should be rotated.
1921      * @param tabPlacement the placement (left, right, bottom, top) of the tab
1922      * @return whether or not the tab run should be rotated
1923      */
1924     protected boolean shouldRotateTabRuns(int tabPlacement) {
1925         return true;
1926     }
1927 
1928     /**
1929      * Returns the icon for a tab.
1930      * @param tabIndex the index of the tab
1931      * @return the icon for a tab
1932      */
1933     protected Icon getIconForTab(int tabIndex) {
1934         return (!tabPane.isEnabled() || !tabPane.isEnabledAt(tabIndex))?
1935                           tabPane.getDisabledIconAt(tabIndex) : tabPane.getIconAt(tabIndex);
1936     }
1937 
1938     /**
1939      * Returns the text View object required to render stylized text (HTML) for
1940      * the specified tab or null if no specialized text rendering is needed
1941      * for this tab. This is provided to support html rendering inside tabs.
1942      *
1943      * @param tabIndex the index of the tab
1944      * @return the text view to render the tab's text or null if no
1945      *         specialized rendering is required
1946      *
1947      * @since 1.4
1948      */
1949     protected View getTextViewForTab(int tabIndex) {
1950         if (htmlViews != null) {
1951             return htmlViews.elementAt(tabIndex);
1952         }
1953         return null;
1954     }
1955 
1956     /**
1957      * Calculates the tab height.
1958      * @param tabPlacement  the placement (left, right, bottom, top) of the tab
1959      * @param tabIndex      the index of the tab with respect to other tabs
1960      * @param fontHeight    the font height
1961      * @return the tab height
1962      */
1963     protected int calculateTabHeight(int tabPlacement, int tabIndex, int fontHeight) {
1964         int height = 0;
1965         Component c = tabPane.getTabComponentAt(tabIndex);
1966         if (c != null) {
1967             height = c.getPreferredSize().height;
1968         } else {
1969             View v = getTextViewForTab(tabIndex);
1970             if (v != null) {
1971                 // html
1972                 height += (int) v.getPreferredSpan(View.Y_AXIS);
1973             } else {
1974                 // plain text
1975                 height += fontHeight;
1976             }
1977             Icon icon = getIconForTab(tabIndex);
1978 
1979             if (icon != null) {
1980                 height = Math.max(height, icon.getIconHeight());
1981             }
1982         }
1983         Insets tabInsets = getTabInsets(tabPlacement, tabIndex);
1984         height += tabInsets.top + tabInsets.bottom + 2;
1985         return height;
1986     }
1987 
1988     /**
1989      * Calculates the maximum tab height.
1990      * @param tabPlacement  the placement (left, right, bottom, top) of the tab
1991      * @return the maximum tab height
1992      */
1993     protected int calculateMaxTabHeight(int tabPlacement) {
1994         FontMetrics metrics = getFontMetrics();
1995         int tabCount = tabPane.getTabCount();
1996         int result = 0;
1997         int fontHeight = metrics.getHeight();
1998         for(int i = 0; i < tabCount; i++) {
1999             result = Math.max(calculateTabHeight(tabPlacement, i, fontHeight), result);
2000         }
2001         return result;
2002     }
2003 
2004     /**
2005      * Calculates the tab width.
2006      * @param tabPlacement  the placement (left, right, bottom, top) of the tab
2007      * @param tabIndex      the index of the tab with respect to other tabs
2008      * @param metrics       the font metrics
2009      * @return the tab width
2010      */
2011     protected int calculateTabWidth(int tabPlacement, int tabIndex, FontMetrics metrics) {
2012         Insets tabInsets = getTabInsets(tabPlacement, tabIndex);
2013         int width = tabInsets.left + tabInsets.right + 3;
2014         Component tabComponent = tabPane.getTabComponentAt(tabIndex);
2015         if (tabComponent != null) {
2016             width += tabComponent.getPreferredSize().width;
2017         } else {
2018             Icon icon = getIconForTab(tabIndex);
2019             if (icon != null) {
2020                 width += icon.getIconWidth() + textIconGap;
2021             }
2022             View v = getTextViewForTab(tabIndex);
2023             if (v != null) {
2024                 // html
2025                 width += (int) v.getPreferredSpan(View.X_AXIS);
2026             } else {
2027                 // plain text
2028                 String title = tabPane.getTitleAt(tabIndex);
2029                 width += SwingUtilities2.stringWidth(tabPane, metrics, title);
2030             }
2031         }
2032         return width;
2033     }
2034 
2035     /**
2036      * Calculates the maximum tab width.
2037      * @param tabPlacement  the placement (left, right, bottom, top) of the tab
2038      * @return the maximum tab width
2039      */
2040     protected int calculateMaxTabWidth(int tabPlacement) {
2041         FontMetrics metrics = getFontMetrics();
2042         int tabCount = tabPane.getTabCount();
2043         int result = 0;
2044         for(int i = 0; i < tabCount; i++) {
2045             result = Math.max(calculateTabWidth(tabPlacement, i, metrics), result);
2046         }
2047         return result;
2048     }
2049 
2050     /**
2051      * Calculates the tab area height.
2052      * @param tabPlacement  the placement (left, right, bottom, top) of the tab
2053      * @param horizRunCount horizontal run count
2054      * @param maxTabHeight maximum tab height
2055      * @return the tab area height
2056      */
2057     protected int calculateTabAreaHeight(int tabPlacement, int horizRunCount, int maxTabHeight) {
2058         Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
2059         int tabRunOverlay = getTabRunOverlay(tabPlacement);
2060         return (horizRunCount > 0?
2061                 horizRunCount * (maxTabHeight-tabRunOverlay) + tabRunOverlay +
2062                 tabAreaInsets.top + tabAreaInsets.bottom :
2063                 0);
2064     }
2065 
2066     /**
2067      * Calculates the tab area width.
2068      * @param tabPlacement  the placement (left, right, bottom, top) of the tab
2069      * @param vertRunCount vertical run count
2070      * @param maxTabWidth maximum tab width
2071      * @return the tab area width
2072      */
2073     protected int calculateTabAreaWidth(int tabPlacement, int vertRunCount, int maxTabWidth) {
2074         Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
2075         int tabRunOverlay = getTabRunOverlay(tabPlacement);
2076         return (vertRunCount > 0?
2077                 vertRunCount * (maxTabWidth-tabRunOverlay) + tabRunOverlay +
2078                 tabAreaInsets.left + tabAreaInsets.right :
2079                 0);
2080     }
2081 
2082     /**
2083      * Returns the tab insets.
2084      * @param tabPlacement  the placement (left, right, bottom, top) of the tab
2085      * @param tabIndex the tab index
2086      * @return the tab insets
2087      */
2088     protected Insets getTabInsets(int tabPlacement, int tabIndex) {
2089         return tabInsets;
2090     }
2091 
2092     /**
2093      * Returns the selected tab pad insets.
2094      * @param tabPlacement  the placement (left, right, bottom, top) of the tab
2095      * @return the selected tab pad insets
2096      */
2097     protected Insets getSelectedTabPadInsets(int tabPlacement) {
2098         rotateInsets(selectedTabPadInsets, currentPadInsets, tabPlacement);
2099         return currentPadInsets;
2100     }
2101 
2102     /**
2103      * Returns the tab area insets.
2104      * @param tabPlacement  the placement (left, right, bottom, top) of the tab
2105      * @return the pad area insets
2106      */
2107     protected Insets getTabAreaInsets(int tabPlacement) {
2108         rotateInsets(tabAreaInsets, currentTabAreaInsets, tabPlacement);
2109         return currentTabAreaInsets;
2110     }
2111 
2112     /**
2113      * Returns the content border insets.
2114      * @param tabPlacement  the placement (left, right, bottom, top) of the tab
2115      * @return the content border insets
2116      */
2117     protected Insets getContentBorderInsets(int tabPlacement) {
2118         return contentBorderInsets;
2119     }
2120 
2121     /**
2122      * Returns the font metrics.
2123      * @return the font metrics
2124      */
2125     protected FontMetrics getFontMetrics() {
2126         Font font = tabPane.getFont();
2127         return tabPane.getFontMetrics(font);
2128     }
2129 
2130 
2131 // Tab Navigation methods
2132 
2133     /**
2134      * Navigate the selected tab.
2135      * @param direction the direction
2136      */
2137     protected void navigateSelectedTab(int direction) {
2138         int tabPlacement = tabPane.getTabPlacement();
2139         int current = DefaultLookup.getBoolean(tabPane, this,
2140                              "TabbedPane.selectionFollowsFocus", true) ?
2141                              tabPane.getSelectedIndex() : getFocusIndex();
2142         int tabCount = tabPane.getTabCount();
2143         boolean leftToRight = BasicGraphicsUtils.isLeftToRight(tabPane);
2144 
2145         // If we have no tabs then don't navigate.
2146         if (tabCount <= 0) {
2147             return;
2148         }
2149 
2150         int offset;
2151         switch(tabPlacement) {
2152           case LEFT:
2153           case RIGHT:
2154               switch(direction) {
2155                  case NEXT:
2156                      selectNextTab(current);


2195                     break;
2196                 case EAST:
2197                     if (leftToRight) {
2198                         selectNextTabInRun(current);
2199                     } else {
2200                         selectPreviousTabInRun(current);
2201                     }
2202                     break;
2203                 case WEST:
2204                     if (leftToRight) {
2205                         selectPreviousTabInRun(current);
2206                     } else {
2207                         selectNextTabInRun(current);
2208                     }
2209                     break;
2210                 default:
2211               }
2212         }
2213     }
2214 
2215     /**
2216      * Select the next tab in the run.
2217      * @param current the current tab
2218      */
2219     protected void selectNextTabInRun(int current) {
2220         int tabCount = tabPane.getTabCount();
2221         int tabIndex = getNextTabIndexInRun(tabCount, current);
2222 
2223         while(tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
2224             tabIndex = getNextTabIndexInRun(tabCount, tabIndex);
2225         }
2226         navigateTo(tabIndex);
2227     }
2228 
2229     /**
2230      * Select the previous tab in the run.
2231      * @param current the current tab
2232      */
2233     protected void selectPreviousTabInRun(int current) {
2234         int tabCount = tabPane.getTabCount();
2235         int tabIndex = getPreviousTabIndexInRun(tabCount, current);
2236 
2237         while(tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
2238             tabIndex = getPreviousTabIndexInRun(tabCount, tabIndex);
2239         }
2240         navigateTo(tabIndex);
2241     }
2242 
2243     /**
2244      * Select the next tab.
2245      * @param current the current tab
2246      */
2247     protected void selectNextTab(int current) {
2248         int tabIndex = getNextTabIndex(current);
2249 
2250         while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
2251             tabIndex = getNextTabIndex(tabIndex);
2252         }
2253         navigateTo(tabIndex);
2254     }
2255 
2256     /**
2257      * Select the previous tab.
2258      * @param current the current tab
2259      */
2260     protected void selectPreviousTab(int current) {
2261         int tabIndex = getPreviousTabIndex(current);
2262 
2263         while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
2264             tabIndex = getPreviousTabIndex(tabIndex);
2265         }
2266         navigateTo(tabIndex);
2267     }
2268 
2269     /**
2270      * Selects an adjacent run of tabs.
2271      * @param tabPlacement  the placement (left, right, bottom, top) of the tab
2272      * @param tabIndex      the index of the tab with respect to other tabs
2273      * @param offset        selection offset
2274      */
2275     protected void selectAdjacentRunTab(int tabPlacement,
2276                                         int tabIndex, int offset) {
2277         if ( runCount < 2 ) {
2278             return;
2279         }
2280         int newIndex;
2281         Rectangle r = rects[tabIndex];
2282         switch(tabPlacement) {
2283           case LEFT:
2284           case RIGHT:
2285               newIndex = tabForCoordinate(tabPane, r.x + r.width/2 + offset,
2286                                        r.y + r.height/2);
2287               break;
2288           case BOTTOM:
2289           case TOP:
2290           default:
2291               newIndex = tabForCoordinate(tabPane, r.x + r.width/2,
2292                                        r.y + r.height/2 + offset);
2293         }
2294         if (newIndex != -1) {


2333 
2334     /**
2335      * Makes sure the focusIndex is valid.
2336      */
2337     private void validateFocusIndex() {
2338         if (focusIndex >= tabPane.getTabCount()) {
2339             setFocusIndex(tabPane.getSelectedIndex(), false);
2340         }
2341     }
2342 
2343     /**
2344      * Returns the index of the tab that has focus.
2345      *
2346      * @return index of tab that has focus
2347      * @since 1.5
2348      */
2349     protected int getFocusIndex() {
2350         return focusIndex;
2351     }
2352 
2353     /**
2354      * Returns the tab run offset.
2355      * @param tabPlacement  the placement (left, right, bottom, top) of the tab
2356      * @param tabCount the tab count
2357      * @param tabIndex      the index of the tab with respect to other tabs
2358      * @param forward forward or not
2359      * @return the tab run offset
2360      */
2361     protected int getTabRunOffset(int tabPlacement, int tabCount,
2362                                   int tabIndex, boolean forward) {
2363         int run = getRunForTab(tabCount, tabIndex);
2364         int offset;
2365         switch(tabPlacement) {
2366           case LEFT: {
2367               if (run == 0) {
2368                   offset = (forward?
2369                             -(calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth) :
2370                             -maxTabWidth);
2371 
2372               } else if (run == runCount - 1) {
2373                   offset = (forward?
2374                             maxTabWidth :
2375                             calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth);
2376               } else {
2377                   offset = (forward? maxTabWidth : -maxTabWidth);
2378               }
2379               break;
2380           }


2407               break;
2408           }
2409           case TOP:
2410           default: {
2411               if (run == 0) {
2412                   offset = (forward?
2413                             -(calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight) :
2414                             -maxTabHeight);
2415               } else if (run == runCount - 1) {
2416                   offset = (forward?
2417                             maxTabHeight :
2418                             calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight);
2419               } else {
2420                   offset = (forward? maxTabHeight : -maxTabHeight);
2421               }
2422           }
2423         }
2424         return offset;
2425     }
2426 
2427     /**
2428      * Returns the previous tab index.
2429      * @param base the base
2430      * @return the previous tab index
2431      */
2432     protected int getPreviousTabIndex(int base) {
2433         int tabIndex = (base - 1 >= 0? base - 1 : tabPane.getTabCount() - 1);
2434         return (tabIndex >= 0? tabIndex : 0);
2435     }
2436 
2437     /**
2438      * Returns the next tab index.
2439      * @param base the base
2440      * @return the next tab index
2441      */
2442     protected int getNextTabIndex(int base) {
2443         return (base+1)%tabPane.getTabCount();
2444     }
2445 
2446     /**
2447      * Returns the next tab index in the run.
2448      * @param tabCount the tab count
2449      * @param base the base
2450      * @return the next tab index in the run
2451      */
2452     protected int getNextTabIndexInRun(int tabCount, int base) {
2453         if (runCount < 2) {
2454             return getNextTabIndex(base);
2455         }
2456         int currentRun = getRunForTab(tabCount, base);
2457         int next = getNextTabIndex(base);
2458         if (next == tabRuns[getNextTabRun(currentRun)]) {
2459             return tabRuns[currentRun];
2460         }
2461         return next;
2462     }
2463 
2464     /**
2465      * Returns the previous tab index in the run.
2466      * @param tabCount the tab count
2467      * @param base the base
2468      * @return the previous tab index in the run
2469      */
2470     protected int getPreviousTabIndexInRun(int tabCount, int base) {
2471         if (runCount < 2) {
2472             return getPreviousTabIndex(base);
2473         }
2474         int currentRun = getRunForTab(tabCount, base);
2475         if (base == tabRuns[currentRun]) {
2476             int previous = tabRuns[getNextTabRun(currentRun)]-1;
2477             return (previous != -1? previous : tabCount-1);
2478         }
2479         return getPreviousTabIndex(base);
2480     }
2481 
2482     /**
2483      * Returns the previous tab run.
2484      * @param baseRun the base run
2485      * @return the previous tab run
2486      */
2487     protected int getPreviousTabRun(int baseRun) {
2488         int runIndex = (baseRun - 1 >= 0? baseRun - 1 : runCount - 1);
2489         return (runIndex >= 0? runIndex : 0);
2490     }
2491 
2492     /**
2493      * Returns the next tab run.
2494      * @param baseRun the base run
2495      * @return the next tab run
2496      */
2497     protected int getNextTabRun(int baseRun) {
2498         return (baseRun+1)%runCount;
2499     }
2500 
2501     /**
2502      * Rotates the insets.
2503      * @param topInsets the top insets
2504      * @param targetInsets the target insets
2505      * @param targetPlacement the target placement
2506      */
2507     protected static void rotateInsets(Insets topInsets, Insets targetInsets, int targetPlacement) {
2508 
2509         switch(targetPlacement) {
2510           case LEFT:
2511               targetInsets.top = topInsets.left;
2512               targetInsets.left = topInsets.top;
2513               targetInsets.bottom = topInsets.right;
2514               targetInsets.right = topInsets.bottom;
2515               break;
2516           case BOTTOM:
2517               targetInsets.top = topInsets.bottom;
2518               targetInsets.left = topInsets.left;
2519               targetInsets.bottom = topInsets.top;
2520               targetInsets.right = topInsets.right;
2521               break;
2522           case RIGHT:
2523               targetInsets.top = topInsets.left;
2524               targetInsets.left = topInsets.bottom;
2525               targetInsets.bottom = topInsets.right;
2526               targetInsets.right = topInsets.top;


2645     }
2646 
2647     /**
2648      * This class should be treated as a &quot;protected&quot; inner class.
2649      * Instantiate it only within subclasses of BasicTabbedPaneUI.
2650      */
2651     public class TabbedPaneLayout implements LayoutManager {
2652 
2653         public void addLayoutComponent(String name, Component comp) {}
2654 
2655         public void removeLayoutComponent(Component comp) {}
2656 
2657         public Dimension preferredLayoutSize(Container parent) {
2658             return calculateSize(false);
2659         }
2660 
2661         public Dimension minimumLayoutSize(Container parent) {
2662             return calculateSize(true);
2663         }
2664 
2665         /**
2666          * Returns the calculated size.
2667          * @param minimum use the minimum size or preferred size 
2668          * @return the calculated size
2669          */
2670         protected Dimension calculateSize(boolean minimum) {
2671             int tabPlacement = tabPane.getTabPlacement();
2672             Insets insets = tabPane.getInsets();
2673             Insets contentInsets = getContentBorderInsets(tabPlacement);
2674             Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
2675 
2676             Dimension zeroSize = new Dimension(0,0);
2677             int height = 0;
2678             int width = 0;
2679             int cWidth = 0;
2680             int cHeight = 0;
2681 
2682             // Determine minimum size required to display largest
2683             // child in each dimension
2684             //
2685             for (int i = 0; i < tabPane.getTabCount(); i++) {
2686                 Component component = tabPane.getComponentAt(i);
2687                 if (component != null) {
2688                     Dimension size = minimum ? component.getMinimumSize() :
2689                                 component.getPreferredSize();


2704             //
2705             switch(tabPlacement) {
2706               case LEFT:
2707               case RIGHT:
2708                   height = Math.max(height, calculateMaxTabHeight(tabPlacement));
2709                   tabExtent = preferredTabAreaWidth(tabPlacement, height - tabAreaInsets.top - tabAreaInsets.bottom);
2710                   width += tabExtent;
2711                   break;
2712               case TOP:
2713               case BOTTOM:
2714               default:
2715                   width = Math.max(width, calculateMaxTabWidth(tabPlacement));
2716                   tabExtent = preferredTabAreaHeight(tabPlacement, width - tabAreaInsets.left - tabAreaInsets.right);
2717                   height += tabExtent;
2718             }
2719             return new Dimension(width + insets.left + insets.right + contentInsets.left + contentInsets.right,
2720                              height + insets.bottom + insets.top + contentInsets.top + contentInsets.bottom);
2721 
2722         }
2723 
2724         /**
2725          * Returns the preferred tab area height.
2726          * @param tabPlacement the tab placement
2727          * @param width the width
2728          * @return the preferred tab area height
2729          */
2730         protected int preferredTabAreaHeight(int tabPlacement, int width) {
2731             FontMetrics metrics = getFontMetrics();
2732             int tabCount = tabPane.getTabCount();
2733             int total = 0;
2734             if (tabCount > 0) {
2735                 int rows = 1;
2736                 int x = 0;
2737 
2738                 int maxTabHeight = calculateMaxTabHeight(tabPlacement);
2739 
2740                 for (int i = 0; i < tabCount; i++) {
2741                     int tabWidth = calculateTabWidth(tabPlacement, i, metrics);
2742 
2743                     if (x != 0 && x + tabWidth > width) {
2744                         rows++;
2745                         x = 0;
2746                     }
2747                     x += tabWidth;
2748                 }
2749                 total = calculateTabAreaHeight(tabPlacement, rows, maxTabHeight);
2750             }
2751             return total;
2752         }
2753 
2754         /**
2755          * Returns the preferred tab area width.
2756          * @param tabPlacement the tab placement
2757          * @param height the height
2758          * @return the preferred tab area widty
2759          */
2760         protected int preferredTabAreaWidth(int tabPlacement, int height) {
2761             FontMetrics metrics = getFontMetrics();
2762             int tabCount = tabPane.getTabCount();
2763             int total = 0;
2764             if (tabCount > 0) {
2765                 int columns = 1;
2766                 int y = 0;
2767                 int fontHeight = metrics.getHeight();
2768 
2769                 maxTabWidth = calculateMaxTabWidth(tabPlacement);
2770 
2771                 for (int i = 0; i < tabCount; i++) {
2772                     int tabHeight = calculateTabHeight(tabPlacement, i, fontHeight);
2773 
2774                     if (y != 0 && y + tabHeight > height) {
2775                         columns++;
2776                         y = 0;
2777                     }
2778                     y += tabHeight;
2779                 }
2780                 total = calculateTabAreaWidth(tabPlacement, columns, maxTabWidth);
2781             }
2782             return total;
2783         }
2784 
2785         /** {@inheritDoc} */
2786         @SuppressWarnings("deprecation")
2787         public void layoutContainer(Container parent) {
2788             /* Some of the code in this method deals with changing the
2789             * visibility of components to hide and show the contents for the
2790             * selected tab. This is older code that has since been duplicated
2791             * in JTabbedPane.fireStateChanged(), so as to allow visibility
2792             * changes to happen sooner (see the note there). This code remains
2793             * for backward compatibility as there are some cases, such as
2794             * subclasses that don't fireStateChanged() where it may be used.
2795             * Any changes here need to be kept in synch with
2796             * JTabbedPane.fireStateChanged().
2797             */
2798 
2799             setRolloverTab(-1);
2800 
2801             int tabPlacement = tabPane.getTabPlacement();
2802             Insets insets = tabPane.getInsets();
2803             int selectedIndex = tabPane.getSelectedIndex();
2804             Component visibleComponent = getVisibleComponent();
2805 


2888                         int tabContainerY = 0;
2889                         if(tabPlacement == BOTTOM) {
2890                             tabContainerY = bounds.height - tabContainerHeight;
2891                         } else if(tabPlacement == RIGHT) {
2892                             tabContainerX = bounds.width - tabContainerWidth;
2893                         }
2894                         child.setBounds(tabContainerX, tabContainerY, tabContainerWidth, tabContainerHeight);
2895                     } else {
2896                         child.setBounds(cx, cy, cw, ch);
2897                     }
2898                 }
2899             }
2900             layoutTabComponents();
2901             if(shouldChangeFocus) {
2902                 if(!requestFocusForVisibleComponent()) {
2903                     tabPane.requestFocus();
2904                 }
2905             }
2906         }
2907 
2908         /**
2909          * Calculates the layout info.
2910          */
2911         public void calculateLayoutInfo() {
2912             int tabCount = tabPane.getTabCount();
2913             assureRectsCreated(tabCount);
2914             calculateTabRects(tabPane.getTabPlacement(), tabCount);
2915             isRunsDirty = false;
2916         }
2917 
2918         private void layoutTabComponents() {
2919             if (tabContainer == null) {
2920                 return;
2921             }
2922             Rectangle rect = new Rectangle();
2923             Point delta = new Point(-tabContainer.getX(), -tabContainer.getY());
2924             if (scrollableTabLayoutEnabled()) {
2925                 translatePointToTabPanel(0, 0, delta);
2926             }
2927             for (int i = 0; i < tabPane.getTabCount(); i++) {
2928                 Component c = tabPane.getTabComponentAt(i);
2929                 if (c == null) {
2930                     continue;
2931                 }
2932                 getTabBounds(i, rect);
2933                 Dimension preferredSize = c.getPreferredSize();
2934                 Insets insets = getTabInsets(tabPane.getTabPlacement(), i);
2935                 int outerX = rect.x + insets.left + delta.x;
2936                 int outerY = rect.y + insets.top + delta.y;
2937                 int outerWidth = rect.width - insets.left - insets.right;
2938                 int outerHeight = rect.height - insets.top - insets.bottom;
2939                 //centralize component
2940                 int x = outerX + (outerWidth - preferredSize.width) / 2;
2941                 int y = outerY + (outerHeight - preferredSize.height) / 2;
2942                 int tabPlacement = tabPane.getTabPlacement();
2943                 boolean isSeleceted = i == tabPane.getSelectedIndex();
2944                 c.setBounds(x + getTabLabelShiftX(tabPlacement, i, isSeleceted),
2945                             y + getTabLabelShiftY(tabPlacement, i, isSeleceted),
2946                         preferredSize.width, preferredSize.height);
2947             }
2948         }
2949 
2950         /**
2951          * Calculate the tab rectangles.
2952          * @param tabPlacement the tab placement
2953          * @param tabCount the tab count
2954          */
2955         protected void calculateTabRects(int tabPlacement, int tabCount) {
2956             FontMetrics metrics = getFontMetrics();
2957             Dimension size = tabPane.getSize();
2958             Insets insets = tabPane.getInsets();
2959             Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
2960             int fontHeight = metrics.getHeight();
2961             int selectedIndex = tabPane.getSelectedIndex();
2962             int tabRunOverlay;
2963             int i, j;
2964             int x, y;
2965             int returnAt;
2966             boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
2967             boolean leftToRight = BasicGraphicsUtils.isLeftToRight(tabPane);
2968 
2969             //
2970             // Calculate bounds within which a tab run must fit
2971             //
2972             switch(tabPlacement) {
2973               case LEFT:
2974                   maxTabWidth = calculateMaxTabWidth(tabPlacement);


3120                         x += (maxTabWidth - tabRunOverlay);
3121                     }
3122                 }
3123             }
3124 
3125             // Pad the selected tab so that it appears raised in front
3126             padSelectedTab(tabPlacement, selectedIndex);
3127 
3128             // if right to left and tab placement on the top or
3129             // the bottom, flip x positions and adjust by widths
3130             if (!leftToRight && !verticalTabRuns) {
3131                 int rightMargin = size.width
3132                                   - (insets.right + tabAreaInsets.right);
3133                 for (i = 0; i < tabCount; i++) {
3134                     rects[i].x = rightMargin - rects[i].x - rects[i].width;
3135                 }
3136             }
3137         }
3138 
3139 
3140         /**
3141          * Rotates the run-index array so that the selected run is run[0].
3142          * @param tabPlacement the tab placement
3143          * @param selectedRun the selected run
3144          */
3145         protected void rotateTabRuns(int tabPlacement, int selectedRun) {
3146             for (int i = 0; i < selectedRun; i++) {
3147                 int save = tabRuns[0];
3148                 for (int j = 1; j < runCount; j++) {
3149                     tabRuns[j - 1] = tabRuns[j];
3150                 }
3151                 tabRuns[runCount-1] = save;
3152             }
3153         }
3154 
3155         /**
3156          * Normalizes the tab runs.
3157          * @param tabPlacement the tab placement
3158          * @param tabCount the tab count
3159          * @param start the start
3160          * @param max the max
3161          */
3162         protected void normalizeTabRuns(int tabPlacement, int tabCount,
3163                                      int start, int max) {
3164             boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
3165             int run = runCount - 1;
3166             boolean keepAdjusting = true;
3167             double weight = 1.25;
3168 
3169             // At this point the tab runs are packed to fit as many
3170             // tabs as possible, which can leave the last run with a lot
3171             // of extra space (resulting in very fat tabs on the last run).
3172             // So we'll attempt to distribute this extra space more evenly
3173             // across the runs in order to make the runs look more consistent.
3174             //
3175             // Starting with the last run, determine whether the last tab in
3176             // the previous run would fit (generously) in this run; if so,
3177             // move tab to current run and shift tabs accordingly.  Cycle
3178             // through remaining runs using the same algorithm.
3179             //
3180             while (keepAdjusting) {
3181                 int last = lastTabInRun(tabCount, run);


3210                         }
3211                     }
3212 
3213                 } else if (run == runCount - 1) {
3214                     // no more room left in last run, so we're done!
3215                     keepAdjusting = false;
3216                 }
3217                 if (run - 1 > 0) {
3218                     // check previous run next...
3219                     run -= 1;
3220                 } else {
3221                     // check last run again...but require a higher ratio
3222                     // of extraspace-to-tabsize because we don't want to
3223                     // end up with too many tabs on the last run!
3224                     run = runCount - 1;
3225                     weight += .25;
3226                 }
3227             }
3228         }
3229 
3230         /**
3231          * Pads the tab run.
3232          * @param tabPlacement the tab placement
3233          * @param start the start
3234          * @param end the end
3235          * @param max the max
3236          */
3237         protected void padTabRun(int tabPlacement, int start, int end, int max) {
3238             Rectangle lastRect = rects[end];
3239             if (tabPlacement == TOP || tabPlacement == BOTTOM) {
3240                 int runWidth = (lastRect.x + lastRect.width) - rects[start].x;
3241                 int deltaWidth = max - (lastRect.x + lastRect.width);
3242                 float factor = (float)deltaWidth / (float)runWidth;
3243 
3244                 for (int j = start; j <= end; j++) {
3245                     Rectangle pastRect = rects[j];
3246                     if (j > start) {
3247                         pastRect.x = rects[j-1].x + rects[j-1].width;
3248                     }
3249                     pastRect.width += Math.round((float)pastRect.width * factor);
3250                 }
3251                 lastRect.width = max - lastRect.x;
3252             } else {
3253                 int runHeight = (lastRect.y + lastRect.height) - rects[start].y;
3254                 int deltaHeight = max - (lastRect.y + lastRect.height);
3255                 float factor = (float)deltaHeight / (float)runHeight;
3256 
3257                 for (int j = start; j <= end; j++) {
3258                     Rectangle pastRect = rects[j];
3259                     if (j > start) {
3260                         pastRect.y = rects[j-1].y + rects[j-1].height;
3261                     }
3262                     pastRect.height += Math.round((float)pastRect.height * factor);
3263                 }
3264                 lastRect.height = max - lastRect.y;
3265             }
3266         }
3267 
3268         /**
3269          * Pads selected tab.
3270          * @param tabPlacement the tab placement
3271          * @param selectedIndex the selected index
3272          */
3273         protected void padSelectedTab(int tabPlacement, int selectedIndex) {
3274 
3275             if (selectedIndex >= 0) {
3276                 Rectangle selRect = rects[selectedIndex];
3277                 Insets padInsets = getSelectedTabPadInsets(tabPlacement);
3278                 selRect.x -= padInsets.left;
3279                 selRect.width += (padInsets.left + padInsets.right);
3280                 selRect.y -= padInsets.top;
3281                 selRect.height += (padInsets.top + padInsets.bottom);
3282 
3283                 if (!scrollableTabLayoutEnabled()) { // WRAP_TAB_LAYOUT
3284                     // do not expand selected tab more then necessary
3285                     Dimension size = tabPane.getSize();
3286                     Insets insets = tabPane.getInsets();
3287 
3288                     if ((tabPlacement == LEFT) || (tabPlacement == RIGHT)) {
3289                         int top = insets.top - selRect.y;
3290                         if (top > 0) {
3291                             selRect.y += top;
3292                             selRect.height -= top;


< prev index next >