39 import java.util.Vector;
40 import java.util.Hashtable;
41
42 import sun.swing.DefaultLookup;
43 import sun.swing.UIAction;
44
45 /**
46 * A Basic L&F implementation of TabbedPaneUI.
47 *
48 * @author Amy Fowler
49 * @author Philip Milne
50 * @author Steve Wilson
51 * @author Tom Santos
52 * @author Dave Moore
53 */
54 public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants {
55
56
57 // Instance variables initialized at installation
58
59 protected JTabbedPane tabPane;
60
61 protected Color highlight;
62 protected Color lightHighlight;
63 protected Color shadow;
64 protected Color darkShadow;
65 protected Color focus;
66 private Color selectedColor;
67
68 protected int textIconGap;
69
70 protected int tabRunOverlay;
71
72 protected Insets tabInsets;
73 protected Insets selectedTabPadInsets;
74 protected Insets tabAreaInsets;
75 protected Insets contentBorderInsets;
76 private boolean tabsOverlapBorder;
77 private boolean tabsOpaque = true;
78 private boolean contentOpaque = true;
79
80 /**
81 * As of Java 2 platform v1.3 this previously undocumented field is no
82 * longer used.
83 * Key bindings are now defined by the LookAndFeel, please refer to
84 * the key bindings specification for further details.
85 *
86 * @deprecated As of Java 2 platform v1.3.
87 */
88 @Deprecated
89 protected KeyStroke upKey;
90 /**
91 * As of Java 2 platform v1.3 this previously undocumented field is no
92 * longer used.
93 * Key bindings are now defined by the LookAndFeel, please refer to
94 * the key bindings specification for further details.
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 "protected" 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&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 "protected" 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;
|