1. /*
  2. * @(#)MetalTabbedPaneUI.java 1.36 03/12/19
  3. *
  4. * Copyright 2004 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. // Background color for unselected tabs that don't have an explicitly
  33. // set color.
  34. private Color unselectedBackground;
  35. protected Color tabAreaBackground;
  36. protected Color selectColor;
  37. protected Color selectHighlight;
  38. private boolean tabsOpaque = true;
  39. // Whether or not we're using ocean. This is cached as it is used
  40. // extensively during painting.
  41. private boolean ocean;
  42. // Selected border color for ocean.
  43. private Color oceanSelectedBorderColor;
  44. public static ComponentUI createUI( JComponent x ) {
  45. return new MetalTabbedPaneUI();
  46. }
  47. protected LayoutManager createLayoutManager() {
  48. if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
  49. return super.createLayoutManager();
  50. }
  51. return new TabbedPaneLayout();
  52. }
  53. protected void installDefaults() {
  54. super.installDefaults();
  55. tabAreaBackground = UIManager.getColor("TabbedPane.tabAreaBackground");
  56. selectColor = UIManager.getColor("TabbedPane.selected");
  57. selectHighlight = UIManager.getColor("TabbedPane.selectHighlight");
  58. tabsOpaque = UIManager.getBoolean("TabbedPane.tabsOpaque");
  59. unselectedBackground = UIManager.getColor(
  60. "TabbedPane.unselectedBackground");
  61. ocean = MetalLookAndFeel.usingOcean();
  62. if (ocean) {
  63. oceanSelectedBorderColor = UIManager.getColor(
  64. "TabbedPane.borderHightlightColor");
  65. }
  66. }
  67. protected void paintTabBorder( Graphics g, int tabPlacement,
  68. int tabIndex, int x, int y, int w, int h,
  69. boolean isSelected) {
  70. int bottom = y + (h-1);
  71. int right = x + (w-1);
  72. switch ( tabPlacement ) {
  73. case LEFT:
  74. paintLeftTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
  75. break;
  76. case BOTTOM:
  77. paintBottomTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
  78. break;
  79. case RIGHT:
  80. paintRightTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
  81. break;
  82. case TOP:
  83. default:
  84. paintTopTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
  85. }
  86. }
  87. protected void paintTopTabBorder( int tabIndex, Graphics g,
  88. int x, int y, int w, int h,
  89. int btm, int rght,
  90. boolean isSelected ) {
  91. int currentRun = getRunForTab( tabPane.getTabCount(), tabIndex );
  92. int lastIndex = lastTabInRun( tabPane.getTabCount(), currentRun );
  93. int firstIndex = tabRuns[ currentRun ];
  94. boolean leftToRight = MetalUtils.isLeftToRight(tabPane);
  95. int selectedIndex = tabPane.getSelectedIndex();
  96. int bottom = h - 1;
  97. int right = w - 1;
  98. //
  99. // Paint Gap
  100. //
  101. if (shouldFillGap( currentRun, tabIndex, x, y ) ) {
  102. g.translate( x, y );
  103. if ( leftToRight ) {
  104. g.setColor( getColorForGap( currentRun, x, y + 1 ) );
  105. g.fillRect( 1, 0, 5, 3 );
  106. g.fillRect( 1, 3, 2, 2 );
  107. } else {
  108. g.setColor( getColorForGap( currentRun, x + w - 1, y + 1 ) );
  109. g.fillRect( right - 5, 0, 5, 3 );
  110. g.fillRect( right - 2, 3, 2, 2 );
  111. }
  112. g.translate( -x, -y );
  113. }
  114. g.translate( x, y );
  115. //
  116. // Paint Border
  117. //
  118. if (ocean && isSelected) {
  119. g.setColor(oceanSelectedBorderColor);
  120. }
  121. else {
  122. g.setColor( darkShadow );
  123. }
  124. if ( leftToRight ) {
  125. // Paint slant
  126. g.drawLine( 1, 5, 6, 0 );
  127. // Paint top
  128. g.drawLine( 6, 0, right, 0 );
  129. // Paint right
  130. if ( tabIndex==lastIndex ) {
  131. // last tab in run
  132. g.drawLine( right, 1, right, bottom );
  133. }
  134. if (ocean && tabIndex - 1 == selectedIndex &&
  135. currentRun == getRunForTab(
  136. tabPane.getTabCount(), selectedIndex)) {
  137. g.setColor(oceanSelectedBorderColor);
  138. }
  139. // Paint left
  140. if ( tabIndex != tabRuns[ runCount - 1 ] ) {
  141. // not the first tab in the last run
  142. if (ocean && isSelected) {
  143. g.drawLine(0, 6, 0, bottom);
  144. g.setColor(darkShadow);
  145. g.drawLine(0, 0, 0, 5);
  146. }
  147. else {
  148. g.drawLine( 0, 0, 0, bottom );
  149. }
  150. } else {
  151. // the first tab in the last run
  152. g.drawLine( 0, 6, 0, bottom );
  153. }
  154. } else {
  155. // Paint slant
  156. g.drawLine( right - 1, 5, right - 6, 0 );
  157. // Paint top
  158. g.drawLine( right - 6, 0, 0, 0 );
  159. // Paint left
  160. if ( tabIndex==lastIndex ) {
  161. // last tab in run
  162. g.drawLine( 0, 1, 0, bottom );
  163. }
  164. // Paint right
  165. if (ocean && tabIndex - 1 == selectedIndex &&
  166. currentRun == getRunForTab(
  167. tabPane.getTabCount(), selectedIndex)) {
  168. g.setColor(oceanSelectedBorderColor);
  169. g.drawLine(right, 0, right, bottom);
  170. }
  171. else if (ocean && isSelected) {
  172. g.drawLine(right, 6, right, bottom);
  173. if (tabIndex != 0) {
  174. g.setColor(darkShadow);
  175. g.drawLine(right, 0, right, 5);
  176. }
  177. }
  178. else {
  179. if ( tabIndex != tabRuns[ runCount - 1 ] ) {
  180. // not the first tab in the last run
  181. g.drawLine( right, 0, right, bottom );
  182. } else {
  183. // the first tab in the last run
  184. g.drawLine( right, 6, right, bottom );
  185. }
  186. }
  187. }
  188. //
  189. // Paint Highlight
  190. //
  191. g.setColor( isSelected ? selectHighlight : highlight );
  192. if ( leftToRight ) {
  193. // Paint slant
  194. g.drawLine( 1, 6, 6, 1 );
  195. // Paint top
  196. g.drawLine( 6, 1, right, 1 );
  197. // Paint left
  198. g.drawLine( 1, 6, 1, bottom );
  199. // paint highlight in the gap on tab behind this one
  200. // on the left end (where they all line up)
  201. if ( tabIndex==firstIndex && tabIndex!=tabRuns[runCount - 1] ) {
  202. // first tab in run but not first tab in last run
  203. if (tabPane.getSelectedIndex()==tabRuns[currentRun+1]) {
  204. // tab in front of selected tab
  205. g.setColor( selectHighlight );
  206. }
  207. else {
  208. // tab in front of normal tab
  209. g.setColor( highlight );
  210. }
  211. g.drawLine( 1, 0, 1, 4 );
  212. }
  213. } else {
  214. // Paint slant
  215. g.drawLine( right - 1, 6, right - 6, 1 );
  216. // Paint top
  217. g.drawLine( right - 6, 1, 1, 1 );
  218. // Paint left
  219. if ( tabIndex==lastIndex ) {
  220. // last tab in run
  221. g.drawLine( 1, 1, 1, bottom );
  222. } else {
  223. g.drawLine( 0, 1, 0, bottom );
  224. }
  225. }
  226. g.translate( -x, -y );
  227. }
  228. protected boolean shouldFillGap( int currentRun, int tabIndex, int x, int y ) {
  229. boolean result = false;
  230. if (!tabsOpaque) {
  231. return false;
  232. }
  233. if ( currentRun == runCount - 2 ) { // If it's the second to last row.
  234. Rectangle lastTabBounds = getTabBounds( tabPane, tabPane.getTabCount() - 1 );
  235. Rectangle tabBounds = getTabBounds( tabPane, tabIndex );
  236. if (MetalUtils.isLeftToRight(tabPane)) {
  237. int lastTabRight = lastTabBounds.x + lastTabBounds.width - 1;
  238. // is the right edge of the last tab to the right
  239. // of the left edge of the current tab?
  240. if ( lastTabRight > tabBounds.x + 2 ) {
  241. return true;
  242. }
  243. } else {
  244. int lastTabLeft = lastTabBounds.x;
  245. int currentTabRight = tabBounds.x + tabBounds.width - 1;
  246. // is the left edge of the last tab to the left
  247. // of the right edge of the current tab?
  248. if ( lastTabLeft < currentTabRight - 2 ) {
  249. return true;
  250. }
  251. }
  252. } else {
  253. // fill in gap for all other rows except last row
  254. result = currentRun != runCount - 1;
  255. }
  256. return result;
  257. }
  258. protected Color getColorForGap( int currentRun, int x, int y ) {
  259. final int shadowWidth = 4;
  260. int selectedIndex = tabPane.getSelectedIndex();
  261. int startIndex = tabRuns[ currentRun + 1 ];
  262. int endIndex = lastTabInRun( tabPane.getTabCount(), currentRun + 1 );
  263. int tabOverGap = -1;
  264. // Check each tab in the row that is 'on top' of this row
  265. for ( int i = startIndex; i <= endIndex; ++i ) {
  266. Rectangle tabBounds = getTabBounds( tabPane, i );
  267. int tabLeft = tabBounds.x;
  268. int tabRight = (tabBounds.x + tabBounds.width) - 1;
  269. // Check to see if this tab is over the gap
  270. if ( MetalUtils.isLeftToRight(tabPane) ) {
  271. if ( tabLeft <= x && tabRight - shadowWidth > x ) {
  272. if (ocean) {
  273. if (selectedIndex == i) {
  274. return oceanSelectedBorderColor;
  275. }
  276. return getUnselectedBackgroundAt(i);
  277. }
  278. return selectedIndex == i ? selectColor : getUnselectedBackgroundAt( i );
  279. }
  280. }
  281. else {
  282. if ( tabLeft + shadowWidth < x && tabRight >= x ) {
  283. if (ocean) {
  284. if (selectedIndex == i) {
  285. return oceanSelectedBorderColor;
  286. }
  287. return getUnselectedBackgroundAt(i);
  288. }
  289. return selectedIndex == i ? selectColor : getUnselectedBackgroundAt( i );
  290. }
  291. }
  292. }
  293. return tabPane.getBackground();
  294. }
  295. protected void paintLeftTabBorder( int tabIndex, Graphics g,
  296. int x, int y, int w, int h,
  297. int btm, int rght,
  298. boolean isSelected ) {
  299. int tabCount = tabPane.getTabCount();
  300. int currentRun = getRunForTab( tabCount, tabIndex );
  301. int lastIndex = lastTabInRun( tabCount, currentRun );
  302. int firstIndex = tabRuns[ currentRun ];
  303. g.translate( x, y );
  304. int bottom = h - 1;
  305. int right = w - 1;
  306. //
  307. // Paint part of the tab above
  308. //
  309. if ( tabIndex != firstIndex && tabsOpaque ) {
  310. g.setColor( tabPane.getSelectedIndex() == tabIndex - 1 ?
  311. selectColor :
  312. getUnselectedBackgroundAt( tabIndex - 1 ) );
  313. g.fillRect( 2, 0, 4, 3 );
  314. g.drawLine( 2, 3, 2, 3 );
  315. }
  316. //
  317. // Paint Highlight
  318. //
  319. if (ocean) {
  320. g.setColor(isSelected ? selectHighlight :
  321. MetalLookAndFeel.getWhite());
  322. }
  323. else {
  324. g.setColor( isSelected ? selectHighlight : highlight );
  325. }
  326. // Paint slant
  327. g.drawLine( 1, 6, 6, 1 );
  328. // Paint left
  329. g.drawLine( 1, 6, 1, bottom );
  330. // Paint top
  331. g.drawLine( 6, 1, right, 1 );
  332. if ( tabIndex != firstIndex ) {
  333. if (ocean) {
  334. g.setColor(MetalLookAndFeel.getWhite());
  335. }
  336. g.drawLine( 1, 0, 1, 4 );
  337. }
  338. //
  339. // Paint Border
  340. //
  341. if (ocean) {
  342. if (isSelected) {
  343. g.setColor(oceanSelectedBorderColor);
  344. }
  345. else {
  346. g.setColor( darkShadow );
  347. }
  348. }
  349. else {
  350. g.setColor( darkShadow );
  351. }
  352. // Paint slant
  353. g.drawLine( 1, 5, 6, 0 );
  354. // Paint top
  355. g.drawLine( 6, 0, right, 0 );
  356. // Paint bottom
  357. if ( tabIndex == lastIndex ) {
  358. g.drawLine( 0, bottom, right, bottom );
  359. }
  360. // Paint left
  361. if (ocean) {
  362. if (tabPane.getSelectedIndex() == tabIndex - 1) {
  363. g.drawLine(0, 5, 0, bottom);
  364. g.setColor(oceanSelectedBorderColor);
  365. g.drawLine(0, 0, 0, 5);
  366. }
  367. else if (isSelected) {
  368. g.drawLine( 0, 5, 0, bottom );
  369. if (tabIndex != 0) {
  370. g.setColor(darkShadow);
  371. g.drawLine(0, 0, 0, 5);
  372. }
  373. }
  374. else if ( tabIndex != firstIndex ) {
  375. g.drawLine( 0, 0, 0, bottom );
  376. } else {
  377. g.drawLine( 0, 6, 0, bottom );
  378. }
  379. }
  380. else { // metal
  381. if ( tabIndex != firstIndex ) {
  382. g.drawLine( 0, 0, 0, bottom );
  383. } else {
  384. g.drawLine( 0, 6, 0, bottom );
  385. }
  386. }
  387. g.translate( -x, -y );
  388. }
  389. protected void paintBottomTabBorder( int tabIndex, Graphics g,
  390. int x, int y, int w, int h,
  391. int btm, int rght,
  392. boolean isSelected ) {
  393. int tabCount = tabPane.getTabCount();
  394. int currentRun = getRunForTab( tabCount, tabIndex );
  395. int lastIndex = lastTabInRun( tabCount, currentRun );
  396. int firstIndex = tabRuns[ currentRun ];
  397. boolean leftToRight = MetalUtils.isLeftToRight(tabPane);
  398. int bottom = h - 1;
  399. int right = w - 1;
  400. //
  401. // Paint Gap
  402. //
  403. if ( shouldFillGap( currentRun, tabIndex, x, y ) ) {
  404. g.translate( x, y );
  405. if ( leftToRight ) {
  406. g.setColor( getColorForGap( currentRun, x, y ) );
  407. g.fillRect( 1, bottom - 4, 3, 5 );
  408. g.fillRect( 4, bottom - 1, 2, 2 );
  409. } else {
  410. g.setColor( getColorForGap( currentRun, x + w - 1, y ) );
  411. g.fillRect( right - 3, bottom - 3, 3, 4 );
  412. g.fillRect( right - 5, bottom - 1, 2, 2 );
  413. g.drawLine( right - 1, bottom - 4, right - 1, bottom - 4 );
  414. }
  415. g.translate( -x, -y );
  416. }
  417. g.translate( x, y );
  418. //
  419. // Paint Border
  420. //
  421. if (ocean && isSelected) {
  422. g.setColor(oceanSelectedBorderColor);
  423. }
  424. else {
  425. g.setColor( darkShadow );
  426. }
  427. if ( leftToRight ) {
  428. // Paint slant
  429. g.drawLine( 1, bottom - 5, 6, bottom );
  430. // Paint bottom
  431. g.drawLine( 6, bottom, right, bottom );
  432. // Paint right
  433. if ( tabIndex == lastIndex ) {
  434. g.drawLine( right, 0, right, bottom );
  435. }
  436. // Paint left
  437. if (ocean && isSelected) {
  438. g.drawLine(0, 0, 0, bottom - 5);
  439. if ((currentRun == 0 && tabIndex != 0) ||
  440. (currentRun > 0 && tabIndex != tabRuns[currentRun - 1])) {
  441. g.setColor(darkShadow);
  442. g.drawLine(0, bottom - 5, 0, bottom);
  443. }
  444. }
  445. else {
  446. if (ocean && tabIndex == tabPane.getSelectedIndex() + 1) {
  447. g.setColor(oceanSelectedBorderColor);
  448. }
  449. if ( tabIndex != tabRuns[ runCount - 1 ] ) {
  450. g.drawLine( 0, 0, 0, bottom );
  451. } else {
  452. g.drawLine( 0, 0, 0, bottom - 6 );
  453. }
  454. }
  455. } else {
  456. // Paint slant
  457. g.drawLine( right - 1, bottom - 5, right - 6, bottom );
  458. // Paint bottom
  459. g.drawLine( right - 6, bottom, 0, bottom );
  460. // Paint left
  461. if ( tabIndex==lastIndex ) {
  462. // last tab in run
  463. g.drawLine( 0, 0, 0, bottom );
  464. }
  465. // Paint right
  466. if (ocean && tabIndex == tabPane.getSelectedIndex() + 1) {
  467. g.setColor(oceanSelectedBorderColor);
  468. g.drawLine(right, 0, right, bottom);
  469. }
  470. else if (ocean && isSelected) {
  471. g.drawLine(right, 0, right, bottom - 6);
  472. if (tabIndex != firstIndex) {
  473. g.setColor(darkShadow);
  474. g.drawLine(right, bottom - 5, right, bottom);
  475. }
  476. }
  477. else if ( tabIndex != tabRuns[ runCount - 1 ] ) {
  478. // not the first tab in the last run
  479. g.drawLine( right, 0, right, bottom );
  480. } else {
  481. // the first tab in the last run
  482. g.drawLine( right, 0, right, bottom - 6 );
  483. }
  484. }
  485. //
  486. // Paint Highlight
  487. //
  488. g.setColor( isSelected ? selectHighlight : highlight );
  489. if ( leftToRight ) {
  490. // Paint slant
  491. g.drawLine( 1, bottom - 6, 6, bottom - 1 );
  492. // Paint left
  493. g.drawLine( 1, 0, 1, bottom - 6 );
  494. // paint highlight in the gap on tab behind this one
  495. // on the left end (where they all line up)
  496. if ( tabIndex==firstIndex && tabIndex!=tabRuns[runCount - 1] ) {
  497. // first tab in run but not first tab in last run
  498. if (tabPane.getSelectedIndex()==tabRuns[currentRun+1]) {
  499. // tab in front of selected tab
  500. g.setColor( selectHighlight );
  501. }
  502. else {
  503. // tab in front of normal tab
  504. g.setColor( highlight );
  505. }
  506. g.drawLine( 1, bottom - 4, 1, bottom );
  507. }
  508. } else {
  509. // Paint left
  510. if ( tabIndex==lastIndex ) {
  511. // last tab in run
  512. g.drawLine( 1, 0, 1, bottom - 1 );
  513. } else {
  514. g.drawLine( 0, 0, 0, bottom - 1 );
  515. }
  516. }
  517. g.translate( -x, -y );
  518. }
  519. protected void paintRightTabBorder( int tabIndex, Graphics g,
  520. int x, int y, int w, int h,
  521. int btm, int rght,
  522. boolean isSelected ) {
  523. int tabCount = tabPane.getTabCount();
  524. int currentRun = getRunForTab( tabCount, tabIndex );
  525. int lastIndex = lastTabInRun( tabCount, currentRun );
  526. int firstIndex = tabRuns[ currentRun ];
  527. g.translate( x, y );
  528. int bottom = h - 1;
  529. int right = w - 1;
  530. //
  531. // Paint part of the tab above
  532. //
  533. if ( tabIndex != firstIndex && tabsOpaque ) {
  534. g.setColor( tabPane.getSelectedIndex() == tabIndex - 1 ?
  535. tabAreaBackground :
  536. getUnselectedBackgroundAt( tabIndex - 1 ) );
  537. g.fillRect( right - 5, 0, 5, 3 );
  538. g.fillRect( right - 2, 3, 2, 2 );
  539. }
  540. //
  541. // Paint Highlight
  542. //
  543. g.setColor( isSelected ? selectHighlight : highlight );
  544. // Paint slant
  545. g.drawLine( right - 6, 1, right - 1, 6 );
  546. // Paint top
  547. g.drawLine( 0, 1, right - 6, 1 );
  548. // Paint left
  549. if ( !isSelected ) {
  550. g.drawLine( 0, 1, 0, bottom );
  551. }
  552. //
  553. // Paint Border
  554. //
  555. if (ocean && isSelected) {
  556. g.setColor(oceanSelectedBorderColor);
  557. }
  558. else {
  559. g.setColor( darkShadow );
  560. }
  561. // Paint bottom
  562. if ( tabIndex == lastIndex ) {
  563. g.drawLine( 0, bottom, right, bottom );
  564. }
  565. // Paint slant
  566. if (ocean && tabPane.getSelectedIndex() == tabIndex - 1) {
  567. g.setColor(oceanSelectedBorderColor);
  568. }
  569. g.drawLine( right - 6, 0, right, 6 );
  570. // Paint top
  571. g.drawLine( 0, 0, right - 6, 0 );
  572. // Paint right
  573. if (ocean && isSelected) {
  574. g.drawLine(right, 6, right, bottom);
  575. if (tabIndex != firstIndex) {
  576. g.setColor(darkShadow);
  577. g.drawLine(right, 0, right, 5);
  578. }
  579. }
  580. else if (ocean && tabPane.getSelectedIndex() == tabIndex - 1) {
  581. g.setColor(oceanSelectedBorderColor);
  582. g.drawLine(right, 0, right, 6);
  583. g.setColor(darkShadow);
  584. g.drawLine(right, 6, right, bottom);
  585. }
  586. else if ( tabIndex != firstIndex ) {
  587. g.drawLine( right, 0, right, bottom );
  588. } else {
  589. g.drawLine( right, 6, right, bottom );
  590. }
  591. g.translate( -x, -y );
  592. }
  593. public void update( Graphics g, JComponent c ) {
  594. if ( c.isOpaque() ) {
  595. g.setColor( tabAreaBackground );
  596. g.fillRect( 0, 0, c.getWidth(),c.getHeight() );
  597. }
  598. paint( g, c );
  599. }
  600. protected void paintTabBackground( Graphics g, int tabPlacement,
  601. int tabIndex, int x, int y, int w, int h, boolean isSelected ) {
  602. int slantWidth = h / 2;
  603. if ( isSelected ) {
  604. g.setColor( selectColor );
  605. } else {
  606. g.setColor( getUnselectedBackgroundAt( tabIndex ) );
  607. }
  608. if (MetalUtils.isLeftToRight(tabPane)) {
  609. switch ( tabPlacement ) {
  610. case LEFT:
  611. g.fillRect( x + 5, y + 1, w - 5, h - 1);
  612. g.fillRect( x + 2, y + 4, 3, h - 4 );
  613. break;
  614. case BOTTOM:
  615. g.fillRect( x + 2, y, w - 2, h - 4 );
  616. g.fillRect( x + 5, y + (h - 1) - 3, w - 5, 3 );
  617. break;
  618. case RIGHT:
  619. g.fillRect( x, y + 1, w - 4, h - 1);
  620. g.fillRect( x + (w - 1) - 3, y + 5, 3, h - 5 );
  621. break;
  622. case TOP:
  623. default:
  624. g.fillRect( x + 4, y + 2, (w - 1) - 3, (h - 1) - 1 );
  625. g.fillRect( x + 2, y + 5, 2, h - 5 );
  626. }
  627. } else {
  628. switch ( tabPlacement ) {
  629. case LEFT:
  630. g.fillRect( x + 5, y + 1, w - 5, h - 1);
  631. g.fillRect( x + 2, y + 4, 3, h - 4 );
  632. break;
  633. case BOTTOM:
  634. g.fillRect( x, y, w - 5, h - 1 );
  635. g.fillRect( x + (w - 1) - 4, y, 4, h - 5);
  636. g.fillRect( x + (w - 1) - 4, y + (h - 1) - 4, 2, 2);
  637. break;
  638. case RIGHT:
  639. g.fillRect( x + 1, y + 1, w - 5, h - 1);
  640. g.fillRect( x + (w - 1) - 3, y + 5, 3, h - 5 );
  641. break;
  642. case TOP:
  643. default:
  644. g.fillRect( x, y + 2, (w - 1) - 3, (h - 1) - 1 );
  645. g.fillRect( x + (w - 1) - 3, y + 4, 3, h - 4 );
  646. }
  647. }
  648. }
  649. /**
  650. * Overridden to do nothing for the Java L&F.
  651. */
  652. protected int getTabLabelShiftX( int tabPlacement, int tabIndex, boolean isSelected ) {
  653. return 0;
  654. }
  655. /**
  656. * Overridden to do nothing for the Java L&F.
  657. */
  658. protected int getTabLabelShiftY( int tabPlacement, int tabIndex, boolean isSelected ) {
  659. return 0;
  660. }
  661. public void paint( Graphics g, JComponent c ) {
  662. int tabPlacement = tabPane.getTabPlacement();
  663. Insets insets = c.getInsets(); Dimension size = c.getSize();
  664. // Paint the background for the tab area
  665. if ( tabPane.isOpaque() ) {
  666. Color bg = UIManager.getColor("TabbedPane.tabAreaBackground");
  667. if (bg != null) {
  668. g.setColor(bg);
  669. }
  670. else {
  671. g.setColor( c.getBackground() );
  672. }
  673. switch ( tabPlacement ) {
  674. case LEFT:
  675. g.fillRect( insets.left, insets.top,
  676. calculateTabAreaWidth( tabPlacement, runCount, maxTabWidth ),
  677. size.height - insets.bottom - insets.top );
  678. break;
  679. case BOTTOM:
  680. int totalTabHeight = calculateTabAreaHeight( tabPlacement, runCount, maxTabHeight );
  681. g.fillRect( insets.left, size.height - insets.bottom - totalTabHeight,
  682. size.width - insets.left - insets.right,
  683. totalTabHeight );
  684. break;
  685. case RIGHT:
  686. int totalTabWidth = calculateTabAreaWidth( tabPlacement, runCount, maxTabWidth );
  687. g.fillRect( size.width - insets.right - totalTabWidth,
  688. insets.top, totalTabWidth,
  689. size.height - insets.top - insets.bottom );
  690. break;
  691. case TOP:
  692. default:
  693. g.fillRect( insets.left, insets.top,
  694. size.width - insets.right - insets.left,
  695. calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight) );
  696. paintHighlightBelowTab();
  697. }
  698. }
  699. super.paint( g, c );
  700. }
  701. protected void paintHighlightBelowTab( ) {
  702. }
  703. protected void paintFocusIndicator(Graphics g, int tabPlacement,
  704. Rectangle[] rects, int tabIndex,
  705. Rectangle iconRect, Rectangle textRect,
  706. boolean isSelected) {
  707. if ( tabPane.hasFocus() && isSelected ) {
  708. Rectangle tabRect = rects[tabIndex];
  709. boolean lastInRun = isLastInRun( tabIndex );
  710. g.setColor( focus );
  711. g.translate( tabRect.x, tabRect.y );
  712. int right = tabRect.width - 1;
  713. int bottom = tabRect.height - 1;
  714. boolean leftToRight = MetalUtils.isLeftToRight(tabPane);
  715. switch ( tabPlacement ) {
  716. case RIGHT:
  717. g.drawLine( right - 6,2 , right - 2,6 ); // slant
  718. g.drawLine( 1,2 , right - 6,2 ); // top
  719. g.drawLine( right - 2,6 , right - 2,bottom ); // right
  720. g.drawLine( 1,2 , 1,bottom ); // left
  721. g.drawLine( 1,bottom , right - 2,bottom ); // bottom
  722. break;
  723. case BOTTOM:
  724. if ( leftToRight ) {
  725. g.drawLine( 2, bottom - 6, 6, bottom - 2 ); // slant
  726. g.drawLine( 6, bottom - 2,
  727. right, bottom - 2 ); // bottom
  728. g.drawLine( 2, 0, 2, bottom - 6 ); // left
  729. g.drawLine( 2, 0, right, 0 ); // top
  730. g.drawLine( right, 0, right, bottom - 2 ); // right
  731. } else {
  732. g.drawLine( right - 2, bottom - 6,
  733. right - 6, bottom - 2 ); // slant
  734. g.drawLine( right - 2, 0,
  735. right - 2, bottom - 6 ); // right
  736. if ( lastInRun ) {
  737. // last tab in run
  738. g.drawLine( 2, bottom - 2,
  739. right - 6, bottom - 2 ); // bottom
  740. g.drawLine( 2, 0, right - 2, 0 ); // top
  741. g.drawLine( 2, 0, 2, bottom - 2 ); // left
  742. } else {
  743. g.drawLine( 1, bottom - 2,
  744. right - 6, bottom - 2 ); // bottom
  745. g.drawLine( 1, 0, right - 2, 0 ); // top
  746. g.drawLine( 1, 0, 1, bottom - 2 ); // left
  747. }
  748. }
  749. break;
  750. case LEFT:
  751. g.drawLine( 2, 6, 6, 2 ); // slant
  752. g.drawLine( 2, 6, 2, bottom - 1); // left
  753. g.drawLine( 6, 2, right, 2 ); // top
  754. g.drawLine( right, 2, right, bottom - 1 ); // right
  755. g.drawLine( 2, bottom - 1,
  756. right, bottom - 1 ); // bottom
  757. break;
  758. case TOP:
  759. default:
  760. if ( leftToRight ) {
  761. g.drawLine( 2, 6, 6, 2 ); // slant
  762. g.drawLine( 2, 6, 2, bottom - 1); // left
  763. g.drawLine( 6, 2, right, 2 ); // top
  764. g.drawLine( right, 2, right, bottom - 1 ); // right
  765. g.drawLine( 2, bottom - 1,
  766. right, bottom - 1 ); // bottom
  767. }
  768. else {
  769. g.drawLine( right - 2, 6, right - 6, 2 ); // slant
  770. g.drawLine( right - 2, 6,
  771. right - 2, bottom - 1); // right
  772. if ( lastInRun ) {
  773. // last tab in run
  774. g.drawLine( right - 6, 2, 2, 2 ); // top
  775. g.drawLine( 2, 2, 2, bottom - 1 ); // left
  776. g.drawLine( right - 2, bottom - 1,
  777. 2, bottom - 1 ); // bottom
  778. }
  779. else {
  780. g.drawLine( right - 6, 2, 1, 2 ); // top
  781. g.drawLine( 1, 2, 1, bottom - 1 ); // left
  782. g.drawLine( right - 2, bottom - 1,
  783. 1, bottom - 1 ); // bottom
  784. }
  785. }
  786. }
  787. g.translate( -tabRect.x, -tabRect.y );
  788. }
  789. }
  790. protected void paintContentBorderTopEdge( Graphics g, int tabPlacement,
  791. int selectedIndex,
  792. int x, int y, int w, int h ) {
  793. boolean leftToRight = MetalUtils.isLeftToRight(tabPane);
  794. int right = x + w - 1;
  795. Rectangle selRect = selectedIndex < 0? null :
  796. getTabBounds(selectedIndex, calcRect);
  797. if (ocean) {
  798. g.setColor(oceanSelectedBorderColor);
  799. }
  800. else {
  801. g.setColor(selectHighlight);
  802. }
  803. // Draw unbroken line if tabs are not on TOP, OR
  804. // selected tab is not in run adjacent to content, OR
  805. // selected tab is not visible (SCROLL_TAB_LAYOUT)
  806. //
  807. if (tabPlacement != TOP || selectedIndex < 0 ||
  808. (selRect.y + selRect.height + 1 < y) ||
  809. (selRect.x < x || selRect.x > x + w)) {
  810. g.drawLine(x, y, x+w-2, y);
  811. if (ocean && tabPlacement == TOP) {
  812. g.setColor(MetalLookAndFeel.getWhite());
  813. g.drawLine(x, y + 1, x+w-2, y + 1);
  814. }
  815. } else {
  816. // Break line to show visual connection to selected tab
  817. boolean lastInRun = isLastInRun(selectedIndex);
  818. if ( leftToRight || lastInRun ) {
  819. g.drawLine(x, y, selRect.x + 1, y);
  820. } else {
  821. g.drawLine(x, y, selRect.x, y);
  822. }
  823. if (selRect.x + selRect.width < right - 1) {
  824. if ( leftToRight && !lastInRun ) {
  825. g.drawLine(selRect.x + selRect.width, y, right - 1, y);
  826. } else {
  827. g.drawLine(selRect.x + selRect.width - 1, y, right - 1, y);
  828. }
  829. } else {
  830. g.setColor(shadow);
  831. g.drawLine(x+w-2, y, x+w-2, y);
  832. }
  833. if (ocean) {
  834. g.setColor(MetalLookAndFeel.getWhite());
  835. if ( leftToRight || lastInRun ) {
  836. g.drawLine(x, y + 1, selRect.x + 1, y + 1);
  837. } else {
  838. g.drawLine(x, y + 1, selRect.x, y + 1);
  839. }
  840. if (selRect.x + selRect.width < right - 1) {
  841. if ( leftToRight && !lastInRun ) {
  842. g.drawLine(selRect.x + selRect.width, y + 1,
  843. right - 1, y + 1);
  844. } else {
  845. g.drawLine(selRect.x + selRect.width - 1, y + 1,
  846. right - 1, y + 1);
  847. }
  848. } else {
  849. g.setColor(shadow);
  850. g.drawLine(x+w-2, y + 1, x+w-2, y + 1);
  851. }
  852. }
  853. }
  854. }
  855. protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
  856. int selectedIndex,
  857. int x, int y, int w, int h) {
  858. boolean leftToRight = MetalUtils.isLeftToRight(tabPane);
  859. int bottom = y + h - 1;
  860. int right = x + w - 1;
  861. Rectangle selRect = selectedIndex < 0? null :
  862. getTabBounds(selectedIndex, calcRect);
  863. g.setColor(darkShadow);
  864. // Draw unbroken line if tabs are not on BOTTOM, OR
  865. // selected tab is not in run adjacent to content, OR
  866. // selected tab is not visible (SCROLL_TAB_LAYOUT)
  867. //
  868. if (tabPlacement != BOTTOM || selectedIndex < 0 ||
  869. (selRect.y - 1 > h) ||
  870. (selRect.x < x || selRect.x > x + w)) {
  871. if (ocean && tabPlacement == BOTTOM) {
  872. g.setColor(oceanSelectedBorderColor);
  873. }
  874. g.drawLine(x, y+h-1, x+w-1, y+h-1);
  875. } else {
  876. // Break line to show visual connection to selected tab
  877. boolean lastInRun = isLastInRun(selectedIndex);
  878. if (ocean) {
  879. g.setColor(oceanSelectedBorderColor);
  880. }
  881. if ( leftToRight || lastInRun ) {
  882. g.drawLine(x, bottom, selRect.x, bottom);
  883. } else {
  884. g.drawLine(x, bottom, selRect.x - 1, bottom);
  885. }
  886. if (selRect.x + selRect.width < x + w - 2) {
  887. if ( leftToRight && !lastInRun ) {
  888. g.drawLine(selRect.x + selRect.width, bottom,
  889. right, bottom);
  890. } else {
  891. g.drawLine(selRect.x + selRect.width - 1, bottom,
  892. right, bottom);
  893. }
  894. }
  895. }
  896. }
  897. protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
  898. int selectedIndex,
  899. int x, int y, int w, int h) {
  900. Rectangle selRect = selectedIndex < 0? null :
  901. getTabBounds(selectedIndex, calcRect);
  902. if (ocean) {
  903. g.setColor(oceanSelectedBorderColor);
  904. }
  905. else {
  906. g.setColor(selectHighlight);
  907. }
  908. // Draw unbroken line if tabs are not on LEFT, OR
  909. // selected tab is not in run adjacent to content, OR
  910. // selected tab is not visible (SCROLL_TAB_LAYOUT)
  911. //
  912. if (tabPlacement != LEFT || selectedIndex < 0 ||
  913. (selRect.x + selRect.width + 1 < x) ||
  914. (selRect.y < y || selRect.y > y + h)) {
  915. g.drawLine(x, y + 1, x, y+h-2);
  916. if (ocean && tabPlacement == LEFT) {
  917. g.setColor(MetalLookAndFeel.getWhite());
  918. g.drawLine(x + 1, y, x + 1, y + h - 2);
  919. }
  920. } else {
  921. // Break line to show visual connection to selected tab
  922. g.drawLine(x, y, x, selRect.y + 1);
  923. if (selRect.y + selRect.height < y + h - 2) {
  924. g.drawLine(x, selRect.y + selRect.height + 1,
  925. x, y+h+2);
  926. }
  927. if (ocean) {
  928. g.setColor(MetalLookAndFeel.getWhite());
  929. g.drawLine(x + 1, y + 1, x + 1, selRect.y + 1);
  930. if (selRect.y + selRect.height < y + h - 2) {
  931. g.drawLine(x + 1, selRect.y + selRect.height + 1,
  932. x + 1, y+h+2);
  933. }
  934. }
  935. }
  936. }
  937. protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
  938. int selectedIndex,
  939. int x, int y, int w, int h) {
  940. Rectangle selRect = selectedIndex < 0? null :
  941. getTabBounds(selectedIndex, calcRect);
  942. g.setColor(darkShadow);
  943. // Draw unbroken line if tabs are not on RIGHT, OR
  944. // selected tab is not in run adjacent to content, OR
  945. // selected tab is not visible (SCROLL_TAB_LAYOUT)
  946. //
  947. if (tabPlacement != RIGHT || selectedIndex < 0 ||
  948. (selRect.x - 1 > w) ||
  949. (selRect.y < y || selRect.y > y + h)) {
  950. if (ocean && tabPlacement == RIGHT) {
  951. g.setColor(oceanSelectedBorderColor);
  952. }
  953. g.drawLine(x+w-1, y, x+w-1, y+h-1);
  954. } else {
  955. // Break line to show visual connection to selected tab
  956. if (ocean) {
  957. g.setColor(oceanSelectedBorderColor);
  958. }
  959. g.drawLine(x+w-1, y, x+w-1, selRect.y);
  960. if (selRect.y + selRect.height < y + h - 2) {
  961. g.drawLine(x+w-1, selRect.y + selRect.height,
  962. x+w-1, y+h-2);
  963. }
  964. }
  965. }
  966. protected int calculateMaxTabHeight( int tabPlacement ) {
  967. FontMetrics metrics = getFontMetrics();
  968. int height = metrics.getHeight();
  969. boolean tallerIcons = false;
  970. for ( int i = 0; i < tabPane.getTabCount(); ++i ) {
  971. Icon icon = tabPane.getIconAt( i );
  972. if ( icon != null ) {
  973. if ( icon.getIconHeight() > height ) {
  974. tallerIcons = true;
  975. break;
  976. }
  977. }
  978. }
  979. return super.calculateMaxTabHeight( tabPlacement ) -
  980. (tallerIcons ? (tabInsets.top + tabInsets.bottom) : 0);
  981. }
  982. protected int getTabRunOverlay( int tabPlacement ) {
  983. // Tab runs laid out vertically should overlap
  984. // at least as much as the largest slant
  985. if ( tabPlacement == LEFT || tabPlacement == RIGHT ) {
  986. int maxTabHeight = calculateMaxTabHeight(tabPlacement);
  987. return maxTabHeight / 2;
  988. }
  989. return 0;
  990. }
  991. // Don't rotate runs!
  992. protected boolean shouldRotateTabRuns( int tabPlacement, int selectedRun ) {
  993. return false;
  994. }
  995. // Don't pad last run
  996. protected boolean shouldPadTabRun( int tabPlacement, int run ) {
  997. return runCount > 1 && run < runCount - 1;
  998. }
  999. private boolean isLastInRun( int tabIndex ) {
  1000. int run = getRunForTab( tabPane.getTabCount(), tabIndex );
  1001. int lastIndex = lastTabInRun( tabPane.getTabCount(), run );
  1002. return tabIndex == lastIndex;
  1003. }
  1004. /**
  1005. * Returns the color to use for the specified tab.
  1006. */
  1007. private Color getUnselectedBackgroundAt(int index) {
  1008. Color color = tabPane.getBackgroundAt(index);
  1009. if (color instanceof UIResource) {
  1010. if (unselectedBackground != null) {
  1011. return unselectedBackground;
  1012. }
  1013. }
  1014. return color;
  1015. }
  1016. /**
  1017. * Returns the tab index of JTabbedPane the mouse is currently over
  1018. */
  1019. int getRolloverTabIndex() {
  1020. return getRolloverTab();
  1021. }
  1022. /**
  1023. * This inner class is marked "public" due to a compiler bug.
  1024. * This class should be treated as a "protected" inner class.
  1025. * Instantiate it only within subclasses of MetalTabbedPaneUI.
  1026. */
  1027. public class TabbedPaneLayout extends BasicTabbedPaneUI.TabbedPaneLayout {
  1028. public TabbedPaneLayout() {
  1029. MetalTabbedPaneUI.this.super();
  1030. }
  1031. protected void normalizeTabRuns( int tabPlacement, int tabCount,
  1032. int start, int max ) {
  1033. // Only normalize the runs for top & bottom; normalizing
  1034. // doesn't look right for Metal's vertical tabs
  1035. // because the last run isn't padded and it looks odd to have
  1036. // fat tabs in the first vertical runs, but slimmer ones in the
  1037. // last (this effect isn't noticeable for horizontal tabs).
  1038. if ( tabPlacement == TOP || tabPlacement == BOTTOM ) {
  1039. super.normalizeTabRuns( tabPlacement, tabCount, start, max );
  1040. }
  1041. }
  1042. // Don't rotate runs!
  1043. protected void rotateTabRuns( int tabPlacement, int selectedRun ) {
  1044. }
  1045. // Don't pad selected tab
  1046. protected void padSelectedTab( int tabPlacement, int selectedIndex ) {
  1047. }
  1048. }
  1049. }