001/* MetalTabbedPaneUI.java 002 Copyright (C) 2005, 2006 Free Software Foundation, Inc. 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038 039package javax.swing.plaf.metal; 040 041import java.awt.Color; 042import java.awt.Graphics; 043import java.awt.LayoutManager; 044import java.awt.Rectangle; 045 046import javax.swing.JComponent; 047import javax.swing.JTabbedPane; 048import javax.swing.UIManager; 049import javax.swing.plaf.ComponentUI; 050import javax.swing.plaf.UIResource; 051import javax.swing.plaf.basic.BasicTabbedPaneUI; 052 053/** 054 * A UI delegate for the {@link JTabbedPane} component. 055 */ 056public class MetalTabbedPaneUI extends BasicTabbedPaneUI 057{ 058 059 /** 060 * A {@link LayoutManager} responsible for placing all the tabs and the 061 * visible component inside the {@link JTabbedPane}. This class is only used 062 * for {@link JTabbedPane#WRAP_TAB_LAYOUT}. 063 * 064 * @specnote Apparently this class was intended to be protected, 065 * but was made public by a compiler bug and is now 066 * public for compatibility. 067 */ 068 public class TabbedPaneLayout 069 extends BasicTabbedPaneUI.TabbedPaneLayout 070 { 071 /** 072 * Creates a new instance of the layout manager. 073 */ 074 public TabbedPaneLayout() 075 { 076 // Nothing to do here. 077 } 078 079 /** 080 * Overridden to do nothing, because tab runs are not rotated in the 081 * {@link MetalLookAndFeel}. 082 * 083 * @param tabPlacement the tab placement (one of {@link #TOP}, 084 * {@link #BOTTOM}, {@link #LEFT} or {@link #RIGHT}). 085 * @param selectedRun the index of the selected run. 086 */ 087 protected void rotateTabRuns(int tabPlacement, int selectedRun) 088 { 089 // do nothing, because tab runs are not rotated in the MetalLookAndFeel 090 } 091 092 /** 093 * Overridden to do nothing, because the selected tab does not have extra 094 * padding in the {@link MetalLookAndFeel}. 095 * 096 * @param tabPlacement the tab placement (one of {@link #TOP}, 097 * {@link #BOTTOM}, {@link #LEFT} or {@link #RIGHT}). 098 * @param selectedIndex the index of the selected tab. 099 */ 100 protected void padSelectedTab(int tabPlacement, int selectedIndex) 101 { 102 // do nothing, because the selected tab does not have extra padding in 103 // the MetalLookAndFeel 104 } 105 106 /** 107 * Overridden because tab runs are only normalized for TOP and BOTTOM 108 * tab placement in the Metal L&F. 109 */ 110 protected void normalizeTabRuns(int tabPlacement, int tabCount, int start, 111 int max) 112 { 113 if (tabPlacement == TOP || tabPlacement == BOTTOM) 114 super.normalizeTabRuns(tabPlacement, tabCount, start, max); 115 } 116 } 117 118 /** 119 * The minimum tab width. 120 */ 121 protected int minTabWidth; 122 123 /** 124 * The color for the selected tab. 125 */ 126 protected Color selectColor; 127 128 /** 129 * The color for a highlighted selected tab. 130 */ 131 protected Color selectHighlight; 132 133 /** 134 * The background color used for the tab area. 135 */ 136 protected Color tabAreaBackground; 137 138 /** The graphics to draw the highlight below the tab. */ 139 private Graphics hg; 140 141 /** 142 * Indicates if the tabs are having their background filled. 143 */ 144 private boolean tabsOpaque; 145 146 /** 147 * Constructs a new instance of MetalTabbedPaneUI. 148 */ 149 public MetalTabbedPaneUI() 150 { 151 super(); 152 } 153 154 /** 155 * Returns an instance of MetalTabbedPaneUI. 156 * 157 * @param component the component for which we return an UI instance 158 * 159 * @return an instance of MetalTabbedPaneUI 160 */ 161 public static ComponentUI createUI(JComponent component) 162 { 163 return new MetalTabbedPaneUI(); 164 } 165 166 /** 167 * Creates and returns an instance of {@link TabbedPaneLayout}. 168 * 169 * @return A layout manager used by this UI delegate. 170 */ 171 protected LayoutManager createLayoutManager() 172 { 173 return (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT) 174 ? new MetalTabbedPaneUI.TabbedPaneLayout() 175 : super.createLayoutManager(); 176 } 177 178 /** 179 * Paints the border for a single tab. 180 * 181 * @param g the graphics device. 182 * @param tabPlacement the tab placement ({@link #TOP}, {@link #LEFT}, 183 * {@link #BOTTOM} or {@link #RIGHT}). 184 * @param tabIndex the index of the tab to draw the border for. 185 * @param x the x-coordinate for the tab's bounding rectangle. 186 * @param y the y-coordinate for the tab's bounding rectangle. 187 * @param w the width for the tab's bounding rectangle. 188 * @param h the height for the tab's bounding rectangle. 189 * @param isSelected indicates whether or not the tab is selected. 190 */ 191 protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex, 192 int x, int y, int w, int h, boolean isSelected) 193 { 194 int bottom = y + h - 1; 195 int right = x + w - 1; 196 197 switch (tabPlacement) 198 { 199 case LEFT: 200 paintLeftTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected); 201 break; 202 case BOTTOM: 203 paintBottomTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected); 204 break; 205 case RIGHT: 206 paintRightTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected); 207 break; 208 case TOP: 209 default: 210 paintTopTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected); 211 } 212 } 213 214 /** 215 * Paints the border for a tab assuming that the tab position is at the top 216 * ({@link #TOP}). 217 * 218 * @param tabIndex the tab index. 219 * @param g the graphics device. 220 * @param x the x-coordinate for the tab's bounding rectangle. 221 * @param y the y-coordinate for the tab's bounding rectangle. 222 * @param w the width for the tab's bounding rectangle. 223 * @param h the height for the tab's bounding rectangle. 224 * @param btm the y coordinate of the bottom border 225 * @param rght the x coordinate of the right border 226 * @param isSelected indicates whether the tab is selected. 227 */ 228 protected void paintTopTabBorder(int tabIndex, Graphics g, int x, int y, 229 int w, int h, int btm, int rght, boolean isSelected) 230 { 231 int tabCount = tabPane.getTabCount(); 232 int currentRun = getRunForTab(tabCount, tabIndex); 233 int right = w - 1; 234 int bottom = h - 1; 235 236 // Paint gap. 237 if (shouldFillGap(currentRun, tabIndex, x, y)) 238 { 239 g.translate(x, y); 240 g.setColor(getColorForGap(currentRun, x, y + 1)); 241 g.fillRect(1, 0, 5, 3); 242 g.fillRect(1, 3, 2, 2); 243 g.translate(-x, -y); 244 } 245 246 g.translate(x, y); 247 248 boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme; 249 Color oceanSelectedBorder = 250 UIManager.getColor("TabbedPane.borderHightlightColor"); 251 if (isOcean && isSelected) 252 g.setColor(oceanSelectedBorder); 253 else 254 g.setColor(darkShadow); 255 256 // Slant 257 g.drawLine(1, 5, 6, 0); 258 // Top. 259 g.drawLine(6, 0, right, 0); 260 // Right. 261 int lastIndex = lastTabInRun(tabCount, currentRun); 262 if (tabIndex == lastIndex) 263 g.drawLine(right, 1, right, bottom); 264 // Left. 265 int selectedIndex = tabPane.getSelectedIndex(); 266 if (isOcean && tabIndex - 1 == selectedIndex 267 && currentRun == getRunForTab(tabCount, selectedIndex)) 268 { 269 g.setColor(oceanSelectedBorder); 270 } 271 if (tabIndex != tabRuns[runCount - 1]) 272 { 273 if (isOcean && isSelected) 274 { 275 g.drawLine(0, 6, 0, bottom); 276 g.setColor(darkShadow); 277 g.drawLine(0, 0, 0, 5); 278 } 279 else 280 { 281 g.drawLine(0, 0, 0, bottom); 282 } 283 } 284 else 285 { 286 g.drawLine(0, 6, 0, bottom); 287 } 288 289 // Paint the highlight. 290 g.setColor(isSelected ? selectHighlight : highlight); 291 // Slant. 292 g.drawLine(1, 6, 6, 1); 293 // Top. 294 g.drawLine(6, 1, right, 1); 295 // Left. 296 g.drawLine(1, 6, 1, bottom); 297 int firstIndex = tabRuns[currentRun]; 298 if (tabIndex == firstIndex && tabIndex != tabRuns[runCount - 1]) 299 { 300 if (tabPane.getSelectedIndex() == tabRuns[currentRun + 1]) 301 g.setColor(selectHighlight); 302 else 303 g.setColor(highlight); 304 g.drawLine(1, 0, 1, 4); 305 } 306 307 g.translate(-x, -y); 308 } 309 310 /** 311 * Paints the border for a tab assuming that the tab position is at the left 312 * ({@link #LEFT}). 313 * 314 * @param tabIndex the tab index. 315 * @param g the graphics device. 316 * @param x the x-coordinate for the tab's bounding rectangle. 317 * @param y the y-coordinate for the tab's bounding rectangle. 318 * @param w the width for the tab's bounding rectangle. 319 * @param h the height for the tab's bounding rectangle. 320 * @param btm ??? 321 * @param rght ??? 322 * @param isSelected indicates whether the tab is selected. 323 */ 324 protected void paintLeftTabBorder(int tabIndex, Graphics g, int x, int y, 325 int w, int h, int btm, int rght, boolean isSelected) 326 { 327 g.translate(x, y); 328 int bottom = h - 1; 329 int right = w - 1; 330 331 int tabCount = tabPane.getTabCount(); 332 int currentRun = getRunForTab(tabCount, tabIndex); 333 int firstIndex = tabRuns[currentRun]; 334 335 // Paint the part of the above tab. 336 if (tabIndex != firstIndex && tabIndex > 0 && tabsOpaque) 337 { 338 Color c; 339 if (tabPane.getSelectedIndex() == tabIndex - 1) 340 c = selectColor; 341 else 342 c = getUnselectedBackground(tabIndex - 1); 343 g.setColor(c); 344 g.fillRect(2, 0, 4, 3); 345 g.drawLine(2, 3, 2, 3); 346 } 347 348 // Paint the highlight. 349 boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme; 350 if (isOcean) 351 { 352 g.setColor(isSelected ? selectHighlight : MetalLookAndFeel.getWhite()); 353 } 354 else 355 { 356 g.setColor(isSelected ? selectHighlight : highlight); 357 } 358 // Slant. 359 g.drawLine(1, 6, 6, 1); 360 // Left. 361 g.drawLine(1, 6, 1, bottom); 362 // Top. 363 g.drawLine(6, 1, right, 1); 364 if (tabIndex != firstIndex) 365 { 366 if (isOcean) 367 { 368 g.setColor(MetalLookAndFeel.getWhite()); 369 } 370 g.drawLine(1, 0, 1, 4); 371 } 372 373 // Paint border. 374 Color oceanSelectedBorder = 375 UIManager.getColor("TabbedPane.borderHightlightColor"); 376 if (isOcean && isSelected) 377 { 378 g.setColor(oceanSelectedBorder); 379 } 380 else 381 { 382 g.setColor(darkShadow); 383 } 384 385 // Slant. 386 g.drawLine(1, 5, 6, 0); 387 // Top. 388 g.drawLine(6, 0, right, 0); 389 // Bottom. 390 int lastIndex = lastTabInRun(tabCount, currentRun); 391 if (tabIndex == lastIndex) 392 { 393 g.drawLine(0, bottom, right, bottom); 394 } 395 // Left. 396 if (isOcean) 397 { 398 if (tabPane.getSelectedIndex() == tabIndex - 1) 399 { 400 g.drawLine(0, 6, 0, bottom); 401 if (tabIndex != firstIndex) 402 { 403 g.setColor(oceanSelectedBorder); 404 g.drawLine(0, 0, 0, 5); 405 } 406 } 407 else if (isSelected) 408 { 409 g.drawLine(0, 5, 0, bottom); 410 if (tabIndex != firstIndex) 411 { 412 g.setColor(darkShadow); 413 g.drawLine(0, 0, 0, 5); 414 } 415 } 416 else if (tabIndex != firstIndex) 417 { 418 g.drawLine(0, 0, 0, bottom); 419 } 420 else 421 { 422 g.drawLine(0, 6, 0, bottom); 423 } 424 } 425 else 426 { 427 if (tabIndex != firstIndex) 428 { 429 g.drawLine(0, 0, 0, bottom); 430 } 431 else 432 { 433 g.drawLine(0, 6, 0, bottom); 434 } 435 } 436 437 g.translate(-x, -y); 438 } 439 440 /** 441 * Paints the border for a tab assuming that the tab position is at the right 442 * ({@link #RIGHT}). 443 * 444 * @param tabIndex the tab index. 445 * @param g the graphics device. 446 * @param x the x-coordinate for the tab's bounding rectangle. 447 * @param y the y-coordinate for the tab's bounding rectangle. 448 * @param w the width for the tab's bounding rectangle. 449 * @param h the height for the tab's bounding rectangle. 450 * @param btm ??? 451 * @param rght ??? 452 * @param isSelected indicates whether the tab is selected. 453 */ 454 protected void paintRightTabBorder(int tabIndex, Graphics g, int x, int y, 455 int w, int h, int btm, int rght, boolean isSelected) 456 { 457 g.translate(x, y); 458 int bottom = h - 1; 459 int right = w - 1; 460 461 int tabCount = tabPane.getTabCount(); 462 int currentRun = getRunForTab(tabCount, tabIndex); 463 int firstIndex = tabRuns[currentRun]; 464 465 // Paint part of the above tab. 466 if (tabIndex != firstIndex && tabIndex > 0 && tabsOpaque) 467 { 468 Color c; 469 if (tabPane.getSelectedIndex() == tabIndex - 1) 470 c = selectColor; 471 else 472 c = getUnselectedBackground(tabIndex - 1); 473 g.setColor(c); 474 g.fillRect(right - 5, 0, 5, 3); 475 g.fillRect(right - 2, 3, 2, 2); 476 } 477 478 // Paint highlight. 479 g.setColor(isSelected ? selectHighlight : highlight); 480 481 // Slant. 482 g.drawLine(right - 6, 1, right - 1, 6); 483 // Top. 484 g.drawLine(0, 1, right - 6, 1); 485 // Left. 486 if (! isSelected) 487 { 488 g.drawLine(0, 1, 0, bottom); 489 } 490 491 // Paint border. 492 boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme; 493 Color oceanSelectedBorder = 494 UIManager.getColor("TabbedPane.borderHightlightColor"); 495 if (isOcean && isSelected) 496 { 497 g.setColor(oceanSelectedBorder); 498 } 499 else 500 { 501 g.setColor(darkShadow); 502 } 503 504 // Bottom. 505 int lastIndex = lastTabInRun(tabCount, currentRun); 506 if (tabIndex == lastIndex) 507 { 508 g.drawLine(0, bottom, right, bottom); 509 } 510 // Slant. 511 if (isOcean && tabPane.getSelectedIndex() == tabIndex - 1) 512 { 513 g.setColor(oceanSelectedBorder); 514 } 515 g.drawLine(right - 6, 0, right, 6); 516 // Top. 517 g.drawLine(0, 0, right - 6, 0); 518 // Right. 519 if (isOcean && isSelected) 520 { 521 g.drawLine(right, 6, right, bottom); 522 if (tabIndex != firstIndex) 523 { 524 g.setColor(darkShadow); 525 g.drawLine(right, 0, right, 5); 526 } 527 } 528 else if (isOcean && tabPane.getSelectedIndex() == tabIndex - 1) 529 { 530 if (tabIndex != firstIndex) 531 { 532 g.setColor(oceanSelectedBorder); 533 g.drawLine(right, 0, right, 6); 534 } 535 g.setColor(darkShadow); 536 g.drawLine(right, 7, right, bottom); 537 } 538 else if (tabIndex != firstIndex) 539 { 540 g.drawLine(right, 0, right, bottom); 541 } 542 else 543 { 544 g.drawLine(right, 6, right, bottom); 545 } 546 g.translate(-x, -y); 547 } 548 549 /** 550 * Paints the border for a tab assuming that the tab position is at the bottom 551 * ({@link #BOTTOM}). 552 * 553 * @param tabIndex the tab index. 554 * @param g the graphics device. 555 * @param x the x-coordinate for the tab's bounding rectangle. 556 * @param y the y-coordinate for the tab's bounding rectangle. 557 * @param w the width for the tab's bounding rectangle. 558 * @param h the height for the tab's bounding rectangle. 559 * @param btm ??? 560 * @param rght ??? 561 * @param isSelected indicates whether the tab is selected. 562 */ 563 protected void paintBottomTabBorder(int tabIndex, Graphics g, int x, int y, 564 int w, int h, int btm, int rght, boolean isSelected) 565 { 566 int bottom = h - 1; 567 int right = w - 1; 568 569 int tabCount = tabPane.getTabCount(); 570 int currentRun = getRunForTab(tabCount, tabIndex); 571 // Paint gap if necessary. 572 if (shouldFillGap(currentRun, tabIndex, x, y)) 573 { 574 g.translate(x, y); 575 g.setColor(getColorForGap(currentRun, x, y)); 576 g.fillRect(1, bottom - 4, 3, 5); 577 g.fillRect(4, bottom - 1, 2, 2); 578 g.translate(-x, -y); 579 } 580 581 g.translate(x, y); 582 583 // Paint border. 584 boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme; 585 Color oceanSelectedBorder = 586 UIManager.getColor("TabbedPane.borderHightlightColor"); 587 if (isOcean && isSelected) 588 { 589 g.setColor(oceanSelectedBorder); 590 } 591 else 592 { 593 g.setColor(darkShadow); 594 } 595 // Slant. 596 g.drawLine(1, bottom - 5, 6, bottom); 597 // Bottom. 598 g.drawLine(6, bottom, right, bottom); 599 // Right. 600 int lastIndex = lastTabInRun(tabCount, currentRun); 601 if (tabIndex == lastIndex) 602 { 603 g.drawLine(right, 0, right, bottom); 604 } 605 // Left. 606 if (isOcean && isSelected) 607 { 608 g.drawLine(0, 0, 0, bottom - 5); 609 610 // Paint a connecting line to the tab below for all 611 // but the first tab in the last run. 612 if (tabIndex != tabRuns[runCount-1]) 613 { 614 g.setColor(darkShadow); 615 g.drawLine(0, bottom - 5, 0, bottom); 616 } 617 } 618 else 619 { 620 if (isOcean && tabIndex == tabPane.getSelectedIndex() + 1) 621 { 622 g.setColor(oceanSelectedBorder); 623 } 624 if (tabIndex != tabRuns[runCount - 1]) 625 { 626 g.drawLine(0, 0, 0, bottom); 627 } 628 else 629 { 630 g.drawLine(0, 0, 0, bottom - 6); 631 } 632 } 633 634 // Paint highlight. 635 g.setColor(isSelected ? selectHighlight : highlight); 636 // Slant. 637 g.drawLine(1, bottom - 6, 6, bottom - 1); 638 // Left. 639 g.drawLine(1, 0, 1, bottom - 6); 640 641 int firstIndex = tabRuns[currentRun]; 642 if (tabIndex == firstIndex && tabIndex != tabRuns[runCount - 1]) 643 { 644 if (tabPane.getSelectedIndex() == tabRuns[currentRun + 1]) 645 { 646 g.setColor(selectHighlight); 647 } 648 else 649 { 650 g.setColor(highlight); 651 } 652 g.drawLine(1, bottom - 4, 1, bottom); 653 } 654 655 g.translate(-x, -y); 656 } 657 658 /** 659 * Paints the background for a tab. 660 * 661 * @param g the graphics device. 662 * @param tabPlacement the tab placement ({@link #TOP}, {@link #LEFT}, 663 * {@link #BOTTOM} or {@link #RIGHT}). 664 * @param tabIndex the index of the tab to draw the border for. 665 * @param x the x-coordinate for the tab's bounding rectangle. 666 * @param y the y-coordinate for the tab's bounding rectangle. 667 * @param w the width for the tab's bounding rectangle. 668 * @param h the height for the tab's bounding rectangle. 669 * @param isSelected indicates whether or not the tab is selected. 670 */ 671 protected void paintTabBackground(Graphics g, int tabPlacement, 672 int tabIndex, int x, int y, int w, int h, boolean isSelected) 673 { 674 if (isSelected) 675 g.setColor(selectColor); 676 else 677 g.setColor(getUnselectedBackground(tabIndex)); 678 679 switch (tabPlacement) 680 { 681 case LEFT: 682 g.fillRect(x + 5, y + 1, w - 5, h - 1); 683 g.fillRect(x + 2, y + 4, 3, h - 4); 684 break; 685 case BOTTOM: 686 g.fillRect(x + 2, y, w - 2, h - 3); 687 g.fillRect(x + 5, y + h - 4, w - 5, 3); 688 break; 689 case RIGHT: 690 g.fillRect(x, y + 1, w - 4, h - 1); 691 g.fillRect(x + w - 4, y + 5, 3, h - 5); 692 break; 693 case TOP: 694 default: 695 g.fillRect(x + 4, y + 2, w - 4, h - 2); 696 g.fillRect(x + 2, y + 5, 2, h - 5); 697 } 698 } 699 700 /** 701 * This method paints the focus rectangle around the selected tab. 702 * 703 * @param g The Graphics object to paint with. 704 * @param tabPlacement The JTabbedPane's tab placement. 705 * @param rects The array of rectangles keeping track of size and position. 706 * @param tabIndex The tab index. 707 * @param iconRect The icon bounds. 708 * @param textRect The text bounds. 709 * @param isSelected Whether this tab is selected. 710 */ 711 protected void paintFocusIndicator(Graphics g, int tabPlacement, 712 Rectangle[] rects, int tabIndex, 713 Rectangle iconRect, Rectangle textRect, 714 boolean isSelected) 715 { 716 if (tabPane.hasFocus() && isSelected) 717 { 718 Rectangle rect = rects[tabIndex]; 719 720 g.setColor(focus); 721 g.translate(rect.x, rect.y); 722 723 switch (tabPlacement) 724 { 725 case LEFT: 726 // Top line 727 g.drawLine(7, 2, rect.width-2, 2); 728 729 // Right line 730 g.drawLine(rect.width-1, 2, rect.width-1, rect.height-3); 731 732 // Bottom line 733 g.drawLine(rect.width-2, rect.height-2, 3, rect.height-2); 734 735 // Left line 736 g.drawLine(2, rect.height-3, 2, 7); 737 738 // Slant 739 g.drawLine(2, 6, 6, 2); 740 break; 741 case RIGHT: 742 // Top line 743 g.drawLine(1, 2, rect.width-8, 2); 744 745 // Slant 746 g.drawLine(rect.width-7, 2, rect.width-3, 6); 747 748 // Right line 749 g.drawLine(rect.width-3, 7, rect.width-3, rect.height-3); 750 751 // Bottom line 752 g.drawLine(rect.width-3, rect.height-2, 2, rect.height-2); 753 754 // Left line 755 g.drawLine(1, rect.height-2, 1, 2); 756 break; 757 case BOTTOM: 758 // Top line 759 g.drawLine(2, 1, rect.width-2, 1); 760 761 // Right line 762 g.drawLine(rect.width-1, 2, rect.width-1, rect.height-3); 763 764 // Bottom line 765 g.drawLine(7, rect.height-3, rect.width-2, rect.height-3); 766 767 // Slant 768 g.drawLine(6, rect.height-3, 2, rect.height-7); 769 770 // Left line 771 g.drawLine(2, rect.height-8, 2, 2); 772 773 break; 774 case TOP: 775 default: 776 // Top line 777 g.drawLine(6, 2, rect.width-2, 2); 778 779 // Right line 780 g.drawLine(rect.width-1, 2, rect.width-1, rect.height-3); 781 782 // Bottom line 783 g.drawLine(3, rect.height-3, rect.width-2, rect.height-3); 784 785 // Left line 786 g.drawLine(2, rect.height-3, 2, 7); 787 788 // Slant 789 g.drawLine(2, 6, 6, 2); 790 791 } 792 793 g.translate(-rect.x, -rect.y); 794 } 795 } 796 797 /** 798 * Returns <code>true</code> if the tabs in the specified run should be 799 * padded to make the run fill the width/height of the {@link JTabbedPane}. 800 * 801 * @param tabPlacement the tab placement for the {@link JTabbedPane} (one of 802 * {@link #TOP}, {@link #BOTTOM}, {@link #LEFT} and {@link #RIGHT}). 803 * @param run the run index. 804 * 805 * @return A boolean. 806 */ 807 protected boolean shouldPadTabRun(int tabPlacement, int run) 808 { 809 // as far as I can tell, all runs should be padded except the last run 810 // (which is drawn at the very top for tabPlacement == TOP) 811 return run < this.runCount - 1; 812 } 813 814 /** 815 * Installs the defaults for this UI. This method calls super.installDefaults 816 * and then loads the Metal specific defaults for TabbedPane. 817 */ 818 protected void installDefaults() 819 { 820 super.installDefaults(); 821 selectColor = UIManager.getColor("TabbedPane.selected"); 822 selectHighlight = UIManager.getColor("TabbedPane.selectHighlight"); 823 tabAreaBackground = UIManager.getColor("TabbedPane.tabAreaBackground"); 824 tabsOpaque = UIManager.getBoolean("TabbedPane.tabsOpaque"); 825 minTabWidth = 0; 826 } 827 828 /** 829 * Returns the color for the gap. 830 * 831 * @param currentRun - The current run to return the color for 832 * @param x - The x position of the current run 833 * @param y - The y position of the current run 834 * 835 * @return the color for the gap in the current run. 836 */ 837 protected Color getColorForGap(int currentRun, int x, int y) 838 { 839 int index = tabForCoordinate(tabPane, x, y); 840 int selected = tabPane.getSelectedIndex(); 841 if (selected == index) 842 return selectColor; 843 return tabAreaBackground; 844 } 845 846 /** 847 * Returns true if the gap should be filled in. 848 * 849 * @param currentRun - The current run 850 * @param tabIndex - The current tab 851 * @param x - The x position of the tab 852 * @param y - The y position of the tab 853 * 854 * @return true if the gap at the current run should be filled 855 */ 856 protected boolean shouldFillGap(int currentRun, int tabIndex, int x, int y) 857 { 858 // As far as I can tell, the gap is never filled in. 859 return false; 860 } 861 862 /** 863 * Paints the highlight below the tab, if there is one. 864 */ 865 protected void paintHighlightBelowTab() 866 { 867 int selected = tabPane.getSelectedIndex(); 868 int tabPlacement = tabPane.getTabPlacement(); 869 Rectangle bounds = getTabBounds(tabPane, selected); 870 871 hg.setColor(selectColor); 872 int x = bounds.x; 873 int y = bounds.y; 874 int w = bounds.width; 875 int h = bounds.height; 876 877 if (tabPlacement == TOP) 878 hg.fillRect(x, y + h - 2, w, 30); 879 else if (tabPlacement == LEFT) 880 hg.fillRect(x + w - 1, y, 20, h); 881 else if (tabPlacement == BOTTOM) 882 hg.fillRect(x, y - h + 2, w, 30); 883 else if (tabPlacement == RIGHT) 884 hg.fillRect(x - 18, y, 20, h); 885 else 886 throw new AssertionError("Unrecognised 'tabPlacement' argument."); 887 hg = null; 888 } 889 890 /** 891 * Returns true if we should rotate the tab runs. 892 * 893 * @param tabPlacement - The current tab placement. 894 * @param selectedRun - The selected run. 895 * 896 * @return true if the tab runs should be rotated. 897 */ 898 protected boolean shouldRotateTabRuns(int tabPlacement, 899 int selectedRun) 900 { 901 // false because tab runs are not rotated in the MetalLookAndFeel 902 return false; 903 } 904 905 protected int calculateMaxTabHeight(int tabPlacement) 906 { 907 // FIXME: Why is this overridden? 908 return super.calculateMaxTabHeight(tabPlacement); 909 } 910 911 /** 912 * Returns the amount of overlay among the tabs. In 913 * the Metal L&F the overlay for LEFT and RIGHT placement 914 * is half of the maxTabHeight. For TOP and BOTTOM placement 915 * the tabs do not overlay. 916 * 917 * @param tabPlacement the placement 918 * 919 * @return the amount of overlay among the tabs 920 */ 921 protected int getTabRunOverlay(int tabPlacement) 922 { 923 int overlay = 0; 924 if (tabPlacement == LEFT || tabPlacement == RIGHT) 925 { 926 int maxHeight = calculateMaxTabHeight(tabPlacement); 927 overlay = maxTabHeight / 2; 928 } 929 return overlay; 930 } 931 932 /** 933 * Paints the upper edge of the content border. 934 * 935 * @param g the graphics to use for painting 936 * @param tabPlacement the tab placement 937 * @param selectedIndex the index of the selected tab 938 * @param x the upper left coordinate of the content area 939 * @param y the upper left coordinate of the content area 940 * @param w the width of the content area 941 * @param h the height of the content area 942 */ 943 protected void paintContentBorderTopEdge(Graphics g, int tabPlacement, 944 int selectedIndex, int x, int y, 945 int w, int h) 946 { 947 Color oceanSelectedBorder = 948 UIManager.getColor("TabbedPane.borderHightlightColor"); 949 boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme; 950 if (isOcean) 951 { 952 g.setColor(oceanSelectedBorder); 953 } 954 else 955 { 956 g.setColor(selectHighlight); 957 } 958 959 Rectangle rect = selectedIndex < 0 ? null : 960 getTabBounds(selectedIndex, calcRect); 961 962 // If tabs are not placed on TOP, or if the selected tab is not in the 963 // run directly above the content or the selected tab is not visible, 964 // then we draw an unbroken line. 965 if (tabPlacement != TOP || selectedIndex < 0 966 || rect.y + rect.height + 1 < y || rect.x < x || rect.x > x + w) 967 { 968 g.drawLine(x, y, x + w - 2, y); 969 if (isOcean && tabPlacement == TOP) 970 { 971 g.setColor(MetalLookAndFeel.getWhite()); 972 g.drawLine(x, y + 1, x + w - 2, y + 1); 973 } 974 } 975 else 976 { 977 boolean isLast = isLastTabInRun(selectedIndex); 978 if (isLast) 979 { 980 g.drawLine(x, y, rect.x + 1, y); 981 } 982 else 983 { 984 g.drawLine(x, y, rect.x, y); 985 } 986 987 int right = x + w - 1; 988 if (rect.x + rect.width < right - 1) 989 { 990 if (isLast) 991 { 992 g.drawLine(rect.x + rect.width - 1, y, right - 1, y); 993 } 994 else 995 { 996 g.drawLine(rect.x + rect.width, y, right - 1, y); 997 } 998 } 999 else 1000 { 1001 g.setColor(shadow); 1002 g.drawLine(x + w - 2, y, x + w - 2, y); 1003 } 1004 1005 // When in OceanTheme, draw another white line. 1006 if (isOcean) 1007 { 1008 g.setColor(MetalLookAndFeel.getWhite()); 1009 if (isLast) 1010 { 1011 g.drawLine(x, y + 1, rect.x + 1, y + 1); 1012 } 1013 else 1014 { 1015 g.drawLine(x, y + 1, rect.x, y + 1); 1016 } 1017 1018 if (rect.x + rect.width < right - 1) 1019 { 1020 if (isLast) 1021 { 1022 g.drawLine(rect.x + rect.width - 1, y + 1, right - 1, 1023 y + 1); 1024 } 1025 else 1026 { 1027 g.drawLine(rect.x + rect.width, y + 1, right - 1, y + 1); 1028 } 1029 } 1030 else 1031 { 1032 g.setColor(shadow); 1033 g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1); 1034 } 1035 } 1036 } 1037 } 1038 1039 /** 1040 * Paints the lower edge of the content border. 1041 * 1042 * @param g the graphics to use for painting 1043 * @param tabPlacement the tab placement 1044 * @param selectedIndex the index of the selected tab 1045 * @param x the upper left coordinate of the content area 1046 * @param y the upper left coordinate of the content area 1047 * @param w the width of the content area 1048 * @param h the height of the content area 1049 */ 1050 protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement, 1051 int selectedIndex, int x, int y, 1052 int w, int h) 1053 { 1054 g.setColor(darkShadow); 1055 1056 // If tabs are not placed on BOTTOM, or if the selected tab is not in the 1057 // run directly below the content or the selected tab is not visible, 1058 // then we draw an unbroken line. 1059 Rectangle rect = selectedIndex < 0 ? null : 1060 getTabBounds(selectedIndex, calcRect); 1061 boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme; 1062 Color oceanSelectedBorder = 1063 UIManager.getColor("TabbedPane.borderHightlightColor"); 1064 if (tabPlacement != BOTTOM || selectedIndex < 0 || rect.y - 1 > h 1065 || rect.x < x || rect.x > x + w) 1066 { 1067 if (isOcean && tabPlacement == BOTTOM) 1068 { 1069 g.setColor(oceanSelectedBorder); 1070 } 1071 g.drawLine(x, y + h - 1, x + w - 1, y + h - 1); 1072 } 1073 else 1074 { 1075 boolean isLast = isLastTabInRun(selectedIndex); 1076 if (isOcean) 1077 { 1078 g.setColor(oceanSelectedBorder); 1079 } 1080 1081 int bottom = y + h - 1; 1082 int right = x + w - 1; 1083 if (isLast) 1084 { 1085 g.drawLine(x, bottom, rect.x, bottom); 1086 } 1087 else 1088 { 1089 g.drawLine(x, bottom, rect.x - 1, bottom); 1090 } 1091 1092 if (rect.x + rect.width < x + w - 2) 1093 { 1094 if (isLast) 1095 { 1096 g.drawLine(rect.x + rect.width - 1, bottom, right, bottom); 1097 } 1098 else 1099 { 1100 g.drawLine(rect.x + rect.width, bottom, right, bottom); 1101 } 1102 } 1103 } 1104 } 1105 1106 /** 1107 * Paints the left edge of the content border. 1108 * 1109 * @param g the graphics to use for painting 1110 * @param tabPlacement the tab placement 1111 * @param selectedIndex the index of the selected tab 1112 * @param x the upper left coordinate of the content area 1113 * @param y the upper left coordinate of the content area 1114 * @param w the width of the content area 1115 * @param h the height of the content area 1116 */ 1117 protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement, 1118 int selectedIndex, int x, int y, 1119 int w, int h) 1120 { 1121 boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme; 1122 Color oceanSelectedBorder = 1123 UIManager.getColor("TabbedPane.borderHightlightColor"); 1124 Rectangle rect = selectedIndex < 0 ? null : 1125 getTabBounds(selectedIndex, calcRect); 1126 1127 if (isOcean) 1128 { 1129 g.setColor(oceanSelectedBorder); 1130 } 1131 else 1132 { 1133 g.setColor(selectHighlight); 1134 } 1135 1136 // If tabs are not placed on LEFT, or if the selected tab is not in the 1137 // run directly left to the content or the selected tab is not visible, 1138 // then we draw an unbroken line. 1139 if (tabPlacement != LEFT || selectedIndex < 0 1140 || rect.x + rect.width + 1 < x || rect.y < y || rect.y > y + h) 1141 { 1142 g.drawLine(x, y + 1, x, y + h - 2); 1143 if (isOcean && tabPlacement == LEFT) 1144 { 1145 g.setColor(MetalLookAndFeel.getWhite()); 1146 g.drawLine(x, y + 1, x, y + h - 2); 1147 } 1148 } 1149 else 1150 { 1151 g.drawLine(x, y, x, rect.y + 1); 1152 if (rect.y + rect.height < y + h - 2) 1153 { 1154 g.drawLine(x, rect.y + rect.height + 1, x, y + h + 2); 1155 } 1156 if (isOcean) 1157 { 1158 g.setColor(MetalLookAndFeel.getWhite()); 1159 g.drawLine(x + 1, y + 1, x + 1, rect.y + 1); 1160 if (rect.y + rect.height < y + h - 2) 1161 { 1162 g.drawLine(x + 1, rect.y + rect.height + 1, x + 1, y + h + 2); 1163 } 1164 } 1165 } 1166 1167 } 1168 1169 /** 1170 * Paints the right edge of the content border. 1171 * 1172 * @param g the graphics to use for painting 1173 * @param tabPlacement the tab placement 1174 * @param selectedIndex the index of the selected tab 1175 * @param x the upper left coordinate of the content area 1176 * @param y the upper left coordinate of the content area 1177 * @param w the width of the content area 1178 * @param h the height of the content area 1179 */ 1180 protected void paintContentBorderRightEdge(Graphics g, int tabPlacement, 1181 int selectedIndex, int x, int y, 1182 int w, int h) 1183 { 1184 g.setColor(darkShadow); 1185 Rectangle rect = selectedIndex < 0 ? null : 1186 getTabBounds(selectedIndex, calcRect); 1187 boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme; 1188 Color oceanSelectedBorder = 1189 UIManager.getColor("TabbedPane.borderHightlightColor"); 1190 1191 // If tabs are not placed on RIGHT, or if the selected tab is not in the 1192 // run directly right to the content or the selected tab is not visible, 1193 // then we draw an unbroken line. 1194 if (tabPlacement != RIGHT || selectedIndex < 0 || rect.x - 1 > w 1195 || rect.y < y || rect.y > y + h) 1196 { 1197 if (isOcean && tabPlacement == RIGHT) 1198 { 1199 g.setColor(oceanSelectedBorder); 1200 } 1201 g.drawLine(x + w - 1, y, x + w - 1, y + h - 1); 1202 } 1203 else 1204 { 1205 if (isOcean) 1206 { 1207 g.setColor(oceanSelectedBorder); 1208 } 1209 g.drawLine(x + w - 1, y, x + w - 1, rect.y); 1210 1211 if (rect.y + rect.height < y + h - 2) 1212 { 1213 g.drawLine(x + w - 1, rect.y + rect.height, x + w - 1, y + h - 2); 1214 } 1215 } 1216 } 1217 1218 /** 1219 * Determines if the specified tab is the last tab in its tab run. 1220 * 1221 * @param tabIndex the index of the tab 1222 * 1223 * @return if the specified tab is the last tab in its tab run 1224 */ 1225 private boolean isLastTabInRun(int tabIndex) 1226 { 1227 int count = tabPane.getTabCount(); 1228 int run = getRunForTab(count, tabIndex); 1229 int lastIndex = lastTabInRun(count, run); 1230 return tabIndex == lastIndex; 1231 } 1232 1233 /** 1234 * Returns the background for an unselected tab. This first asks the 1235 * JTabbedPane for the background at the specified tab index, if this 1236 * is an UIResource (that means, it is inherited from the JTabbedPane) 1237 * and the TabbedPane.unselectedBackground UI property is not null, 1238 * this returns the value of the TabbedPane.unselectedBackground property, 1239 * otherwise the value returned by the JTabbedPane. 1240 * 1241 * @param tabIndex the index of the tab for which we query the background 1242 * 1243 * @return the background for an unselected tab 1244 */ 1245 private Color getUnselectedBackground(int tabIndex) 1246 { 1247 Color bg = tabPane.getBackgroundAt(tabIndex); 1248 Color unselectedBackground = 1249 UIManager.getColor("TabbedPane.unselectedBackground"); 1250 if (bg instanceof UIResource && unselectedBackground != null) 1251 bg = unselectedBackground; 1252 return bg; 1253 } 1254 1255 protected int getTabLabelShiftX(int tabPlacement, 1256 int index, 1257 boolean isSelected) 1258 { 1259 return 0; 1260 } 1261 1262 protected int getTabLabelShiftY(int tabPlacement, 1263 int index, 1264 boolean isSelected) 1265 { 1266 return 0; 1267 } 1268 1269}