1. /*
  2. * @(#)MetalTabbedPaneUI.java 1.20 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.metal;
  8. import javax.swing.*;
  9. import javax.swing.event.*;
  10. import java.awt.*;
  11. import java.awt.event.*;
  12. import javax.swing.plaf.*;
  13. import java.io.Serializable;
  14. import javax.swing.plaf.basic.BasicTabbedPaneUI;
  15. /**
  16. * The Metal subclass of BasicTabbedPaneUI.
  17. * <p>
  18. * <strong>Warning:</strong>
  19. * Serialized objects of this class will not be compatible with
  20. * future Swing releases. The current serialization support is appropriate
  21. * for short term storage or RMI between applications running the same
  22. * version of Swing. A future release of Swing will provide support for
  23. * long term persistence.
  24. *
  25. * @version 1.20 11/29/01
  26. * @author Tom Santos
  27. */
  28. public class MetalTabbedPaneUI extends BasicTabbedPaneUI {
  29. protected int minTabWidth = 40;
  30. protected Color tabAreaBackground;
  31. protected Color selectColor;
  32. protected Color selectHighlight;
  33. public static ComponentUI createUI( JComponent x ) {
  34. return new MetalTabbedPaneUI();
  35. }
  36. protected LayoutManager createLayoutManager() {
  37. return new TabbedPaneLayout();
  38. }
  39. protected void installDefaults() {
  40. super.installDefaults();
  41. tabAreaBackground = UIManager.getColor("TabbedPane.tabAreaBackground");
  42. selectColor = UIManager.getColor("TabbedPane.selected");
  43. selectHighlight = UIManager.getColor("TabbedPane.selectHighlight");
  44. }
  45. protected void paintTabBorder( Graphics g, int tabPlacement,
  46. int tabIndex, int x, int y, int w, int h,
  47. boolean isSelected) {
  48. int bottom = y + (h-1);
  49. int right = x + (w-1);
  50. switch ( tabPlacement ) {
  51. case LEFT:
  52. paintLeftTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
  53. break;
  54. case BOTTOM:
  55. paintBottomTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
  56. break;
  57. case RIGHT:
  58. paintRightTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
  59. break;
  60. case TOP:
  61. default:
  62. paintTopTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
  63. }
  64. }
  65. protected void paintTopTabBorder( int tabIndex, Graphics g,
  66. int x, int y, int w, int h,
  67. int btm, int rght,
  68. boolean isSelected ) {
  69. int currentRun = getRunForTab( tabPane.getTabCount(), tabIndex );
  70. int lastIndex = lastTabInRun( tabPane.getTabCount(), currentRun );
  71. int firstIndex = tabRuns[ currentRun ];
  72. //
  73. // Paint Gap
  74. //
  75. if ( shouldFillGap( currentRun, tabIndex, x, y ) ) {
  76. g.translate( x, y );
  77. g.setColor( getColorForGap( currentRun, x, y + 1 ) );
  78. g.fillRect( 1, 0, 5, 3 );
  79. g.fillRect( 1, 3, 2, 2 );
  80. g.translate( -x, -y );
  81. }
  82. g.translate( x, y );
  83. int bottom = h - 1;
  84. int right = w - 1;
  85. //
  86. // Paint Border
  87. //
  88. g.setColor( darkShadow );
  89. // Paint slant
  90. g.drawLine( 1,5 , 6,0 );
  91. // Paint top
  92. g.drawLine( 6,0 , right,0 );
  93. // Paint right
  94. if ( tabIndex == lastIndex ) {
  95. g.drawLine( right,1 , right,bottom );
  96. }
  97. // Paint left
  98. if ( tabIndex != tabRuns[ runCount - 1 ] ) {
  99. g.drawLine( 0,0 , 0,bottom );
  100. } else {
  101. g.drawLine( 0,6 , 0,bottom );
  102. }
  103. //
  104. // Paint Highlight
  105. //
  106. g.setColor( isSelected ? selectHighlight : highlight );
  107. // Paint slant
  108. g.drawLine( 1,6 , 6,1 );
  109. // Paint top
  110. g.drawLine( 6,1 , right,1 );
  111. // Paint left
  112. g.drawLine( 1,6 , 1,bottom );
  113. if ( tabIndex == firstIndex && tabIndex != tabRuns[ runCount - 1 ] ) {
  114. g.setColor( tabPane.getSelectedIndex() == tabRuns[ currentRun + 1 ] ? selectHighlight : highlight );
  115. g.drawLine( 1,0 , 1,4 );
  116. }
  117. g.translate( -x, -y );
  118. }
  119. protected boolean shouldFillGap( int currentRun, int tabIndex, int x, int y ) {
  120. boolean result = false;
  121. if ( currentRun == runCount - 2 ) { // If it's the second to last row.
  122. Rectangle lastTabBounds = getTabBounds( tabPane, tabPane.getTabCount() - 1 );
  123. int lastTabRight = lastTabBounds.x + lastTabBounds.width - 1;
  124. if ( lastTabRight > getTabBounds( tabPane, tabIndex ).x + 2 ) {
  125. return true;
  126. }
  127. } else {
  128. result = currentRun != runCount - 1;
  129. }
  130. return result;
  131. }
  132. protected Color getColorForGap( int currentRun, int x, int y ) {
  133. final int shadowWidth = 4;
  134. int selectedIndex = tabPane.getSelectedIndex();
  135. int startIndex = tabRuns[ currentRun + 1 ];
  136. int endIndex = lastTabInRun( tabPane.getTabCount(), currentRun + 1 );
  137. int tabOverGap = -1;
  138. // Check each tab in the row that is 'on top' of this row
  139. for ( int i = startIndex; i <= endIndex; ++i ) {
  140. Rectangle tabBounds = getTabBounds( tabPane, i );
  141. int tabLeft = tabBounds.x;
  142. int tabRight = (tabBounds.x + tabBounds.width) - 1;
  143. // Check to see if this tab is over the gap
  144. if ( tabLeft <= x && tabRight - shadowWidth > x ) {
  145. return selectedIndex == i ? selectColor : tabPane.getBackgroundAt( i );
  146. }
  147. }
  148. return tabPane.getBackground();
  149. }
  150. protected void paintLeftTabBorder( int tabIndex, Graphics g,
  151. int x, int y, int w, int h,
  152. int btm, int rght,
  153. boolean isSelected ) {
  154. int tabCount = tabPane.getTabCount();
  155. int currentRun = getRunForTab( tabCount, tabIndex );
  156. int lastIndex = lastTabInRun( tabCount, currentRun );
  157. int firstIndex = tabRuns[ currentRun ];
  158. g.translate( x, y );
  159. int bottom = h - 1;
  160. int right = w - 1;
  161. //
  162. // Paint part of the tab above
  163. //
  164. if ( tabIndex != firstIndex ) {
  165. g.setColor( tabPane.getSelectedIndex() == tabIndex - 1 ?
  166. selectColor :
  167. tabPane.getBackgroundAt( tabIndex - 1 ) );
  168. g.fillRect( 2, 0, 4, 3 );
  169. g.drawLine( 2, 3, 2, 3 );
  170. }
  171. //
  172. // Paint Highlight
  173. //
  174. g.setColor( isSelected ? selectHighlight : highlight );
  175. // Paint slant
  176. g.drawLine( 1, 6, 6, 1 );
  177. // Paint top
  178. g.drawLine( 6, 1, right, 1 );
  179. // Paint left
  180. g.drawLine( 1, 6, 1, bottom );
  181. if ( tabIndex != firstIndex ) {
  182. g.setColor( tabPane.getSelectedIndex() == tabIndex - 1 ?
  183. selectHighlight :
  184. highlight );
  185. g.drawLine( 1, 0, 1, 4 );
  186. }
  187. //
  188. // Paint Border
  189. //
  190. g.setColor( darkShadow );
  191. // Paint slant
  192. g.drawLine( 1, 5, 6, 0 );
  193. // Paint top
  194. g.drawLine( 6, 0, right, 0 );
  195. // Paint left
  196. if ( tabIndex != firstIndex ) {
  197. g.drawLine( 0, 0, 0, bottom );
  198. } else {
  199. g.drawLine( 0, 6, 0, bottom );
  200. }
  201. // Paint bottom
  202. if ( tabIndex == lastIndex ) {
  203. g.drawLine( 0, bottom, right, bottom );
  204. }
  205. g.translate( -x, -y );
  206. }
  207. protected void paintBottomTabBorder( int tabIndex, Graphics g,
  208. int x, int y, int w, int h,
  209. int btm, int rght,
  210. boolean isSelected ) {
  211. int tabCount = tabPane.getTabCount();
  212. int currentRun = getRunForTab( tabCount, tabIndex );
  213. int lastIndex = lastTabInRun( tabCount, currentRun );
  214. int firstIndex = tabRuns[ currentRun ];
  215. int bottom = h - 1;
  216. int right = w - 1;
  217. //
  218. // Paint Gap
  219. //
  220. if ( shouldFillGap( currentRun, tabIndex, x, y ) ) {
  221. g.translate( x, y );
  222. g.setColor( getColorForGap( currentRun, x, y ) );
  223. g.fillRect( 1, bottom - 4, 3, 5 );
  224. g.fillRect( 4, bottom - 1, 2, 2 );
  225. g.translate( -x, -y );
  226. }
  227. g.translate( x, y );
  228. //
  229. // Paint Border
  230. //
  231. g.setColor( darkShadow );
  232. // Paint slant
  233. g.drawLine( 1, bottom - 5, 6, bottom );
  234. // Paint bottom
  235. g.drawLine( 6, bottom, right, bottom );
  236. // Paint right
  237. if ( tabIndex == lastIndex ) {
  238. g.drawLine( right, 0, right, bottom );
  239. }
  240. // Paint left
  241. if ( tabIndex != tabRuns[ runCount - 1 ] ) {
  242. g.drawLine( 0, 0, 0, bottom );
  243. } else {
  244. g.drawLine( 0, 0, 0, bottom - 6 );
  245. }
  246. //
  247. // Paint Highlight
  248. //
  249. g.setColor( isSelected ? selectHighlight : highlight );
  250. // Paint slant
  251. g.drawLine( 1, bottom - 6, 6, bottom - 1 );
  252. // Paint left
  253. g.drawLine( 1, 0, 1, bottom - 6 );
  254. if ( tabIndex == firstIndex && tabIndex != tabRuns[ runCount - 1 ] ) {
  255. g.setColor( tabPane.getSelectedIndex() == tabRuns[ currentRun + 1 ] ? selectHighlight : highlight );
  256. g.drawLine( 1, bottom - 4, 1, bottom );
  257. }
  258. g.translate( -x, -y );
  259. }
  260. protected void paintRightTabBorder( int tabIndex, Graphics g,
  261. int x, int y, int w, int h,
  262. int btm, int rght,
  263. boolean isSelected ) {
  264. int tabCount = tabPane.getTabCount();
  265. int currentRun = getRunForTab( tabCount, tabIndex );
  266. int lastIndex = lastTabInRun( tabCount, currentRun );
  267. int firstIndex = tabRuns[ currentRun ];
  268. g.translate( x, y );
  269. int bottom = h - 1;
  270. int right = w - 1;
  271. //
  272. // Paint part of the tab above
  273. //
  274. if ( tabIndex != firstIndex ) {
  275. g.setColor( tabPane.getSelectedIndex() == tabIndex - 1 ?
  276. tabAreaBackground :
  277. tabPane.getBackgroundAt( tabIndex - 1 ) );
  278. g.fillRect( right - 5, 0, 5, 3 );
  279. g.fillRect( right - 2, 3, 2, 2 );
  280. }
  281. //
  282. // Paint Highlight
  283. //
  284. g.setColor( isSelected ? selectHighlight : highlight );
  285. // Paint slant
  286. g.drawLine( right - 6, 1, right - 1, 6 );
  287. // Paint top
  288. g.drawLine( 0, 1, right - 6, 1 );
  289. // Paint left
  290. if ( !isSelected ) {
  291. g.drawLine( 0, 1, 0, bottom );
  292. }
  293. //
  294. // Paint Border
  295. //
  296. g.setColor( darkShadow );
  297. // Paint slant
  298. g.drawLine( right - 6, 0, right, 6 );
  299. // Paint top
  300. g.drawLine( 0, 0, right - 6, 0 );
  301. // Paint right
  302. if ( tabIndex != firstIndex ) {
  303. g.drawLine( right, 0, right, bottom );
  304. } else {
  305. g.drawLine( right, 6, right, bottom );
  306. }
  307. // Paint bottom
  308. if ( tabIndex == lastIndex ) {
  309. g.drawLine( 0, bottom, right, bottom );
  310. }
  311. g.translate( -x, -y );
  312. }
  313. public void update( Graphics g, JComponent c ) {
  314. if ( c.isOpaque() ) {
  315. g.setColor( tabAreaBackground );
  316. g.fillRect( 0, 0, c.getWidth(),c.getHeight() );
  317. }
  318. paint( g, c );
  319. }
  320. protected void paintTabBackground( Graphics g, int tabPlacement,
  321. int tabIndex, int x, int y, int w, int h, boolean isSelected ) {
  322. int slantWidth = h / 2;
  323. if ( isSelected ) {
  324. g.setColor( selectColor );
  325. } else {
  326. g.setColor( tabPane.getBackgroundAt( tabIndex ) );
  327. }
  328. switch ( tabPlacement ) {
  329. case LEFT:
  330. g.fillRect(x + 5, y + 1, w - 5, h - 1);
  331. g.fillRect( x + 2, y + 4, 3, h - 4 );
  332. break;
  333. case BOTTOM:
  334. g.fillRect( x + 2, y, w - 2, h - 3 );
  335. g.fillRect( x + 5, (y + (h - 1)) - 3, w - 5, 3 );
  336. break;
  337. case RIGHT:
  338. g.fillRect(x + 1, y + 1, w - 5, h - 1);
  339. g.fillRect( (x+(w-1)) - 3, y+5, 3, h - 5 );
  340. break;
  341. case TOP:
  342. default:
  343. g.fillRect( x + 4, y + 2, (w - 1) - 3, (h - 1) - 1 );
  344. g.fillRect( x + 2, y + 5, 2, h - 5 );
  345. }
  346. }
  347. /**
  348. * Overidden to do nothing for the Java L&F.
  349. */
  350. protected int getTabLabelShiftX( int tabPlacement, int tabIndex, boolean isSelected ) {
  351. return 0;
  352. }
  353. /**
  354. * Overidden to do nothing for the Java L&F.
  355. */
  356. protected int getTabLabelShiftY( int tabPlacement, int tabIndex, boolean isSelected ) {
  357. return 0;
  358. }
  359. public void paint( Graphics g, JComponent c ) {
  360. int tabPlacement = tabPane.getTabPlacement();
  361. Insets insets = c.getInsets(); Dimension size = c.getSize();
  362. // Paint the background for the tab area
  363. if ( tabPane.isOpaque() ) {
  364. g.setColor( c.getBackground() );
  365. switch ( tabPlacement ) {
  366. case LEFT:
  367. g.fillRect( insets.left, insets.top,
  368. calculateTabAreaWidth( tabPlacement, runCount, maxTabWidth ),
  369. size.height - insets.bottom - insets.top );
  370. break;
  371. case BOTTOM:
  372. int totalTabHeight = calculateTabAreaHeight( tabPlacement, runCount, maxTabHeight );
  373. g.fillRect( insets.left, size.height - insets.bottom - totalTabHeight,
  374. size.width - insets.left - insets.right,
  375. totalTabHeight );
  376. break;
  377. case RIGHT:
  378. int totalTabWidth = calculateTabAreaWidth( tabPlacement, runCount, maxTabWidth );
  379. g.fillRect( size.width - insets.right - totalTabWidth,
  380. insets.top, totalTabWidth,
  381. size.height - insets.top - insets.bottom );
  382. break;
  383. case TOP:
  384. default:
  385. g.fillRect( insets.left, insets.top,
  386. size.width - insets.right - insets.left,
  387. calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight) );
  388. paintHighlightBelowTab();
  389. }
  390. }
  391. super.paint( g, c );
  392. }
  393. protected void paintHighlightBelowTab( ) {
  394. }
  395. protected void paintFocusIndicator(Graphics g, int tabPlacement,
  396. Rectangle[] rects, int tabIndex,
  397. Rectangle iconRect, Rectangle textRect,
  398. boolean isSelected) {
  399. if ( tabPane.hasFocus() && isSelected ) {
  400. Rectangle tabRect = rects[tabIndex];
  401. g.setColor( focus );
  402. g.translate( tabRect.x, tabRect.y );
  403. int right = tabRect.width - 1;
  404. int bottom = tabRect.height - 1;
  405. switch ( tabPlacement ) {
  406. case RIGHT:
  407. g.drawLine( right - 6,2 , right - 2,6 ); // slant
  408. g.drawLine( 1,2 , right - 6,2 ); // top
  409. g.drawLine( right - 2,6 , right - 2,bottom ); // right
  410. g.drawLine( 1,2 , 1,bottom ); // left
  411. g.drawLine( 1,bottom , right - 2,bottom ); // bottom
  412. break;
  413. case BOTTOM:
  414. g.drawLine( 2,bottom - 6 , 6,bottom - 2 ); // slant
  415. g.drawLine( 6,bottom - 2 , right,bottom - 2 ); // bottom
  416. g.drawLine( 2,0 , 2,bottom - 6 ); // left
  417. g.drawLine( 2,0 , right,0 ); // top
  418. g.drawLine( right,0 , right,bottom - 2 ); // right
  419. break;
  420. case TOP:
  421. case LEFT:
  422. default:
  423. g.drawLine( 2,6 , 6,2 ); // slant
  424. g.drawLine( 2,6 , 2,bottom - 1); // left
  425. g.drawLine( 6,2 , right,2 ); // top
  426. g.drawLine( right,2 , right,bottom - 1 ); // right
  427. g.drawLine( 2,bottom - 1 , right, bottom - 1 ); // bottom
  428. }
  429. g.translate( -tabRect.x, -tabRect.y );
  430. }
  431. }
  432. protected void paintContentBorderTopEdge( Graphics g, int tabPlacement,
  433. int selectedIndex,
  434. int x, int y, int w, int h ) {
  435. g.setColor(selectHighlight);
  436. if (tabPlacement != TOP || selectedIndex < 0 ||
  437. (rects[selectedIndex].y + rects[selectedIndex].height + 1 < y)) {
  438. g.drawLine(x, y, x+w-2, y);
  439. } else {
  440. Rectangle selRect = rects[selectedIndex];
  441. g.drawLine(x, y, selRect.x + 1, y);
  442. if (selRect.x + selRect.width < x + w - 2) {
  443. g.drawLine(selRect.x + selRect.width, y,
  444. x+w-2, y);
  445. } else {
  446. g.setColor(shadow);
  447. g.drawLine(x+w-2, y, x+w-2, y);
  448. }
  449. }
  450. }
  451. protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
  452. int selectedIndex,
  453. int x, int y, int w, int h) {
  454. g.setColor(shadow);
  455. if (tabPlacement != BOTTOM || selectedIndex < 0 ||
  456. (rects[selectedIndex].y - 1 > h)) {
  457. g.setColor(darkShadow);
  458. g.drawLine(x, y+h-1, x+w-1, y+h-1);
  459. } else {
  460. Rectangle selRect = rects[selectedIndex];
  461. g.setColor(darkShadow);
  462. g.drawLine(x, y+h-1, selRect.x, y+h-1);
  463. if (selRect.x + selRect.width < x + w - 2) {
  464. g.setColor(darkShadow);
  465. g.drawLine(selRect.x + selRect.width, y+h-1, x+w-1, y+h-1);
  466. }
  467. }
  468. }
  469. protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
  470. int selectedIndex,
  471. int x, int y, int w, int h) {
  472. g.setColor(selectHighlight);
  473. if (tabPlacement != LEFT || selectedIndex < 0 ||
  474. (rects[selectedIndex].x + rects[selectedIndex].width + 1< x)) {
  475. g.drawLine(x, y, x, y+h-2);
  476. } else {
  477. Rectangle selRect = rects[selectedIndex];
  478. g.drawLine(x, y, x, selRect.y + 1);
  479. if (selRect.y + selRect.height < y + h - 2) {
  480. g.drawLine(x, selRect.y + selRect.height + 1,
  481. x, y+h+2);
  482. }
  483. }
  484. }
  485. protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
  486. int selectedIndex,
  487. int x, int y, int w, int h) {
  488. g.setColor(shadow);
  489. if (tabPlacement != RIGHT || selectedIndex < 0 ||
  490. rects[selectedIndex].x - 1 > w) {
  491. g.setColor(darkShadow);
  492. g.drawLine(x+w-1, y, x+w-1, y+h-1);
  493. } else {
  494. Rectangle selRect = rects[selectedIndex];
  495. g.setColor(darkShadow);
  496. g.drawLine(x+w-1, y, x+w-1, selRect.y);
  497. if (selRect.y + selRect.height < y + h - 2) {
  498. g.setColor(darkShadow);
  499. g.drawLine(x+w-1, selRect.y + selRect.height,
  500. x+w-1, y+h-2);
  501. }
  502. }
  503. }
  504. protected int calculateMaxTabHeight( int tabPlacement ) {
  505. FontMetrics metrics = getFontMetrics();
  506. int height = metrics.getHeight();
  507. boolean tallerIcons = false;
  508. for ( int i = 0; i < tabPane.getTabCount(); ++i ) {
  509. Icon icon = tabPane.getIconAt( i );
  510. if ( icon != null ) {
  511. if ( icon.getIconHeight() > height ) {
  512. tallerIcons = true;
  513. break;
  514. }
  515. }
  516. }
  517. return super.calculateMaxTabHeight( tabPlacement ) -
  518. (tallerIcons ? (tabInsets.top + tabInsets.bottom) : 0);
  519. }
  520. protected int getTabRunOverlay( int tabPlacement ) {
  521. // Tab runs layed out vertically should overlap
  522. // at least as much as the largest slant
  523. if ( tabPlacement == LEFT || tabPlacement == RIGHT ) {
  524. int maxTabHeight = calculateMaxTabHeight(tabPlacement);
  525. return maxTabHeight / 2;
  526. }
  527. return 0;
  528. }
  529. // Don't rotate runs!
  530. protected boolean shouldRotateTabRuns( int tabPlacement, int selectedRun ) {
  531. return false;
  532. }
  533. // Don't pad last run
  534. protected boolean shouldPadTabRun( int tabPlacement, int run ) {
  535. return runCount > 1 && run < runCount - 1;
  536. }
  537. /**
  538. * This inner class is marked "public" due to a compiler bug.
  539. * This class should be treated as a "protected" inner class.
  540. * Instantiate it only within subclasses of MetalTabbedPaneUI.
  541. */
  542. public class TabbedPaneLayout extends BasicTabbedPaneUI.TabbedPaneLayout {
  543. protected void normalizeTabRuns( int tabPlacement, int tabCount,
  544. int start, int max ) {
  545. // Only normalize the runs for top & bottom; normalizing
  546. // doesn't look right for Metal's vertical tabs
  547. // because the last run isn't padded and it looks odd to have
  548. // fat tabs in the first vertical runs, but slimmer ones in the
  549. // last (this effect isn't noticable for horizontal tabs).
  550. if ( tabPlacement == TOP || tabPlacement == BOTTOM ) {
  551. super.normalizeTabRuns( tabPlacement, tabCount, start, max );
  552. }
  553. }
  554. // Don't rotate runs!
  555. protected void rotateTabRuns( int tabPlacement, int selectedRun ) {
  556. }
  557. // Don't pad selected tab
  558. protected void padSelectedTab( int tabPlacement, int selectedIndex ) {
  559. }
  560. }
  561. }