1. /*
  2. * @(#)BasicTabbedPaneUI.java 1.93 00/02/02
  3. *
  4. * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package javax.swing.plaf.basic;
  11. import javax.swing.*;
  12. import javax.swing.event.*;
  13. import javax.swing.plaf.*;
  14. import javax.swing.text.View;
  15. import java.awt.*;
  16. import java.awt.event.*;
  17. import java.beans.PropertyChangeListener;
  18. import java.beans.PropertyChangeEvent;
  19. import java.util.Vector;
  20. /**
  21. * A Basic L&F implementation of TabbedPaneUI.
  22. *
  23. * @version 1.87 06/08/99
  24. * @author Amy Fowler
  25. * @author Philip Milne
  26. * @author Steve Wilson
  27. * @author Tom Santos
  28. * @author Dave Moore
  29. */
  30. public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants {
  31. // Instance variables initialized at installation
  32. protected JTabbedPane tabPane;
  33. protected Color highlight;
  34. protected Color lightHighlight;
  35. protected Color shadow;
  36. protected Color darkShadow;
  37. protected Color focus;
  38. protected int textIconGap;
  39. protected int tabRunOverlay;
  40. protected Insets tabInsets;
  41. protected Insets selectedTabPadInsets;
  42. protected Insets tabAreaInsets;
  43. protected Insets contentBorderInsets;
  44. /**
  45. * As of Java 2 platform v1.3 this previously undocumented field is no
  46. * longer used.
  47. * Key bindings are now defined by the LookAndFeel, please refer to
  48. * the key bindings specification for further details.
  49. *
  50. * @deprecated As of Java 2 platform v1.3.
  51. */
  52. protected KeyStroke upKey;
  53. /**
  54. * As of Java 2 platform v1.3 this previously undocumented field is no
  55. * longer used.
  56. * Key bindings are now defined by the LookAndFeel, please refer to
  57. * the key bindings specification for further details.
  58. *
  59. * @deprecated As of Java 2 platform v1.3.
  60. */
  61. protected KeyStroke downKey;
  62. /**
  63. * As of Java 2 platform v1.3 this previously undocumented field is no
  64. * longer used.
  65. * Key bindings are now defined by the LookAndFeel, please refer to
  66. * the key bindings specification for further details.
  67. *
  68. * @deprecated As of Java 2 platform v1.3.
  69. */
  70. protected KeyStroke leftKey;
  71. /**
  72. * As of Java 2 platform v1.3 this previously undocumented field is no
  73. * longer used.
  74. * Key bindings are now defined by the LookAndFeel, please refer to
  75. * the key bindings specification for further details.
  76. *
  77. * @deprecated As of Java 2 platform v1.3.
  78. */
  79. protected KeyStroke rightKey;
  80. // Transient variables (recalculated each time TabbedPane is layed out)
  81. protected int tabRuns[] = new int[10];
  82. protected int runCount = 0;
  83. protected int selectedRun = -1;
  84. protected Rectangle rects[] = new Rectangle[0];
  85. protected int maxTabHeight;
  86. protected int maxTabWidth;
  87. // Listeners
  88. protected ChangeListener tabChangeListener;
  89. protected PropertyChangeListener propertyChangeListener;
  90. protected MouseListener mouseListener;
  91. protected FocusListener focusListener;
  92. // PENDING(api): See comment for ContainerHandler
  93. private ContainerListener containerListener;
  94. // Private instance data
  95. private Insets currentPadInsets = new Insets(0,0,0,0);
  96. private Insets currentTabAreaInsets = new Insets(0,0,0,0);
  97. private Component visibleComponent;
  98. // PENDING(api): See comment for ContainerHandler
  99. private Vector htmlViews;
  100. // UI creation
  101. public static ComponentUI createUI(JComponent c) {
  102. return new BasicTabbedPaneUI();
  103. }
  104. // UI Installation/De-installation
  105. public void installUI(JComponent c) {
  106. this.tabPane = (JTabbedPane)c;
  107. c.setLayout(createLayoutManager());
  108. installDefaults();
  109. installListeners();
  110. installKeyboardActions();
  111. }
  112. public void uninstallUI(JComponent c) {
  113. uninstallKeyboardActions();
  114. uninstallListeners();
  115. uninstallDefaults();
  116. c.setLayout(null);
  117. this.tabPane = null;
  118. }
  119. protected LayoutManager createLayoutManager() {
  120. return new TabbedPaneLayout();
  121. }
  122. protected void installDefaults() {
  123. LookAndFeel.installColorsAndFont(tabPane, "TabbedPane.background",
  124. "TabbedPane.foreground", "TabbedPane.font");
  125. highlight = UIManager.getColor("TabbedPane.highlight");
  126. lightHighlight = UIManager.getColor("TabbedPane.lightHighlight");
  127. shadow = UIManager.getColor("TabbedPane.shadow");
  128. darkShadow = UIManager.getColor("TabbedPane.darkShadow");
  129. focus = UIManager.getColor("TabbedPane.focus");
  130. textIconGap = UIManager.getInt("TabbedPane.textIconGap");
  131. tabInsets = UIManager.getInsets("TabbedPane.tabInsets");
  132. selectedTabPadInsets = UIManager.getInsets("TabbedPane.selectedTabPadInsets");
  133. tabAreaInsets = UIManager.getInsets("TabbedPane.tabAreaInsets");
  134. contentBorderInsets = UIManager.getInsets("TabbedPane.contentBorderInsets");
  135. tabRunOverlay = UIManager.getInt("TabbedPane.tabRunOverlay");
  136. }
  137. protected void uninstallDefaults() {
  138. highlight = null;
  139. lightHighlight = null;
  140. shadow = null;
  141. darkShadow = null;
  142. focus = null;
  143. tabInsets = null;
  144. selectedTabPadInsets = null;
  145. tabAreaInsets = null;
  146. contentBorderInsets = null;
  147. }
  148. protected void installListeners() {
  149. if ((propertyChangeListener = createPropertyChangeListener()) != null) {
  150. tabPane.addPropertyChangeListener(propertyChangeListener);
  151. }
  152. if ((tabChangeListener = createChangeListener()) != null) {
  153. tabPane.addChangeListener(tabChangeListener);
  154. }
  155. if ((mouseListener = createMouseListener()) != null) {
  156. tabPane.addMouseListener(mouseListener);
  157. }
  158. if ((focusListener = createFocusListener()) != null) {
  159. tabPane.addFocusListener(focusListener);
  160. }
  161. // PENDING(api) : See comment for ContainerHandler
  162. if ((containerListener = new ContainerHandler()) != null) {
  163. tabPane.addContainerListener(containerListener);
  164. if (tabPane.getTabCount()>0) {
  165. htmlViews = createHTMLVector();
  166. }
  167. }
  168. }
  169. protected void uninstallListeners() {
  170. if (mouseListener != null) {
  171. tabPane.removeMouseListener(mouseListener);
  172. mouseListener = null;
  173. }
  174. if (focusListener != null) {
  175. tabPane.removeFocusListener(focusListener);
  176. focusListener = null;
  177. }
  178. // PENDING(api): See comment for ContainerHandler
  179. if (containerListener != null) {
  180. tabPane.removeContainerListener(containerListener);
  181. containerListener = null;
  182. if (htmlViews!=null) {
  183. htmlViews.removeAllElements();
  184. htmlViews = null;
  185. }
  186. }
  187. if (tabChangeListener != null) {
  188. tabPane.removeChangeListener(tabChangeListener);
  189. tabChangeListener = null;
  190. }
  191. if (propertyChangeListener != null) {
  192. tabPane.removePropertyChangeListener(propertyChangeListener);
  193. propertyChangeListener = null;
  194. }
  195. }
  196. protected MouseListener createMouseListener() {
  197. return new MouseHandler();
  198. }
  199. protected FocusListener createFocusListener() {
  200. return new FocusHandler();
  201. }
  202. protected ChangeListener createChangeListener() {
  203. return new TabSelectionHandler();
  204. }
  205. protected PropertyChangeListener createPropertyChangeListener() {
  206. return new PropertyChangeHandler();
  207. }
  208. protected void installKeyboardActions() {
  209. InputMap km = getInputMap(JComponent.
  210. WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  211. SwingUtilities.replaceUIInputMap(tabPane, JComponent.
  212. WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
  213. km);
  214. km = getInputMap(JComponent.WHEN_FOCUSED);
  215. SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, km);
  216. ActionMap am = getActionMap();
  217. SwingUtilities.replaceUIActionMap(tabPane, am);
  218. }
  219. InputMap getInputMap(int condition) {
  220. if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
  221. return (InputMap)UIManager.get("TabbedPane.ancestorInputMap");
  222. }
  223. else if (condition == JComponent.WHEN_FOCUSED) {
  224. return (InputMap)UIManager.get("TabbedPane.focusInputMap");
  225. }
  226. return null;
  227. }
  228. ActionMap getActionMap() {
  229. ActionMap map = (ActionMap)UIManager.get("TabbedPane.actionMap");
  230. if (map == null) {
  231. map = createActionMap();
  232. if (map != null) {
  233. UIManager.put("TabbedPane.actionMap", map);
  234. }
  235. }
  236. return map;
  237. }
  238. ActionMap createActionMap() {
  239. ActionMap map = new ActionMapUIResource();
  240. map.put("navigateRight", new RightAction());
  241. map.put("navigateLeft", new LeftAction());
  242. map.put("navigateUp", new UpAction());
  243. map.put("navigateDown", new DownAction());
  244. map.put("navigatePageUp", new PageUpAction());
  245. map.put("navigatePageDown", new PageDownAction());
  246. map.put("requestFocus", new RequestFocusAction());
  247. map.put("requestFocusForVisibleComponent",
  248. new RequestFocusForVisibleAction());
  249. return map;
  250. }
  251. protected void uninstallKeyboardActions() {
  252. SwingUtilities.replaceUIActionMap(tabPane, null);
  253. SwingUtilities.replaceUIInputMap(tabPane, JComponent.
  254. WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
  255. null);
  256. SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED,
  257. null);
  258. }
  259. // Geometry
  260. public Dimension getPreferredSize(JComponent c) {
  261. // Default to LayoutManager's preferredLayoutSize
  262. return null;
  263. }
  264. public Dimension getMinimumSize(JComponent c) {
  265. // Default to LayoutManager's minimumLayoutSize
  266. return null;
  267. }
  268. public Dimension getMaximumSize(JComponent c) {
  269. // Default to LayoutManager's maximumLayoutSize
  270. return null;
  271. }
  272. // UI Rendering
  273. public void paint(Graphics g, JComponent c) {
  274. int selectedIndex = tabPane.getSelectedIndex();
  275. int tabPlacement = tabPane.getTabPlacement();
  276. int tabCount = tabPane.getTabCount();
  277. ensureCurrentLayout();
  278. Rectangle iconRect = new Rectangle(),
  279. textRect = new Rectangle();
  280. Rectangle clipRect = g.getClipBounds();
  281. Insets insets = tabPane.getInsets();
  282. // Paint tabRuns of tabs from back to front
  283. for (int i = runCount - 1; i >= 0; i--) {
  284. int start = tabRuns[i];
  285. int next = tabRuns[(i == runCount - 1)? 0 : i + 1];
  286. int end = (next != 0? next - 1: tabCount - 1);
  287. for (int j = start; j <= end; j++) {
  288. if (rects[j].intersects(clipRect)) {
  289. paintTab(g, tabPlacement, rects, j, iconRect, textRect);
  290. }
  291. }
  292. }
  293. // Paint selected tab if its in the front run
  294. // since it may overlap other tabs
  295. if (selectedIndex >= 0 && getRunForTab(tabCount, selectedIndex) == 0) {
  296. if (rects[selectedIndex].intersects(clipRect)) {
  297. paintTab(g, tabPlacement, rects, selectedIndex, iconRect, textRect);
  298. }
  299. }
  300. // Paint content border
  301. paintContentBorder(g, tabPlacement, selectedIndex);
  302. }
  303. protected void paintTab(Graphics g, int tabPlacement,
  304. Rectangle[] rects, int tabIndex,
  305. Rectangle iconRect, Rectangle textRect) {
  306. Rectangle tabRect = rects[tabIndex];
  307. int selectedIndex = tabPane.getSelectedIndex();
  308. boolean isSelected = selectedIndex == tabIndex;
  309. paintTabBackground(g, tabPlacement, tabIndex, tabRect.x, tabRect.y,
  310. tabRect.width, tabRect.height, isSelected);
  311. paintTabBorder(g, tabPlacement, tabIndex, tabRect.x, tabRect.y,
  312. tabRect.width, tabRect.height, isSelected);
  313. String title = tabPane.getTitleAt(tabIndex);
  314. Font font = tabPane.getFont();
  315. FontMetrics metrics = g.getFontMetrics(font);
  316. Icon icon = getIconForTab(tabIndex);
  317. layoutLabel(tabPlacement, metrics, tabIndex, title, icon,
  318. tabRect, iconRect, textRect, isSelected);
  319. paintText(g, tabPlacement, font, metrics,
  320. tabIndex, title, textRect, isSelected);
  321. paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected);
  322. paintFocusIndicator(g, tabPlacement, rects, tabIndex,
  323. iconRect, textRect, isSelected);
  324. }
  325. protected void layoutLabel(int tabPlacement,
  326. FontMetrics metrics, int tabIndex,
  327. String title, Icon icon,
  328. Rectangle tabRect, Rectangle iconRect,
  329. Rectangle textRect, boolean isSelected ) {
  330. textRect.x = textRect.y = iconRect.x = iconRect.y = 0;
  331. SwingUtilities.layoutCompoundLabel((JComponent) tabPane,
  332. metrics, title, icon,
  333. SwingUtilities.CENTER,
  334. SwingUtilities.CENTER,
  335. SwingUtilities.CENTER,
  336. SwingUtilities.TRAILING,
  337. tabRect,
  338. iconRect,
  339. textRect,
  340. textIconGap);
  341. int xNudge = getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
  342. int yNudge = getTabLabelShiftY(tabPlacement, tabIndex, isSelected);
  343. iconRect.x += xNudge;
  344. iconRect.y += yNudge;
  345. textRect.x += xNudge;
  346. textRect.y += yNudge;
  347. }
  348. protected void paintIcon(Graphics g, int tabPlacement,
  349. int tabIndex, Icon icon, Rectangle iconRect,
  350. boolean isSelected ) {
  351. if (icon != null) {
  352. icon.paintIcon(tabPane, g, iconRect.x, iconRect.y);
  353. }
  354. }
  355. protected void paintText(Graphics g, int tabPlacement,
  356. Font font, FontMetrics metrics, int tabIndex,
  357. String title, Rectangle textRect,
  358. boolean isSelected) {
  359. g.setFont(font);
  360. View v;
  361. if (htmlViews!=null &&
  362. (v = (View)htmlViews.elementAt(tabIndex))!=null) {
  363. v.paint(g, textRect);
  364. } else {
  365. if (tabPane.isEnabled() && tabPane.isEnabledAt(tabIndex)) {
  366. g.setColor(tabPane.getForegroundAt(tabIndex));
  367. g.drawString(title,
  368. textRect.x,
  369. textRect.y + metrics.getAscent());
  370. } else { // tab disabled
  371. g.setColor(tabPane.getBackgroundAt(tabIndex).brighter());
  372. g.drawString(title,
  373. textRect.x, textRect.y + metrics.getAscent());
  374. g.setColor(tabPane.getBackgroundAt(tabIndex).darker());
  375. g.drawString(title,
  376. textRect.x - 1, textRect.y + metrics.getAscent() - 1);
  377. }
  378. }
  379. }
  380. protected int getTabLabelShiftX(int tabPlacement, int tabIndex, boolean isSelected) {
  381. Rectangle tabRect = rects[tabIndex];
  382. int nudge = 0;
  383. switch(tabPlacement) {
  384. case LEFT:
  385. nudge = isSelected? -1 : 1;
  386. break;
  387. case RIGHT:
  388. nudge = isSelected? 1 : -1;
  389. break;
  390. case BOTTOM:
  391. case TOP:
  392. default:
  393. nudge = tabRect.width % 2;
  394. }
  395. return nudge;
  396. }
  397. protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) {
  398. Rectangle tabRect = rects[tabIndex];
  399. int nudge = 0;
  400. switch(tabPlacement) {
  401. case BOTTOM:
  402. nudge = isSelected? 1 : -1;
  403. break;
  404. case LEFT:
  405. case RIGHT:
  406. nudge = tabRect.height % 2;
  407. break;
  408. case TOP:
  409. default:
  410. nudge = isSelected? -1 : 1;;
  411. }
  412. return nudge;
  413. }
  414. protected void paintFocusIndicator(Graphics g, int tabPlacement,
  415. Rectangle[] rects, int tabIndex,
  416. Rectangle iconRect, Rectangle textRect,
  417. boolean isSelected) {
  418. Rectangle tabRect = rects[tabIndex];
  419. if (tabPane.hasFocus() && isSelected) {
  420. int x, y, w, h;
  421. g.setColor(focus);
  422. switch(tabPlacement) {
  423. case LEFT:
  424. x = tabRect.x + 3;
  425. y = tabRect.y + 3;
  426. w = tabRect.width - 5;
  427. h = tabRect.height - 6;
  428. break;
  429. case RIGHT:
  430. x = tabRect.x + 2;
  431. y = tabRect.y + 3;
  432. w = tabRect.width - 5;
  433. h = tabRect.height - 6;
  434. break;
  435. case BOTTOM:
  436. x = tabRect.x + 3;
  437. y = tabRect.y + 2;
  438. w = tabRect.width - 6;
  439. h = tabRect.height - 5;
  440. break;
  441. case TOP:
  442. default:
  443. x = tabRect.x + 3;
  444. y = tabRect.y + 3;
  445. w = tabRect.width - 6;
  446. h = tabRect.height - 5;
  447. }
  448. BasicGraphicsUtils.drawDashedRect(g, x, y, w, h);
  449. }
  450. }
  451. /**
  452. * this function draws the border around each tab
  453. * note that this function does now draw the background of the tab.
  454. * that is done elsewhere
  455. */
  456. protected void paintTabBorder(Graphics g, int tabPlacement,
  457. int tabIndex,
  458. int x, int y, int w, int h,
  459. boolean isSelected ) {
  460. g.setColor(lightHighlight);
  461. switch (tabPlacement) {
  462. case LEFT:
  463. g.drawLine(x+1, y+h-2, x+1, y+h-2); // bottom-left highlight
  464. g.drawLine(x, y+2, x, y+h-3); // left highlight
  465. g.drawLine(x+1, y+1, x+1, y+1); // top-left highlight
  466. g.drawLine(x+2, y, x+w-1, y); // top highlight
  467. g.setColor(shadow);
  468. g.drawLine(x+2, y+h-2, x+w-1, y+h-2); // bottom shadow
  469. g.setColor(darkShadow);
  470. g.drawLine(x+2, y+h-1, x+w-1, y+h-1); // bottom dark shadow
  471. break;
  472. case RIGHT:
  473. g.drawLine(x, y, x+w-3, y); // top highlight
  474. g.setColor(shadow);
  475. g.drawLine(x, y+h-2, x+w-3, y+h-2); // bottom shadow
  476. g.drawLine(x+w-2, y+2, x+w-2, y+h-3); // right shadow
  477. g.setColor(darkShadow);
  478. g.drawLine(x+w-2, y+1, x+w-2, y+1); // top-right dark shadow
  479. g.drawLine(x+w-2, y+h-2, x+w-2, y+h-2); // bottom-right dark shadow
  480. g.drawLine(x+w-1, y+2, x+w-1, y+h-3); // right dark shadow
  481. g.drawLine(x, y+h-1, x+w-3, y+h-1); // bottom dark shadow
  482. break;
  483. case BOTTOM:
  484. g.drawLine(x, y, x, y+h-3); // left highlight
  485. g.drawLine(x+1, y+h-2, x+1, y+h-2); // bottom-left highlight
  486. g.setColor(shadow);
  487. g.drawLine(x+2, y+h-2, x+w-3, y+h-2); // bottom shadow
  488. g.drawLine(x+w-2, y, x+w-2, y+h-3); // right shadow
  489. g.setColor(darkShadow);
  490. g.drawLine(x+2, y+h-1, x+w-3, y+h-1); // bottom dark shadow
  491. g.drawLine(x+w-2, y+h-2, x+w-2, y+h-2); // bottom-right dark shadow
  492. g.drawLine(x+w-1, y, x+w-1, y+h-3); // right dark shadow
  493. break;
  494. case TOP:
  495. default:
  496. g.drawLine(x, y+2, x, y+h-1); // left highlight
  497. g.drawLine(x+1, y+1, x+1, y+1); // top-left highlight
  498. g.drawLine(x+2, y, x+w-3, y); // top highlight
  499. g.setColor(shadow);
  500. g.drawLine(x+w-2, y+2, x+w-2, y+h-1); // right shadow
  501. g.setColor(darkShadow);
  502. g.drawLine(x+w-1, y+2, x+w-1, y+h-1); // right dark-shadow
  503. g.drawLine(x+w-2, y+1, x+w-2, y+1); // top-right shadow
  504. }
  505. }
  506. protected void paintTabBackground(Graphics g, int tabPlacement,
  507. int tabIndex,
  508. int x, int y, int w, int h,
  509. boolean isSelected ) {
  510. g.setColor(tabPane.getBackgroundAt(tabIndex));
  511. switch(tabPlacement) {
  512. case LEFT:
  513. g.fillRect(x+1, y+1, w-2, h-3);
  514. break;
  515. case RIGHT:
  516. g.fillRect(x, y+1, w-2, h-3);
  517. break;
  518. case BOTTOM:
  519. g.fillRect(x+1, y, w-3, h-1);
  520. break;
  521. case TOP:
  522. default:
  523. g.fillRect(x+1, y+1, w-3, h-1);
  524. }
  525. }
  526. protected void paintContentBorder(Graphics g, int tabPlacement, int selectedIndex) {
  527. int width = tabPane.getWidth();
  528. int height = tabPane.getHeight();
  529. Insets insets = tabPane.getInsets();
  530. int x = insets.left;
  531. int y = insets.top;
  532. int w = width - insets.right - insets.left;
  533. int h = height - insets.top - insets.bottom;
  534. switch(tabPlacement) {
  535. case LEFT:
  536. x += calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
  537. w -= (x - insets.left);
  538. break;
  539. case RIGHT:
  540. w -= calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
  541. break;
  542. case BOTTOM:
  543. h -= calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
  544. break;
  545. case TOP:
  546. default:
  547. y += calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
  548. h -= (y - insets.top);
  549. }
  550. paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h);
  551. paintContentBorderLeftEdge(g, tabPlacement, selectedIndex, x, y, w, h);
  552. paintContentBorderBottomEdge(g, tabPlacement, selectedIndex, x, y, w, h);
  553. paintContentBorderRightEdge(g, tabPlacement, selectedIndex, x, y, w, h);
  554. }
  555. protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
  556. int selectedIndex,
  557. int x, int y, int w, int h) {
  558. g.setColor(lightHighlight);
  559. if (tabPlacement != TOP || selectedIndex < 0 ||
  560. (rects[selectedIndex].y + rects[selectedIndex].height + 1 < y)) {
  561. g.drawLine(x, y, x+w-2, y);
  562. } else {
  563. Rectangle selRect = rects[selectedIndex];
  564. g.drawLine(x, y, selRect.x - 1, y);
  565. if (selRect.x + selRect.width < x + w - 2) {
  566. g.drawLine(selRect.x + selRect.width, y,
  567. x+w-2, y);
  568. } else {
  569. g.setColor(shadow);
  570. g.drawLine(x+w-2, y, x+w-2, y);
  571. }
  572. }
  573. }
  574. protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
  575. int selectedIndex,
  576. int x, int y, int w, int h) {
  577. g.setColor(lightHighlight);
  578. if (tabPlacement != LEFT || selectedIndex < 0 ||
  579. (rects[selectedIndex].x + rects[selectedIndex].width + 1< x)) {
  580. g.drawLine(x, y, x, y+h-2);
  581. } else {
  582. Rectangle selRect = rects[selectedIndex];
  583. g.drawLine(x, y, x, selRect.y - 1);
  584. if (selRect.y + selRect.height < y + h - 2) {
  585. g.drawLine(x, selRect.y + selRect.height,
  586. x, y+h-2);
  587. }
  588. }
  589. }
  590. protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
  591. int selectedIndex,
  592. int x, int y, int w, int h) {
  593. g.setColor(shadow);
  594. if (tabPlacement != BOTTOM || selectedIndex < 0 ||
  595. (rects[selectedIndex].y - 1 > h)) {
  596. g.drawLine(x+1, y+h-2, x+w-2, y+h-2);
  597. g.setColor(darkShadow);
  598. g.drawLine(x, y+h-1, x+w-1, y+h-1);
  599. } else {
  600. Rectangle selRect = rects[selectedIndex];
  601. g.drawLine(x+1, y+h-2, selRect.x - 1, y+h-2);
  602. g.setColor(darkShadow);
  603. g.drawLine(x, y+h-1, selRect.x - 1, y+h-1);
  604. if (selRect.x + selRect.width < x + w - 2) {
  605. g.setColor(shadow);
  606. g.drawLine(selRect.x + selRect.width, y+h-2, x+w-2, y+h-2);
  607. g.setColor(darkShadow);
  608. g.drawLine(selRect.x + selRect.width, y+h-1, x+w-1, y+h-1);
  609. }
  610. }
  611. }
  612. protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
  613. int selectedIndex,
  614. int x, int y, int w, int h) {
  615. g.setColor(shadow);
  616. if (tabPlacement != RIGHT || selectedIndex < 0 ||
  617. rects[selectedIndex].x - 1 > w) {
  618. g.drawLine(x+w-2, y+1, x+w-2, y+h-3);
  619. g.setColor(darkShadow);
  620. g.drawLine(x+w-1, y, x+w-1, y+h-1);
  621. } else {
  622. Rectangle selRect = rects[selectedIndex];
  623. g.drawLine(x+w-2, y+1, x+w-2, selRect.y - 1);
  624. g.setColor(darkShadow);
  625. g.drawLine(x+w-1, y, x+w-1, selRect.y - 1);
  626. if (selRect.y + selRect.height < y + h - 2) {
  627. g.setColor(shadow);
  628. g.drawLine(x+w-2, selRect.y + selRect.height,
  629. x+w-2, y+h-2);
  630. g.setColor(darkShadow);
  631. g.drawLine(x+w-1, selRect.y + selRect.height,
  632. x+w-1, y+h-2);
  633. }
  634. }
  635. }
  636. private void ensureCurrentLayout() {
  637. if (!tabPane.isValid()) {
  638. tabPane.validate();
  639. }
  640. /* If tabPane doesn't have a peer yet, the validate() call will
  641. * silently fail. We handle that by forcing a layout if tabPane
  642. * is still invalid. See bug 4237677.
  643. */
  644. if (!tabPane.isValid()) {
  645. TabbedPaneLayout layout = (TabbedPaneLayout)tabPane.getLayout();
  646. layout.calculateLayoutInfo();
  647. }
  648. }
  649. // TabbedPaneUI methods
  650. public Rectangle getTabBounds(JTabbedPane pane, int i) {
  651. ensureCurrentLayout();
  652. return new Rectangle(rects[i]);
  653. }
  654. public int getTabRunCount(JTabbedPane pane) {
  655. return runCount;
  656. }
  657. public int tabForCoordinate(JTabbedPane pane, int x, int y) {
  658. ensureCurrentLayout();
  659. int tabCount = tabPane.getTabCount();
  660. for (int i = 0; i < tabCount; i++) {
  661. if (rects[i].contains(x, y)) {
  662. return i;
  663. }
  664. }
  665. return -1;
  666. }
  667. // BasicTabbedPaneUI methods
  668. protected Component getVisibleComponent() {
  669. return visibleComponent;
  670. }
  671. protected void setVisibleComponent(Component component) {
  672. if (visibleComponent == component) {
  673. return;
  674. }
  675. if (visibleComponent != null) {
  676. visibleComponent.setVisible(false);
  677. }
  678. if (component != null) {
  679. component.setVisible(true);
  680. }
  681. visibleComponent = component;
  682. }
  683. protected void assureRectsCreated(int tabCount) {
  684. int rectArrayLen = rects.length;
  685. if (tabCount != rectArrayLen ) {
  686. Rectangle[] tempRectArray = new Rectangle[tabCount];
  687. System.arraycopy(rects, 0, tempRectArray, 0,
  688. Math.min(rectArrayLen, tabCount));
  689. rects = tempRectArray;
  690. for (int rectIndex = rectArrayLen; rectIndex < tabCount; rectIndex++) {
  691. rects[rectIndex] = new Rectangle();
  692. }
  693. }
  694. }
  695. protected void expandTabRunsArray() {
  696. int rectLen = tabRuns.length;
  697. int[] newArray = new int[rectLen+10];
  698. System.arraycopy(tabRuns, 0, newArray, 0, runCount);
  699. tabRuns = newArray;
  700. }
  701. protected int getRunForTab(int tabCount, int tabIndex) {
  702. for (int i = 0; i < runCount; i++) {
  703. int first = tabRuns[i];
  704. int last = lastTabInRun(tabCount, i);
  705. if (tabIndex >= first && tabIndex <= last) {
  706. return i;
  707. }
  708. }
  709. return 0;
  710. }
  711. protected int lastTabInRun(int tabCount, int run) {
  712. if (runCount == 1) {
  713. return tabCount - 1;
  714. }
  715. int nextRun = (run == runCount - 1? 0 : run + 1);
  716. if (tabRuns[nextRun] == 0) {
  717. return tabCount - 1;
  718. }
  719. return tabRuns[nextRun]-1;
  720. }
  721. protected int getTabRunOverlay(int tabPlacement) {
  722. return tabRunOverlay;
  723. }
  724. protected int getTabRunIndent(int tabPlacement, int run) {
  725. return 0;
  726. }
  727. protected boolean shouldPadTabRun(int tabPlacement, int run) {
  728. return runCount > 1;
  729. }
  730. protected boolean shouldRotateTabRuns(int tabPlacement) {
  731. return true;
  732. }
  733. protected Icon getIconForTab(int tabIndex) {
  734. return ((!tabPane.isEnabled() || !tabPane.isEnabledAt(tabIndex)) &&
  735. tabPane.getDisabledIconAt(tabIndex) != null)?
  736. tabPane.getDisabledIconAt(tabIndex) : tabPane.getIconAt(tabIndex);
  737. }
  738. protected int calculateTabHeight(int tabPlacement, int tabIndex, int fontHeight) {
  739. int height = 0;
  740. View v;
  741. if ( (htmlViews!=null) &&
  742. ((v = (View)htmlViews.elementAt(tabIndex)) != null)) {
  743. height += (int)v.getPreferredSpan(View.Y_AXIS);
  744. } else {
  745. height += fontHeight;
  746. }
  747. Icon icon = getIconForTab(tabIndex);
  748. Insets tabInsets = getTabInsets(tabPlacement, tabIndex);
  749. if (icon != null) {
  750. height = Math.max(height, icon.getIconHeight());
  751. }
  752. height += tabInsets.top + tabInsets.bottom + 2;
  753. return height;
  754. }
  755. protected int calculateMaxTabHeight(int tabPlacement) {
  756. FontMetrics metrics = getFontMetrics();
  757. int tabCount = tabPane.getTabCount();
  758. int result = 0;
  759. int fontHeight = metrics.getHeight();
  760. for(int i = 0; i < tabCount; i++) {
  761. result = Math.max(calculateTabHeight(tabPlacement, i, fontHeight), result);
  762. }
  763. return result;
  764. }
  765. protected int calculateTabWidth(int tabPlacement, int tabIndex, FontMetrics metrics) {
  766. Icon icon = getIconForTab(tabIndex);
  767. Insets tabInsets = getTabInsets(tabPlacement, tabIndex);
  768. int width = tabInsets.left + tabInsets.right + 3;
  769. View v;
  770. if (icon != null) {
  771. width += icon.getIconWidth() + textIconGap;
  772. }
  773. if ((htmlViews!=null) &&
  774. ((v = (View)htmlViews.elementAt(tabIndex)) != null)) {
  775. width += (int)v.getPreferredSpan(View.X_AXIS);
  776. } else {
  777. String title = tabPane.getTitleAt(tabIndex);
  778. width += SwingUtilities.computeStringWidth(metrics, title);
  779. }
  780. return width;
  781. }
  782. protected int calculateMaxTabWidth(int tabPlacement) {
  783. FontMetrics metrics = getFontMetrics();
  784. int tabCount = tabPane.getTabCount();
  785. int result = 0;
  786. for(int i = 0; i < tabCount; i++) {
  787. result = Math.max(calculateTabWidth(tabPlacement, i, metrics), result);
  788. }
  789. return result;
  790. }
  791. protected int calculateTabAreaHeight(int tabPlacement, int horizRunCount, int maxTabHeight) {
  792. Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
  793. int tabRunOverlay = getTabRunOverlay(tabPlacement);
  794. return (horizRunCount > 0?
  795. horizRunCount * (maxTabHeight-tabRunOverlay) + tabRunOverlay +
  796. tabAreaInsets.top + tabAreaInsets.bottom :
  797. 0);
  798. }
  799. protected int calculateTabAreaWidth(int tabPlacement, int vertRunCount, int maxTabWidth) {
  800. Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
  801. int tabRunOverlay = getTabRunOverlay(tabPlacement);
  802. return (vertRunCount > 0?
  803. vertRunCount * (maxTabWidth-tabRunOverlay) + tabRunOverlay +
  804. tabAreaInsets.left + tabAreaInsets.right :
  805. 0);
  806. }
  807. protected Insets getTabInsets(int tabPlacement, int tabIndex) {
  808. return tabInsets;
  809. }
  810. protected Insets getSelectedTabPadInsets(int tabPlacement) {
  811. rotateInsets(selectedTabPadInsets, currentPadInsets, tabPlacement);
  812. return currentPadInsets;
  813. }
  814. protected Insets getTabAreaInsets(int tabPlacement) {
  815. rotateInsets(tabAreaInsets, currentTabAreaInsets, tabPlacement);
  816. return currentTabAreaInsets;
  817. }
  818. protected Insets getContentBorderInsets(int tabPlacement) {
  819. return contentBorderInsets;
  820. }
  821. protected FontMetrics getFontMetrics() {
  822. Font font = tabPane.getFont();
  823. return Toolkit.getDefaultToolkit().getFontMetrics(font);
  824. }
  825. // Tab Navigation methods
  826. protected void navigateSelectedTab(int direction) {
  827. int tabPlacement = tabPane.getTabPlacement();
  828. int current = tabPane.getSelectedIndex();
  829. int tabCount = tabPane.getTabCount();
  830. int offset;
  831. switch(tabPlacement) {
  832. case LEFT:
  833. case RIGHT:
  834. switch(direction) {
  835. case NORTH:
  836. selectPreviousTab(current);
  837. break;
  838. case SOUTH:
  839. selectNextTab(current);
  840. break;
  841. case WEST:
  842. offset = getTabRunOffset(tabPlacement, tabCount, current, false);
  843. selectAdjacentRunTab(tabPlacement, current, offset);
  844. break;
  845. case EAST:
  846. offset = getTabRunOffset(tabPlacement, tabCount, current, true);
  847. selectAdjacentRunTab(tabPlacement, current, offset);
  848. break;
  849. default:
  850. }
  851. break;
  852. case BOTTOM:
  853. case TOP:
  854. default:
  855. switch(direction) {
  856. case NORTH:
  857. offset = getTabRunOffset(tabPlacement, tabCount, current, false);
  858. selectAdjacentRunTab(tabPlacement, current, offset);
  859. break;
  860. case SOUTH:
  861. offset = getTabRunOffset(tabPlacement, tabCount, current, true);
  862. selectAdjacentRunTab(tabPlacement, current, offset);
  863. break;
  864. case EAST:
  865. selectNextTab(current);
  866. break;
  867. case WEST:
  868. selectPreviousTab(current);
  869. break;
  870. default:
  871. }
  872. }
  873. }
  874. protected void selectNextTab(int current) {
  875. int tabIndex = getNextTabIndex(current);
  876. while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
  877. tabIndex = getNextTabIndex(tabIndex);
  878. }
  879. tabPane.setSelectedIndex(tabIndex);
  880. }
  881. protected void selectPreviousTab(int current) {
  882. int tabIndex = getPreviousTabIndex(current);
  883. while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
  884. tabIndex = getPreviousTabIndex(tabIndex);
  885. }
  886. tabPane.setSelectedIndex(tabIndex);
  887. }
  888. protected void selectAdjacentRunTab(int tabPlacement,
  889. int tabIndex, int offset) {
  890. if ( runCount < 2 ) {
  891. return;
  892. }
  893. int newIndex;
  894. Rectangle r = rects[tabIndex];
  895. switch(tabPlacement) {
  896. case LEFT:
  897. case RIGHT:
  898. newIndex = tabForCoordinate(tabPane, r.x + r.width2 + offset,
  899. r.y + r.height2);
  900. break;
  901. case BOTTOM:
  902. case TOP:
  903. default:
  904. newIndex = tabForCoordinate(tabPane, r.x + r.width2,
  905. r.y + r.height2 + offset);
  906. }
  907. if (newIndex != -1) {
  908. while (!tabPane.isEnabledAt(newIndex) && newIndex != tabIndex) {
  909. newIndex = getNextTabIndex(newIndex);
  910. }
  911. tabPane.setSelectedIndex(newIndex);
  912. }
  913. }
  914. protected int getTabRunOffset(int tabPlacement, int tabCount,
  915. int tabIndex, boolean forward) {
  916. int run = getRunForTab(tabCount, tabIndex);
  917. int offset;
  918. switch(tabPlacement) {
  919. case LEFT: {
  920. if (run == 0) {
  921. offset = (forward?
  922. -(calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth) :
  923. -maxTabWidth);
  924. } else if (run == runCount - 1) {
  925. offset = (forward?
  926. maxTabWidth :
  927. calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth);
  928. } else {
  929. offset = (forward? maxTabWidth : -maxTabWidth);
  930. }
  931. break;
  932. }
  933. case RIGHT: {
  934. if (run == 0) {
  935. offset = (forward?
  936. maxTabWidth :
  937. calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth);
  938. } else if (run == runCount - 1) {
  939. offset = (forward?
  940. -(calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth) :
  941. -maxTabWidth);
  942. } else {
  943. offset = (forward? maxTabWidth : -maxTabWidth);
  944. }
  945. break;
  946. }
  947. case BOTTOM: {
  948. if (run == 0) {
  949. offset = (forward?
  950. maxTabHeight :
  951. calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight);
  952. } else if (run == runCount - 1) {
  953. offset = (forward?
  954. -(calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight) :
  955. -maxTabHeight);
  956. } else {
  957. offset = (forward? maxTabHeight : -maxTabHeight);
  958. }
  959. break;
  960. }
  961. case TOP:
  962. default: {
  963. if (run == 0) {
  964. offset = (forward?
  965. -(calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight) :
  966. -maxTabHeight);
  967. } else if (run == runCount - 1) {
  968. offset = (forward?
  969. maxTabHeight :
  970. calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight);
  971. } else {
  972. offset = (forward? maxTabHeight : -maxTabHeight);
  973. }
  974. }
  975. }
  976. return offset;
  977. }
  978. protected int getPreviousTabIndex(int base) {
  979. int tabIndex = (base - 1 >= 0? base - 1 : tabPane.getTabCount() - 1);
  980. return (tabIndex >= 0? tabIndex : 0);
  981. }
  982. protected int getNextTabIndex(int base) {
  983. return (base+1)%tabPane.getTabCount();
  984. }
  985. protected static void rotateInsets(Insets topInsets, Insets targetInsets, int targetPlacement) {
  986. switch(targetPlacement) {
  987. case LEFT:
  988. targetInsets.top = topInsets.right;
  989. targetInsets.left = topInsets.top;
  990. targetInsets.bottom = topInsets.left;
  991. targetInsets.right = topInsets.bottom;
  992. break;
  993. case BOTTOM:
  994. targetInsets.top = topInsets.bottom;
  995. targetInsets.left = topInsets.right;
  996. targetInsets.bottom = topInsets.top;
  997. targetInsets.right = topInsets.left;
  998. break;
  999. case RIGHT:
  1000. targetInsets.top = topInsets.left;
  1001. targetInsets.left = topInsets.bottom;
  1002. targetInsets.bottom = topInsets.right;
  1003. targetInsets.right = topInsets.top;
  1004. break;
  1005. case TOP:
  1006. default:
  1007. targetInsets.top = topInsets.top;
  1008. targetInsets.left = topInsets.left;
  1009. targetInsets.bottom = topInsets.bottom;
  1010. targetInsets.right = topInsets.right;
  1011. }
  1012. }
  1013. // REMIND(aim,7/29/98): This method should be made
  1014. // protected in the next release where
  1015. // API changes are allowed
  1016. //
  1017. boolean requestFocusForVisibleComponent() {
  1018. Component visibleComponent = getVisibleComponent();
  1019. if (visibleComponent.isFocusTraversable()) {
  1020. visibleComponent.requestFocus();
  1021. return true;
  1022. } else if (visibleComponent instanceof JComponent) {
  1023. if (((JComponent)visibleComponent).requestDefaultFocus()) {
  1024. return true;
  1025. }
  1026. }
  1027. return false;
  1028. }
  1029. // REMIND(aim,7/29/98): These actions should be broken
  1030. // out into protected inner classes in the next release where
  1031. // API changes are allowed
  1032. private static class RightAction extends AbstractAction {
  1033. public void actionPerformed(ActionEvent e) {
  1034. JTabbedPane pane = (JTabbedPane)e.getSource();
  1035. BasicTabbedPaneUI ui = (BasicTabbedPaneUI)pane.getUI();
  1036. ui.navigateSelectedTab(EAST);
  1037. }
  1038. };
  1039. private static class LeftAction extends AbstractAction {
  1040. public void actionPerformed(ActionEvent e) {
  1041. JTabbedPane pane = (JTabbedPane)e.getSource();
  1042. BasicTabbedPaneUI ui = (BasicTabbedPaneUI)pane.getUI();
  1043. ui.navigateSelectedTab(WEST);
  1044. }
  1045. };
  1046. private static class UpAction extends AbstractAction {
  1047. public void actionPerformed(ActionEvent e) {
  1048. JTabbedPane pane = (JTabbedPane)e.getSource();
  1049. BasicTabbedPaneUI ui = (BasicTabbedPaneUI)pane.getUI();
  1050. ui.navigateSelectedTab(NORTH);
  1051. }
  1052. };
  1053. private static class DownAction extends AbstractAction {
  1054. public void actionPerformed(ActionEvent e) {
  1055. JTabbedPane pane = (JTabbedPane)e.getSource();
  1056. BasicTabbedPaneUI ui = (BasicTabbedPaneUI)pane.getUI();
  1057. ui.navigateSelectedTab(SOUTH);
  1058. }
  1059. };
  1060. private static class PageUpAction extends AbstractAction {
  1061. public void actionPerformed(ActionEvent e) {
  1062. JTabbedPane pane = (JTabbedPane)e.getSource();
  1063. BasicTabbedPaneUI ui = (BasicTabbedPaneUI)pane.getUI();
  1064. int tabPlacement = pane.getTabPlacement();
  1065. if (tabPlacement == TOP|| tabPlacement == BOTTOM) {
  1066. ui.navigateSelectedTab(WEST);
  1067. } else {
  1068. ui.navigateSelectedTab(NORTH);
  1069. }
  1070. }
  1071. };
  1072. private static class PageDownAction extends AbstractAction {
  1073. public void actionPerformed(ActionEvent e) {
  1074. JTabbedPane pane = (JTabbedPane)e.getSource();
  1075. BasicTabbedPaneUI ui = (BasicTabbedPaneUI)pane.getUI();
  1076. int tabPlacement = pane.getTabPlacement();
  1077. if (tabPlacement == TOP || tabPlacement == BOTTOM) {
  1078. ui.navigateSelectedTab(EAST);
  1079. } else {
  1080. ui.navigateSelectedTab(SOUTH);
  1081. }
  1082. }
  1083. };
  1084. private static class RequestFocusAction extends AbstractAction {
  1085. public void actionPerformed(ActionEvent e) {
  1086. JTabbedPane pane = (JTabbedPane)e.getSource();
  1087. pane.requestFocus();
  1088. }
  1089. };
  1090. private static class RequestFocusForVisibleAction extends AbstractAction {
  1091. public void actionPerformed(ActionEvent e) {
  1092. JTabbedPane pane = (JTabbedPane)e.getSource();
  1093. BasicTabbedPaneUI ui = (BasicTabbedPaneUI)pane.getUI();
  1094. ui.requestFocusForVisibleComponent();
  1095. }
  1096. };
  1097. /**
  1098. * This inner class is marked "public" due to a compiler bug.
  1099. * This class should be treated as a "protected" inner class.
  1100. * Instantiate it only within subclasses of BasicTabbedPaneUI.
  1101. */
  1102. public class TabbedPaneLayout implements LayoutManager {
  1103. public void addLayoutComponent(String name, Component comp) {}
  1104. public void removeLayoutComponent(Component comp) {}
  1105. public Dimension preferredLayoutSize(Container parent) {
  1106. return calculateSize(false);
  1107. }
  1108. public Dimension minimumLayoutSize(Container parent) {
  1109. return calculateSize(true);
  1110. }
  1111. protected Dimension calculateSize(boolean minimum) {
  1112. int tabPlacement = tabPane.getTabPlacement();
  1113. Insets insets = tabPane.getInsets();
  1114. Insets borderInsets = getContentBorderInsets(tabPlacement);
  1115. Dimension zeroSize = new Dimension(0,0);
  1116. int height = borderInsets.top + borderInsets.bottom;
  1117. int width = borderInsets.left + borderInsets.right;
  1118. int cWidth = 0;
  1119. int cHeight = 0;
  1120. for (int i = 0; i < tabPane.getTabCount(); i++) {
  1121. Component component = tabPane.getComponentAt(i);
  1122. Dimension size = zeroSize;
  1123. size = minimum? component.getMinimumSize() :
  1124. component.getPreferredSize();
  1125. if (size != null) {
  1126. cHeight = Math.max(size.height, cHeight);
  1127. cWidth = Math.max(size.width, cWidth);
  1128. }
  1129. }
  1130. width += cWidth;
  1131. height += cHeight;
  1132. int tabExtent = 0;
  1133. switch(tabPlacement) {
  1134. case LEFT:
  1135. case RIGHT:
  1136. tabExtent = preferredTabAreaWidth(tabPlacement, height);
  1137. width += tabExtent;
  1138. break;
  1139. case TOP:
  1140. case BOTTOM:
  1141. default:
  1142. tabExtent = preferredTabAreaHeight(tabPlacement, width);
  1143. height += tabExtent;
  1144. }
  1145. return new Dimension(width + insets.left + insets.right,
  1146. height + insets.bottom + insets.top);
  1147. }
  1148. protected int preferredTabAreaHeight(int tabPlacement, int width) {
  1149. FontMetrics metrics = getFontMetrics();
  1150. int tabCount = tabPane.getTabCount();
  1151. int total = 0;
  1152. if (tabCount > 0) {
  1153. int rows = 1;
  1154. int x = 0;
  1155. int maxTabHeight = calculateMaxTabHeight(tabPlacement);
  1156. for (int i = 0; i < tabCount; i++) {
  1157. int tabWidth = calculateTabWidth(tabPlacement, i, metrics);
  1158. if (x != 0 && x + tabWidth > width) {
  1159. rows++;
  1160. x = 0;
  1161. }
  1162. x += tabWidth;
  1163. }
  1164. total = calculateTabAreaHeight(tabPlacement, rows, maxTabHeight);
  1165. }
  1166. return total;
  1167. }
  1168. protected int preferredTabAreaWidth(int tabPlacement, int height) {
  1169. FontMetrics metrics = getFontMetrics();
  1170. int tabCount = tabPane.getTabCount();
  1171. int total = 0;
  1172. if (tabCount > 0) {
  1173. int columns = 1;
  1174. int y = 0;
  1175. int fontHeight = metrics.getHeight();
  1176. maxTabWidth = calculateMaxTabWidth(tabPlacement);
  1177. for (int i = 0; i < tabCount; i++) {
  1178. int tabHeight = calculateTabHeight(tabPlacement, i, fontHeight);
  1179. if (y != 0 && y + tabHeight > height) {
  1180. columns++;
  1181. y = 0;
  1182. }
  1183. y += tabHeight;
  1184. }
  1185. total = calculateTabAreaWidth(tabPlacement, columns, maxTabWidth);
  1186. }
  1187. return total;
  1188. }
  1189. public void layoutContainer(Container parent) {
  1190. int tabPlacement = tabPane.getTabPlacement();
  1191. Insets insets = tabPane.getInsets();
  1192. int selectedIndex = tabPane.getSelectedIndex();
  1193. Component visibleComponent = getVisibleComponent();
  1194. calculateLayoutInfo();
  1195. if (selectedIndex < 0) {
  1196. if (visibleComponent != null) {
  1197. // The last tab was removed, so remove the component
  1198. setVisibleComponent(null);
  1199. }
  1200. } else {
  1201. int cx, cy, cw, ch;
  1202. int totalTabWidth = 0;
  1203. int totalTabHeight = 0;
  1204. Insets borderInsets = getContentBorderInsets(tabPlacement);
  1205. Component selectedComponent = tabPane.getComponentAt(selectedIndex);
  1206. boolean shouldChangeFocus = false;
  1207. // In order to allow programs to use a single component
  1208. // as the display for multiple tabs, we will not change
  1209. // the visible compnent if the currently selected tab
  1210. // has a null component. This is a bit dicey, as we don't
  1211. // explicitly state we support this in the spec, but since
  1212. // programs are now depending on this, we're making it work.
  1213. //
  1214. if (selectedComponent != null) {
  1215. if (selectedComponent != visibleComponent) {
  1216. if (visibleComponent != null) {
  1217. if (SwingUtilities.findFocusOwner(visibleComponent) != null) {
  1218. shouldChangeFocus = true;
  1219. }
  1220. }
  1221. setVisibleComponent(selectedComponent);
  1222. }
  1223. }
  1224. Rectangle bounds = tabPane.getBounds();
  1225. int numChildren = tabPane.getComponentCount();
  1226. if (numChildren > 0) {
  1227. switch(tabPlacement) {
  1228. case LEFT:
  1229. totalTabWidth = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
  1230. cx = insets.left + totalTabWidth + borderInsets.left;
  1231. cy = insets.top + borderInsets.top;
  1232. break;
  1233. case RIGHT:
  1234. totalTabWidth = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
  1235. cx = insets.left + borderInsets.left;
  1236. cy = insets.top + borderInsets.top;
  1237. break;
  1238. case BOTTOM:
  1239. totalTabHeight = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
  1240. cx = insets.left + borderInsets.left;
  1241. cy = insets.top + borderInsets.top;
  1242. break;
  1243. case TOP:
  1244. default:
  1245. totalTabHeight = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
  1246. cx = insets.left + borderInsets.left;
  1247. cy = insets.top + totalTabHeight + borderInsets.top;
  1248. }
  1249. cw = bounds.width - totalTabWidth -
  1250. insets.left - insets.right -
  1251. borderInsets.left - borderInsets.right;
  1252. ch = bounds.height - totalTabHeight -
  1253. insets.top - insets.bottom -
  1254. borderInsets.top - borderInsets.bottom;
  1255. for (int i=0; i < numChildren; i++) {
  1256. Component child = tabPane.getComponent(i);
  1257. child.setBounds(cx, cy, cw, ch);
  1258. }
  1259. }
  1260. if (shouldChangeFocus) {
  1261. if (!requestFocusForVisibleComponent()) {
  1262. tabPane.requestFocus();
  1263. }
  1264. }
  1265. }
  1266. }
  1267. public void calculateLayoutInfo() {
  1268. int tabCount = tabPane.getTabCount();
  1269. assureRectsCreated(tabCount);
  1270. calculateTabRects(tabPane.getTabPlacement(), tabCount);
  1271. }
  1272. protected void calculateTabRects(int tabPlacement, int tabCount) {
  1273. FontMetrics metrics = getFontMetrics();
  1274. Dimension size = tabPane.getSize();
  1275. Insets insets = tabPane.getInsets();
  1276. Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
  1277. int fontHeight = metrics.getHeight();
  1278. int selectedIndex = tabPane.getSelectedIndex();
  1279. int tabRunOverlay;
  1280. int i, j;
  1281. int x, y;
  1282. int returnAt;
  1283. boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
  1284. boolean leftToRight = BasicGraphicsUtils.isLeftToRight(tabPane);
  1285. //
  1286. // Calculate bounds within which a tab run must fit
  1287. //
  1288. switch(tabPlacement) {
  1289. case LEFT:
  1290. maxTabWidth = calculateMaxTabWidth(tabPlacement);
  1291. x = insets.left + tabAreaInsets.left;
  1292. y = insets.top + tabAreaInsets.top;
  1293. returnAt = size.height - (insets.bottom + tabAreaInsets.bottom);
  1294. break;
  1295. case RIGHT:
  1296. maxTabWidth = calculateMaxTabWidth(tabPlacement);
  1297. x = size.width - insets.right - tabAreaInsets.right - maxTabWidth;
  1298. y = insets.top + tabAreaInsets.top;
  1299. returnAt = size.height - (insets.bottom + tabAreaInsets.bottom);
  1300. break;
  1301. case BOTTOM:
  1302. maxTabHeight = calculateMaxTabHeight(tabPlacement);
  1303. x = insets.left + tabAreaInsets.left;
  1304. y = size.height - insets.bottom - tabAreaInsets.bottom - maxTabHeight;
  1305. returnAt = size.width - (insets.right + tabAreaInsets.right);
  1306. break;
  1307. case TOP:
  1308. default:
  1309. maxTabHeight = calculateMaxTabHeight(tabPlacement);
  1310. x = insets.left + tabAreaInsets.left;
  1311. y = insets.top + tabAreaInsets.top;
  1312. returnAt = size.width - (insets.right + tabAreaInsets.right);
  1313. break;
  1314. }
  1315. tabRunOverlay = getTabRunOverlay(tabPlacement);
  1316. runCount = 0;
  1317. selectedRun = -1;
  1318. if (tabCount == 0) {
  1319. return;
  1320. }
  1321. // Run through tabs and partition them into runs
  1322. Rectangle rect;
  1323. for (i = 0; i < tabCount; i++) {
  1324. rect = rects[i];
  1325. if (!verticalTabRuns) {
  1326. // Tabs on TOP or BOTTOM....
  1327. if (i > 0) {
  1328. rect.x = rects[i-1].x + rects[i-1].width;
  1329. } else {
  1330. tabRuns[0] = 0;
  1331. runCount = 1;
  1332. maxTabWidth = 0;
  1333. rect.x = x;
  1334. }
  1335. rect.width = calculateTabWidth(tabPlacement, i, metrics);
  1336. maxTabWidth = Math.max(maxTabWidth, rect.width);
  1337. // Never move a TAB down a run if it is in the first column.
  1338. // Even if there isn't enough room, moving it to a fresh
  1339. // line won't help.
  1340. if (rect.x != 2 + insets.left && rect.x + rect.width > returnAt) {
  1341. if (runCount > tabRuns.length - 1) {
  1342. expandTabRunsArray();
  1343. }
  1344. tabRuns[runCount] = i;
  1345. runCount++;
  1346. rect.x = x;
  1347. }
  1348. // Initialize y position in case there's just one run
  1349. rect.y = y;
  1350. rect.height = maxTabHeight/* - 2*/;
  1351. } else {
  1352. // Tabs on LEFT or RIGHT...
  1353. if (i > 0) {
  1354. rect.y = rects[i-1].y + rects[i-1].height;
  1355. } else {
  1356. tabRuns[0] = 0;
  1357. runCount = 1;
  1358. maxTabHeight = 0;
  1359. rect.y = y;
  1360. }
  1361. rect.height = calculateTabHeight(tabPlacement, i, fontHeight);
  1362. maxTabHeight = Math.max(maxTabHeight, rect.height);
  1363. // Never move a TAB over a run if it is in the first run.
  1364. // Even if there isn't enough room, moving it to a fresh
  1365. // column won't help.
  1366. if (rect.y != 2 + insets.top && rect.y + rect.height > returnAt) {
  1367. if (runCount > tabRuns.length - 1) {
  1368. expandTabRunsArray();
  1369. }
  1370. tabRuns[runCount] = i;
  1371. runCount++;
  1372. rect.y = y;
  1373. }
  1374. // Initialize x position in case there's just one column
  1375. rect.x = x;
  1376. rect.width = maxTabWidth/* - 2*/;
  1377. }
  1378. if (i == selectedIndex) {