1. /*
  2. * @(#)MetalTabbedPaneUI.java 1.31 03/01/23
  3. *
  4. * Copyright 2003 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
  21. * appropriate for short term storage or RMI between applications running
  22. * the same version of Swing. As of 1.4, support for long term storage
  23. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  24. * has been added to the <code>java.beans</code> package.
  25. * Please see {@link java.beans.XMLEncoder}.
  26. *
  27. * @version 1.19 08/28/98
  28. * @author Tom Santos
  29. */
  30. public class MetalTabbedPaneUI extends BasicTabbedPaneUI {
  31. protected int minTabWidth = 40;
  32. protected Color tabAreaBackground;
  33. protected Color selectColor;
  34. protected Color selectHighlight;
  35. public static ComponentUI createUI( JComponent x ) {
  36. return new MetalTabbedPaneUI();
  37. }
  38. protected LayoutManager createLayoutManager() {
  39. if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
  40. return super.createLayoutManager();
  41. }
  42. return new TabbedPaneLayout();
  43. }
  44. protected void installDefaults() {
  45. super.installDefaults();
  46. tabAreaBackground = UIManager.getColor("TabbedPane.tabAreaBackground");
  47. selectColor = UIManager.getColor("TabbedPane.selected");
  48. selectHighlight = UIManager.getColor("TabbedPane.selectHighlight");
  49. }
  50. protected void paintTabBorder( Graphics g, int tabPlacement,
  51. int tabIndex, int x, int y, int w, int h,
  52. boolean isSelected) {
  53. int bottom = y + (h-1);
  54. int right = x + (w-1);
  55. switch ( tabPlacement ) {
  56. case LEFT:
  57. paintLeftTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
  58. break;
  59. case BOTTOM:
  60. paintBottomTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
  61. break;
  62. case RIGHT:
  63. paintRightTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
  64. break;
  65. case TOP:
  66. default:
  67. paintTopTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
  68. }
  69. }
  70. protected void paintTopTabBorder( int tabIndex, Graphics g,
  71. int x, int y, int w, int h,
  72. int btm, int rght,
  73. boolean isSelected ) {
  74. int currentRun = getRunForTab( tabPane.getTabCount(), tabIndex );
  75. int lastIndex = lastTabInRun( tabPane.getTabCount(), currentRun );
  76. int firstIndex = tabRuns[ currentRun ];
  77. boolean leftToRight = MetalUtils.isLeftToRight(tabPane);
  78. int bottom = h - 1;
  79. int right = w - 1;
  80. //
  81. // Paint Gap
  82. //
  83. if ( shouldFillGap( currentRun, tabIndex, x, y ) ) {
  84. g.translate( x, y );
  85. if ( leftToRight ) {
  86. g.setColor( getColorForGap( currentRun, x, y + 1 ) );
  87. g.fillRect( 1, 0, 5, 3 );
  88. g.fillRect( 1, 3, 2, 2 );
  89. } else {
  90. g.setColor( getColorForGap( currentRun, x + w - 1, y + 1 ) );
  91. g.fillRect( right - 5, 0, 5, 3 );
  92. g.fillRect( right - 2, 3, 2, 2 );
  93. }
  94. g.translate( -x, -y );
  95. }
  96. g.translate( x, y );
  97. //
  98. // Paint Border
  99. //
  100. g.setColor( darkShadow );
  101. if ( leftToRight ) {
  102. // Paint slant
  103. g.drawLine( 1, 5, 6, 0 );
  104. // Paint top
  105. g.drawLine( 6, 0, right, 0 );
  106. // Paint right
  107. if ( tabIndex==lastIndex ) {
  108. // last tab in run
  109. g.drawLine( right, 1, right, bottom );
  110. }
  111. // Paint left
  112. if ( tabIndex != tabRuns[ runCount - 1 ] ) {
  113. // not the first tab in the last run
  114. g.drawLine( 0, 0, 0, bottom );
  115. } else {
  116. // the first tab in the last run
  117. g.drawLine( 0, 6, 0, bottom );
  118. }
  119. } else {
  120. // Paint slant
  121. g.drawLine( right - 1, 5, right - 6, 0 );
  122. // Paint top
  123. g.drawLine( right - 6, 0, 0, 0 );
  124. // Paint right
  125. if ( tabIndex != tabRuns[ runCount - 1 ] ) {
  126. // not the first tab in the last run
  127. g.drawLine( right, 0, right, bottom );
  128. } else {
  129. // the first tab in the last run
  130. g.drawLine( right, 6, right, bottom );
  131. }
  132. // Paint left
  133. if ( tabIndex==lastIndex ) {
  134. // last tab in run
  135. g.drawLine( 0, 1, 0, bottom );
  136. }
  137. }
  138. //
  139. // Paint Highlight
  140. //
  141. g.setColor( isSelected ? selectHighlight : highlight );
  142. if ( leftToRight ) {
  143. // Paint slant
  144. g.drawLine( 1, 6, 6, 1 );
  145. // Paint top
  146. g.drawLine( 6, 1, right, 1 );
  147. // Paint left
  148. g.drawLine( 1, 6, 1, bottom );
  149. // paint highlight in the gap on tab behind this one
  150. // on the left end (where they all line up)
  151. if ( tabIndex==firstIndex && tabIndex!=tabRuns[runCount - 1] ) {
  152. // first tab in run but not first tab in last run
  153. if (tabPane.getSelectedIndex()==tabRuns[currentRun+1]) {
  154. // tab in front of selected tab
  155. g.setColor( selectHighlight );
  156. }
  157. else {
  158. // tab in front of normal tab
  159. g.setColor( highlight );
  160. }
  161. g.drawLine( 1, 0, 1, 4 );
  162. }
  163. } else {
  164. // Paint slant
  165. g.drawLine( right - 1, 6, right - 6, 1 );
  166. // Paint top
  167. g.drawLine( right - 6, 1, 1, 1 );
  168. // Paint left
  169. if ( tabIndex==lastIndex ) {
  170. // last tab in run
  171. g.drawLine( 1, 1, 1, bottom );
  172. } else {
  173. g.drawLine( 0, 1, 0, bottom );
  174. }
  175. }
  176. g.translate( -x, -y );
  177. }
  178. protected boolean shouldFillGap( int currentRun, int tabIndex, int x, int y ) {
  179. boolean result = false;
  180. if ( currentRun == runCount - 2 ) { // If it's the second to last row.
  181. Rectangle lastTabBounds = getTabBounds( tabPane, tabPane.getTabCount() - 1 );
  182. Rectangle tabBounds = getTabBounds( tabPane, tabIndex );
  183. if (MetalUtils.isLeftToRight(tabPane)) {
  184. int lastTabRight = lastTabBounds.x + lastTabBounds.width - 1;
  185. // is the right edge of the last tab to the right
  186. // of the left edge of the current tab?
  187. if ( lastTabRight > tabBounds.x + 2 ) {
  188. return true;
  189. }
  190. } else {
  191. int lastTabLeft = lastTabBounds.x;
  192. int currentTabRight = tabBounds.x + tabBounds.width - 1;
  193. // is the left edge of the last tab to the left
  194. // of the right edge of the current tab?
  195. if ( lastTabLeft < currentTabRight - 2 ) {
  196. return true;
  197. }
  198. }
  199. } else {
  200. // fill in gap for all other rows except last row
  201. result = currentRun != runCount - 1;
  202. }
  203. return result;
  204. }
  205. protected Color getColorForGap( int currentRun, int x, int y ) {
  206. final int shadowWidth = 4;
  207. int selectedIndex = tabPane.getSelectedIndex();
  208. int startIndex = tabRuns[ currentRun + 1 ];
  209. int endIndex = lastTabInRun( tabPane.getTabCount(), currentRun + 1 );
  210. int tabOverGap = -1;
  211. // Check each tab in the row that is 'on top' of this row
  212. for ( int i = startIndex; i <= endIndex; ++i ) {
  213. Rectangle tabBounds = getTabBounds( tabPane, i );
  214. int tabLeft = tabBounds.x;
  215. int tabRight = (tabBounds.x + tabBounds.width) - 1;
  216. // Check to see if this tab is over the gap
  217. if ( MetalUtils.isLeftToRight(tabPane) ) {
  218. if ( tabLeft <= x && tabRight - shadowWidth > x ) {
  219. return selectedIndex == i ? selectColor : tabPane.getBackgroundAt( i );
  220. }
  221. }
  222. else {
  223. if ( tabLeft + shadowWidth < x && tabRight >= x ) {
  224. return selectedIndex == i ? selectColor : tabPane.getBackgroundAt( i );
  225. }
  226. }
  227. }
  228. return tabPane.getBackground();
  229. }
  230. protected void paintLeftTabBorder( int tabIndex, Graphics g,
  231. int x, int y, int w, int h,
  232. int btm, int rght,
  233. boolean isSelected ) {
  234. int tabCount = tabPane.getTabCount();
  235. int currentRun = getRunForTab( tabCount, tabIndex );
  236. int lastIndex = lastTabInRun( tabCount, currentRun );
  237. int firstIndex = tabRuns[ currentRun ];
  238. g.translate( x, y );
  239. int bottom = h - 1;
  240. int right = w - 1;
  241. //
  242. // Paint part of the tab above
  243. //
  244. if ( tabIndex != firstIndex ) {
  245. g.setColor( tabPane.getSelectedIndex() == tabIndex - 1 ?
  246. selectColor :
  247. tabPane.getBackgroundAt( tabIndex - 1 ) );
  248. g.fillRect( 2, 0, 4, 3 );
  249. g.drawLine( 2, 3, 2, 3 );
  250. }
  251. //
  252. // Paint Highlight
  253. //
  254. g.setColor( isSelected ? selectHighlight : highlight );
  255. // Paint slant
  256. g.drawLine( 1, 6, 6, 1 );
  257. // Paint top
  258. g.drawLine( 6, 1, right, 1 );
  259. // Paint left
  260. g.drawLine( 1, 6, 1, bottom );
  261. if ( tabIndex != firstIndex ) {
  262. g.setColor( tabPane.getSelectedIndex() == tabIndex - 1 ?
  263. selectHighlight :
  264. highlight );
  265. g.drawLine( 1, 0, 1, 4 );
  266. }
  267. //
  268. // Paint Border
  269. //
  270. g.setColor( darkShadow );
  271. // Paint slant
  272. g.drawLine( 1, 5, 6, 0 );
  273. // Paint top
  274. g.drawLine( 6, 0, right, 0 );
  275. // Paint left
  276. if ( tabIndex != firstIndex ) {
  277. g.drawLine( 0, 0, 0, bottom );
  278. } else {
  279. g.drawLine( 0, 6, 0, bottom );
  280. }
  281. // Paint bottom
  282. if ( tabIndex == lastIndex ) {
  283. g.drawLine( 0, bottom, right, bottom );
  284. }
  285. g.translate( -x, -y );
  286. }
  287. protected void paintBottomTabBorder( int tabIndex, Graphics g,
  288. int x, int y, int w, int h,
  289. int btm, int rght,
  290. boolean isSelected ) {
  291. int tabCount = tabPane.getTabCount();
  292. int currentRun = getRunForTab( tabCount, tabIndex );
  293. int lastIndex = lastTabInRun( tabCount, currentRun );
  294. int firstIndex = tabRuns[ currentRun ];
  295. boolean leftToRight = MetalUtils.isLeftToRight(tabPane);
  296. int bottom = h - 1;
  297. int right = w - 1;
  298. //
  299. // Paint Gap
  300. //
  301. if ( shouldFillGap( currentRun, tabIndex, x, y ) ) {
  302. g.translate( x, y );
  303. if ( leftToRight ) {
  304. g.setColor( getColorForGap( currentRun, x, y ) );
  305. g.fillRect( 1, bottom - 4, 3, 5 );
  306. g.fillRect( 4, bottom - 1, 2, 2 );
  307. } else {
  308. g.setColor( getColorForGap( currentRun, x + w - 1, y ) );
  309. g.fillRect( right - 3, bottom - 3, 3, 4 );
  310. g.fillRect( right - 5, bottom - 1, 2, 2 );
  311. g.drawLine( right - 1, bottom - 4, right - 1, bottom - 4 );
  312. }
  313. g.translate( -x, -y );
  314. }
  315. g.translate( x, y );
  316. //
  317. // Paint Border
  318. //
  319. g.setColor( darkShadow );
  320. if ( leftToRight ) {
  321. // Paint slant
  322. g.drawLine( 1, bottom - 5, 6, bottom );
  323. // Paint bottom
  324. g.drawLine( 6, bottom, right, bottom );
  325. // Paint right
  326. if ( tabIndex == lastIndex ) {
  327. g.drawLine( right, 0, right, bottom );
  328. }
  329. // Paint left
  330. if ( tabIndex != tabRuns[ runCount - 1 ] ) {
  331. g.drawLine( 0, 0, 0, bottom );
  332. } else {
  333. g.drawLine( 0, 0, 0, bottom - 6 );
  334. }
  335. } else {
  336. // Paint slant
  337. g.drawLine( right - 1, bottom - 5, right - 6, bottom );
  338. // Paint bottom
  339. g.drawLine( right - 6, bottom, 0, bottom );
  340. // Paint right
  341. if ( tabIndex != tabRuns[ runCount - 1 ] ) {
  342. // not the first tab in the last run
  343. g.drawLine( right, 0, right, bottom );
  344. } else {
  345. // the first tab in the last run
  346. g.drawLine( right, 0, right, bottom - 6 );
  347. }
  348. // Paint left
  349. if ( tabIndex==lastIndex ) {
  350. // last tab in run
  351. g.drawLine( 0, 0, 0, bottom );
  352. }
  353. }
  354. //
  355. // Paint Highlight
  356. //
  357. g.setColor( isSelected ? selectHighlight : highlight );
  358. if ( leftToRight ) {
  359. // Paint slant
  360. g.drawLine( 1, bottom - 6, 6, bottom - 1 );
  361. // Paint left
  362. g.drawLine( 1, 0, 1, bottom - 6 );
  363. // paint highlight in the gap on tab behind this one
  364. // on the left end (where they all line up)
  365. if ( tabIndex==firstIndex && tabIndex!=tabRuns[runCount - 1] ) {
  366. // first tab in run but not first tab in last run
  367. if (tabPane.getSelectedIndex()==tabRuns[currentRun+1]) {
  368. // tab in front of selected tab
  369. g.setColor( selectHighlight );
  370. }
  371. else {
  372. // tab in front of normal tab
  373. g.setColor( highlight );
  374. }
  375. g.drawLine( 1, bottom - 4, 1, bottom );
  376. }
  377. } else {
  378. // Paint left
  379. if ( tabIndex==lastIndex ) {
  380. // last tab in run
  381. g.drawLine( 1, 0, 1, bottom - 1 );
  382. } else {
  383. g.drawLine( 0, 0, 0, bottom - 1 );
  384. }
  385. }
  386. g.translate( -x, -y );
  387. }
  388. protected void paintRightTabBorder( int tabIndex, Graphics g,
  389. int x, int y, int w, int h,
  390. int btm, int rght,
  391. boolean isSelected ) {
  392. int tabCount = tabPane.getTabCount();
  393. int currentRun = getRunForTab( tabCount, tabIndex );
  394. int lastIndex = lastTabInRun( tabCount, currentRun );
  395. int firstIndex = tabRuns[ currentRun ];
  396. g.translate( x, y );
  397. int bottom = h - 1;
  398. int right = w - 1;
  399. //
  400. // Paint part of the tab above
  401. //
  402. if ( tabIndex != firstIndex ) {
  403. g.setColor( tabPane.getSelectedIndex() == tabIndex - 1 ?
  404. tabAreaBackground :
  405. tabPane.getBackgroundAt( tabIndex - 1 ) );
  406. g.fillRect( right - 5, 0, 5, 3 );
  407. g.fillRect( right - 2, 3, 2, 2 );
  408. }
  409. //
  410. // Paint Highlight
  411. //
  412. g.setColor( isSelected ? selectHighlight : highlight );
  413. // Paint slant
  414. g.drawLine( right - 6, 1, right - 1, 6 );
  415. // Paint top
  416. g.drawLine( 0, 1, right - 6, 1 );
  417. // Paint left
  418. if ( !isSelected ) {
  419. g.drawLine( 0, 1, 0, bottom );
  420. }
  421. //
  422. // Paint Border
  423. //
  424. g.setColor( darkShadow );
  425. // Paint slant
  426. g.drawLine( right - 6, 0, right, 6 );
  427. // Paint top
  428. g.drawLine( 0, 0, right - 6, 0 );
  429. // Paint right
  430. if ( tabIndex != firstIndex ) {
  431. g.drawLine( right, 0, right, bottom );
  432. } else {
  433. g.drawLine( right, 6, right, bottom );
  434. }
  435. // Paint bottom
  436. if ( tabIndex == lastIndex ) {
  437. g.drawLine( 0, bottom, right, bottom );
  438. }
  439. g.translate( -x, -y );
  440. }
  441. public void update( Graphics g, JComponent c ) {
  442. if ( c.isOpaque() ) {
  443. g.setColor( tabAreaBackground );
  444. g.fillRect( 0, 0, c.getWidth(),c.getHeight() );
  445. }
  446. paint( g, c );
  447. }
  448. protected void paintTabBackground( Graphics g, int tabPlacement,
  449. int tabIndex, int x, int y, int w, int h, boolean isSelected ) {
  450. int slantWidth = h / 2;
  451. if ( isSelected ) {
  452. g.setColor( selectColor );
  453. } else {
  454. g.setColor( tabPane.getBackgroundAt( tabIndex ) );
  455. }
  456. if (MetalUtils.isLeftToRight(tabPane)) {
  457. switch ( tabPlacement ) {
  458. case LEFT:
  459. g.fillRect( x + 5, y + 1, w - 5, h - 1);
  460. g.fillRect( x + 2, y + 4, 3, h - 4 );
  461. break;
  462. case BOTTOM:
  463. g.fillRect( x + 2, y, w - 2, h - 4 );
  464. g.fillRect( x + 5, y + (h - 1) - 3, w - 5, 3 );
  465. break;
  466. case RIGHT:
  467. g.fillRect( x + 1, y + 1, w - 5, h - 1);
  468. g.fillRect( x + (w - 1) - 3, y + 5, 3, h - 5 );
  469. break;
  470. case TOP:
  471. default:
  472. g.fillRect( x + 4, y + 2, (w - 1) - 3, (h - 1) - 1 );
  473. g.fillRect( x + 2, y + 5, 2, h - 5 );
  474. }
  475. } else {
  476. switch ( tabPlacement ) {
  477. case LEFT:
  478. g.fillRect( x + 5, y + 1, w - 5, h - 1);
  479. g.fillRect( x + 2, y + 4, 3, h - 4 );
  480. break;
  481. case BOTTOM:
  482. g.fillRect( x, y, w - 5, h - 1 );
  483. g.fillRect( x + (w - 1) - 4, y, 4, h - 5);
  484. g.fillRect( x + (w - 1) - 4, y + (h - 1) - 4, 2, 2);
  485. break;
  486. case RIGHT:
  487. g.fillRect( x + 1, y + 1, w - 5, h - 1);
  488. g.fillRect( x + (w - 1) - 3, y + 5, 3, h - 5 );
  489. break;
  490. case TOP:
  491. default:
  492. g.fillRect( x, y + 2, (w - 1) - 3, (h - 1) - 1 );
  493. g.fillRect( x + (w - 1) - 3, y + 4, 3, h - 4 );
  494. }
  495. }
  496. }
  497. /**
  498. * Overridden to do nothing for the Java L&F.
  499. */
  500. protected int getTabLabelShiftX( int tabPlacement, int tabIndex, boolean isSelected ) {
  501. return 0;
  502. }
  503. /**
  504. * Overridden to do nothing for the Java L&F.
  505. */
  506. protected int getTabLabelShiftY( int tabPlacement, int tabIndex, boolean isSelected ) {
  507. return 0;
  508. }
  509. public void paint( Graphics g, JComponent c ) {
  510. int tabPlacement = tabPane.getTabPlacement();
  511. Insets insets = c.getInsets(); Dimension size = c.getSize();
  512. // Paint the background for the tab area
  513. if ( tabPane.isOpaque() ) {
  514. g.setColor( c.getBackground() );
  515. switch ( tabPlacement ) {
  516. case LEFT:
  517. g.fillRect( insets.left, insets.top,
  518. calculateTabAreaWidth( tabPlacement, runCount, maxTabWidth ),
  519. size.height - insets.bottom - insets.top );
  520. break;
  521. case BOTTOM:
  522. int totalTabHeight = calculateTabAreaHeight( tabPlacement, runCount, maxTabHeight );
  523. g.fillRect( insets.left, size.height - insets.bottom - totalTabHeight,
  524. size.width - insets.left - insets.right,
  525. totalTabHeight );
  526. break;
  527. case RIGHT:
  528. int totalTabWidth = calculateTabAreaWidth( tabPlacement, runCount, maxTabWidth );
  529. g.fillRect( size.width - insets.right - totalTabWidth,
  530. insets.top, totalTabWidth,
  531. size.height - insets.top - insets.bottom );
  532. break;
  533. case TOP:
  534. default:
  535. g.fillRect( insets.left, insets.top,
  536. size.width - insets.right - insets.left,
  537. calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight) );
  538. paintHighlightBelowTab();
  539. }
  540. }
  541. super.paint( g, c );
  542. }
  543. protected void paintHighlightBelowTab( ) {
  544. }
  545. protected void paintFocusIndicator(Graphics g, int tabPlacement,
  546. Rectangle[] rects, int tabIndex,
  547. Rectangle iconRect, Rectangle textRect,
  548. boolean isSelected) {
  549. if ( tabPane.hasFocus() && isSelected ) {
  550. Rectangle tabRect = rects[tabIndex];
  551. boolean lastInRun = isLastInRun( tabIndex );
  552. g.setColor( focus );
  553. g.translate( tabRect.x, tabRect.y );
  554. int right = tabRect.width - 1;
  555. int bottom = tabRect.height - 1;
  556. boolean leftToRight = MetalUtils.isLeftToRight(tabPane);
  557. switch ( tabPlacement ) {
  558. case RIGHT:
  559. g.drawLine( right - 6,2 , right - 2,6 ); // slant
  560. g.drawLine( 1,2 , right - 6,2 ); // top
  561. g.drawLine( right - 2,6 , right - 2,bottom ); // right
  562. g.drawLine( 1,2 , 1,bottom ); // left
  563. g.drawLine( 1,bottom , right - 2,bottom ); // bottom
  564. break;
  565. case BOTTOM:
  566. if ( leftToRight ) {
  567. g.drawLine( 2, bottom - 6, 6, bottom - 2 ); // slant
  568. g.drawLine( 6, bottom - 2,
  569. right, bottom - 2 ); // bottom
  570. g.drawLine( 2, 0, 2, bottom - 6 ); // left
  571. g.drawLine( 2, 0, right, 0 ); // top
  572. g.drawLine( right, 0, right, bottom - 2 ); // right
  573. } else {
  574. g.drawLine( right - 2, bottom - 6,
  575. right - 6, bottom - 2 ); // slant
  576. g.drawLine( right - 2, 0,
  577. right - 2, bottom - 6 ); // right
  578. if ( lastInRun ) {
  579. // last tab in run
  580. g.drawLine( 2, bottom - 2,
  581. right - 6, bottom - 2 ); // bottom
  582. g.drawLine( 2, 0, right - 2, 0 ); // top
  583. g.drawLine( 2, 0, 2, bottom - 2 ); // left
  584. } else {
  585. g.drawLine( 1, bottom - 2,
  586. right - 6, bottom - 2 ); // bottom
  587. g.drawLine( 1, 0, right - 2, 0 ); // top
  588. g.drawLine( 1, 0, 1, bottom - 2 ); // left
  589. }
  590. }
  591. break;
  592. case LEFT:
  593. g.drawLine( 2, 6, 6, 2 ); // slant
  594. g.drawLine( 2, 6, 2, bottom - 1); // left
  595. g.drawLine( 6, 2, right, 2 ); // top
  596. g.drawLine( right, 2, right, bottom - 1 ); // right
  597. g.drawLine( 2, bottom - 1,
  598. right, bottom - 1 ); // bottom
  599. break;
  600. case TOP:
  601. default:
  602. if ( leftToRight ) {
  603. g.drawLine( 2, 6, 6, 2 ); // slant
  604. g.drawLine( 2, 6, 2, bottom - 1); // left
  605. g.drawLine( 6, 2, right, 2 ); // top
  606. g.drawLine( right, 2, right, bottom - 1 ); // right
  607. g.drawLine( 2, bottom - 1,
  608. right, bottom - 1 ); // bottom
  609. }
  610. else {
  611. g.drawLine( right - 2, 6, right - 6, 2 ); // slant
  612. g.drawLine( right - 2, 6,
  613. right - 2, bottom - 1); // right
  614. if ( lastInRun ) {
  615. // last tab in run
  616. g.drawLine( right - 6, 2, 2, 2 ); // top
  617. g.drawLine( 2, 2, 2, bottom - 1 ); // left
  618. g.drawLine( right - 2, bottom - 1,
  619. 2, bottom - 1 ); // bottom
  620. }
  621. else {
  622. g.drawLine( right - 6, 2, 1, 2 ); // top
  623. g.drawLine( 1, 2, 1, bottom - 1 ); // left
  624. g.drawLine( right - 2, bottom - 1,
  625. 1, bottom - 1 ); // bottom
  626. }
  627. }
  628. }
  629. g.translate( -tabRect.x, -tabRect.y );
  630. }
  631. }
  632. protected void paintContentBorderTopEdge( Graphics g, int tabPlacement,
  633. int selectedIndex,
  634. int x, int y, int w, int h ) {
  635. boolean leftToRight = MetalUtils.isLeftToRight(tabPane);
  636. int right = x + w - 1;
  637. Rectangle selRect = selectedIndex < 0? null :
  638. getTabBounds(selectedIndex, calcRect);
  639. g.setColor(selectHighlight);
  640. // Draw unbroken line if tabs are not on TOP, OR
  641. // selected tab is not in run adjacent to content, OR
  642. // selected tab is not visible (SCROLL_TAB_LAYOUT)
  643. //
  644. if (tabPlacement != TOP || selectedIndex < 0 ||
  645. (selRect.y + selRect.height + 1 < y) ||
  646. (selRect.x < x || selRect.x > x + w)) {
  647. g.drawLine(x, y, x+w-2, y);
  648. } else {
  649. // Break line to show visual connection to selected tab
  650. boolean lastInRun = isLastInRun(selectedIndex);
  651. if ( leftToRight || lastInRun ) {
  652. g.drawLine(x, y, selRect.x + 1, y);
  653. } else {
  654. g.drawLine(x, y, selRect.x, y);
  655. }
  656. if (selRect.x + selRect.width < right - 1) {
  657. if ( leftToRight && !lastInRun ) {
  658. g.drawLine(selRect.x + selRect.width, y, right - 1, y);
  659. } else {
  660. g.drawLine(selRect.x + selRect.width - 1, y, right - 1, y);
  661. }
  662. } else {
  663. g.setColor(shadow);
  664. g.drawLine(x+w-2, y, x+w-2, y);
  665. }
  666. }
  667. }
  668. protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
  669. int selectedIndex,
  670. int x, int y, int w, int h) {
  671. boolean leftToRight = MetalUtils.isLeftToRight(tabPane);
  672. int bottom = y + h - 1;
  673. int right = x + w - 1;
  674. Rectangle selRect = selectedIndex < 0? null :
  675. getTabBounds(selectedIndex, calcRect);
  676. g.setColor(shadow);
  677. // Draw unbroken line if tabs are not on BOTTOM, OR
  678. // selected tab is not in run adjacent to content, OR
  679. // selected tab is not visible (SCROLL_TAB_LAYOUT)
  680. //
  681. if (tabPlacement != BOTTOM || selectedIndex < 0 ||
  682. (selRect.y - 1 > h) ||
  683. (selRect.x < x || selRect.x > x + w)) {
  684. g.setColor(darkShadow);
  685. g.drawLine(x, y+h-1, x+w-1, y+h-1);
  686. } else {
  687. // Break line to show visual connection to selected tab
  688. boolean lastInRun = isLastInRun(selectedIndex);
  689. g.setColor(darkShadow);
  690. if ( leftToRight || lastInRun ) {
  691. g.drawLine(x, bottom, selRect.x, bottom);
  692. } else {
  693. g.drawLine(x, bottom, selRect.x - 1, bottom);
  694. }
  695. if (selRect.x + selRect.width < x + w - 2) {
  696. if ( leftToRight && !lastInRun ) {
  697. g.drawLine(selRect.x + selRect.width, bottom,
  698. right, bottom);
  699. } else {
  700. g.drawLine(selRect.x + selRect.width - 1, bottom,
  701. right, bottom);
  702. }
  703. }
  704. }
  705. }
  706. protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
  707. int selectedIndex,
  708. int x, int y, int w, int h) {
  709. Rectangle selRect = selectedIndex < 0? null :
  710. getTabBounds(selectedIndex, calcRect);
  711. g.setColor(selectHighlight);
  712. // Draw unbroken line if tabs are not on LEFT, OR
  713. // selected tab is not in run adjacent to content, OR
  714. // selected tab is not visible (SCROLL_TAB_LAYOUT)
  715. //
  716. if (tabPlacement != LEFT || selectedIndex < 0 ||
  717. (selRect.x + selRect.width + 1 < x) ||
  718. (selRect.y < y || selRect.y > y + h)) {
  719. g.drawLine(x, y, x, y+h-2);
  720. } else {
  721. // Break line to show visual connection to selected tab
  722. g.drawLine(x, y, x, selRect.y + 1);
  723. if (selRect.y + selRect.height < y + h - 2) {
  724. g.drawLine(x, selRect.y + selRect.height + 1,
  725. x, y+h+2);
  726. }
  727. }
  728. }
  729. protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
  730. int selectedIndex,
  731. int x, int y, int w, int h) {
  732. Rectangle selRect = selectedIndex < 0? null :
  733. getTabBounds(selectedIndex, calcRect);
  734. g.setColor(shadow);
  735. // Draw unbroken line if tabs are not on RIGHT, OR
  736. // selected tab is not in run adjacent to content, OR
  737. // selected tab is not visible (SCROLL_TAB_LAYOUT)
  738. //
  739. if (tabPlacement != RIGHT || selectedIndex < 0 ||
  740. (selRect.x - 1 > w) ||
  741. (selRect.y < y || selRect.y > y + h)) {
  742. g.setColor(darkShadow);
  743. g.drawLine(x+w-1, y, x+w-1, y+h-1);
  744. } else {
  745. // Break line to show visual connection to selected tab
  746. g.setColor(darkShadow);
  747. g.drawLine(x+w-1, y, x+w-1, selRect.y);
  748. if (selRect.y + selRect.height < y + h - 2) {
  749. g.setColor(darkShadow);
  750. g.drawLine(x+w-1, selRect.y + selRect.height,
  751. x+w-1, y+h-2);
  752. }
  753. }
  754. }
  755. protected int calculateMaxTabHeight( int tabPlacement ) {
  756. FontMetrics metrics = getFontMetrics();
  757. int height = metrics.getHeight();
  758. boolean tallerIcons = false;
  759. for ( int i = 0; i < tabPane.getTabCount(); ++i ) {
  760. Icon icon = tabPane.getIconAt( i );
  761. if ( icon != null ) {
  762. if ( icon.getIconHeight() > height ) {
  763. tallerIcons = true;
  764. break;
  765. }
  766. }
  767. }
  768. return super.calculateMaxTabHeight( tabPlacement ) -
  769. (tallerIcons ? (tabInsets.top + tabInsets.bottom) : 0);
  770. }
  771. protected int getTabRunOverlay( int tabPlacement ) {
  772. // Tab runs laid out vertically should overlap
  773. // at least as much as the largest slant
  774. if ( tabPlacement == LEFT || tabPlacement == RIGHT ) {
  775. int maxTabHeight = calculateMaxTabHeight(tabPlacement);
  776. return maxTabHeight / 2;
  777. }
  778. return 0;
  779. }
  780. // Don't rotate runs!
  781. protected boolean shouldRotateTabRuns( int tabPlacement, int selectedRun ) {
  782. return false;
  783. }
  784. // Don't pad last run
  785. protected boolean shouldPadTabRun( int tabPlacement, int run ) {
  786. return runCount > 1 && run < runCount - 1;
  787. }
  788. private boolean isLastInRun( int tabIndex ) {
  789. int run = getRunForTab( tabPane.getTabCount(), tabIndex );
  790. int lastIndex = lastTabInRun( tabPane.getTabCount(), run );
  791. return tabIndex == lastIndex;
  792. }
  793. /**
  794. * This inner class is marked "public" due to a compiler bug.
  795. * This class should be treated as a "protected" inner class.
  796. * Instantiate it only within subclasses of MetalTabbedPaneUI.
  797. */
  798. public class TabbedPaneLayout extends BasicTabbedPaneUI.TabbedPaneLayout {
  799. public TabbedPaneLayout() {
  800. MetalTabbedPaneUI.this.super();
  801. }
  802. protected void normalizeTabRuns( int tabPlacement, int tabCount,
  803. int start, int max ) {
  804. // Only normalize the runs for top & bottom; normalizing
  805. // doesn't look right for Metal's vertical tabs
  806. // because the last run isn't padded and it looks odd to have
  807. // fat tabs in the first vertical runs, but slimmer ones in the
  808. // last (this effect isn't noticeable for horizontal tabs).
  809. if ( tabPlacement == TOP || tabPlacement == BOTTOM ) {
  810. super.normalizeTabRuns( tabPlacement, tabCount, start, max );
  811. }
  812. }
  813. // Don't rotate runs!
  814. protected void rotateTabRuns( int tabPlacement, int selectedRun ) {
  815. }
  816. // Don't pad selected tab
  817. protected void padSelectedTab( int tabPlacement, int selectedIndex ) {
  818. }
  819. }
  820. }