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