| 1 30 31 package com.jgoodies.looks.plastic; 32 33 import java.awt.Color ; 34 import java.awt.Component ; 35 import java.awt.Container ; 36 import java.awt.Dimension ; 37 import java.awt.Font ; 38 import java.awt.FontMetrics ; 39 import java.awt.Graphics ; 40 import java.awt.Graphics2D ; 41 import java.awt.Insets ; 42 import java.awt.LayoutManager ; 43 import java.awt.Point ; 44 import java.awt.Polygon ; 45 import java.awt.Rectangle ; 46 import java.awt.Shape ; 47 import java.awt.event.ActionEvent ; 48 import java.awt.event.ActionListener ; 49 import java.awt.event.MouseEvent ; 50 import java.beans.PropertyChangeEvent ; 51 import java.beans.PropertyChangeListener ; 52 53 import javax.swing.AbstractAction ; 54 import javax.swing.Action ; 55 import javax.swing.ActionMap ; 56 import javax.swing.Icon ; 57 import javax.swing.JButton ; 58 import javax.swing.JComponent ; 59 import javax.swing.JPanel ; 60 import javax.swing.JTabbedPane ; 61 import javax.swing.JViewport ; 62 import javax.swing.SwingConstants ; 63 import javax.swing.SwingUtilities ; 64 import javax.swing.UIManager ; 65 import javax.swing.event.ChangeEvent ; 66 import javax.swing.event.ChangeListener ; 67 import javax.swing.plaf.ComponentUI ; 68 import javax.swing.plaf.UIResource ; 69 import javax.swing.plaf.basic.BasicTabbedPaneUI ; 70 import javax.swing.plaf.metal.MetalTabbedPaneUI ; 71 import javax.swing.text.View ; 72 73 import com.jgoodies.looks.LookUtils; 74 import com.jgoodies.looks.Options; 75 76 112 public final class PlasticTabbedPaneUI extends MetalTabbedPaneUI { 113 114 115 117 120 private static boolean isTabIconsEnabled = Options.isTabIconsEnabled(); 121 122 127 private Boolean noContentBorder; 128 129 135 private Boolean embeddedTabs; 136 137 140 private AbstractRenderer renderer; 141 142 143 144 private ScrollableTabSupport tabScroller; 145 146 151 public static ComponentUI createUI(JComponent tabPane) { 152 return new PlasticTabbedPaneUI(); 153 } 154 155 160 public void installUI(JComponent c) { 161 super.installUI(c); 162 embeddedTabs = (Boolean ) c.getClientProperty(Options.EMBEDDED_TABS_KEY); 163 noContentBorder = (Boolean ) c.getClientProperty(Options.NO_CONTENT_BORDER_KEY); 164 renderer = createRenderer(tabPane); 165 } 166 167 171 public void uninstallUI(JComponent c) { 172 renderer = null; 173 super.uninstallUI(c); 174 } 175 176 181 protected void installComponents() { 182 if (scrollableTabLayoutEnabled()) { 183 if (tabScroller == null) { 184 tabScroller = new ScrollableTabSupport(tabPane.getTabPlacement()); 185 tabPane.add(tabScroller.viewport); 186 } 187 } 188 } 189 190 195 protected void uninstallComponents() { 196 if (scrollableTabLayoutEnabled()) { 197 tabPane.remove(tabScroller.viewport); 198 tabPane.remove(tabScroller.scrollForwardButton); 199 tabPane.remove(tabScroller.scrollBackwardButton); 200 tabScroller = null; 201 } 202 } 203 204 protected void installListeners() { 205 super.installListeners(); 206 if ((mouseListener != null) && (LookUtils.IS_JAVA_1_4)) { 213 if (scrollableTabLayoutEnabled()) { 214 tabPane.removeMouseListener(mouseListener); 215 tabScroller.tabPanel.addMouseListener(mouseListener); 216 } 217 } 218 } 219 220 protected void uninstallListeners() { 221 if ((mouseListener != null) && (LookUtils.IS_JAVA_1_4)) { 222 if (scrollableTabLayoutEnabled()) { tabScroller.tabPanel.removeMouseListener(mouseListener); 224 } else { tabPane.removeMouseListener(mouseListener); 226 } 227 mouseListener = null; 228 } 229 super.uninstallListeners(); 230 } 231 232 protected void installKeyboardActions() { 233 super.installKeyboardActions(); 234 if (scrollableTabLayoutEnabled()) { 238 Action forwardAction = new ScrollTabsForwardAction(); 239 Action backwardAction = new ScrollTabsBackwardAction(); 240 ActionMap am = SwingUtilities.getUIActionMap(tabPane); 241 am.put("scrollTabsForwardAction", forwardAction); 242 am.put("scrollTabsBackwardAction", backwardAction); 243 tabScroller.scrollForwardButton.setAction(forwardAction); 244 tabScroller.scrollBackwardButton.setAction(backwardAction); 245 } 246 } 247 248 253 private boolean hasNoContentBorder() { 254 return Boolean.TRUE.equals(noContentBorder); 255 } 256 257 260 private boolean hasEmbeddedTabs() { 261 return Boolean.TRUE.equals(embeddedTabs); 262 } 263 264 269 private AbstractRenderer createRenderer(JTabbedPane tabbedPane) { 270 return hasEmbeddedTabs() 271 ? AbstractRenderer.createEmbeddedRenderer(tabbedPane) 272 : AbstractRenderer.createRenderer(tabPane); 273 } 274 275 280 protected PropertyChangeListener createPropertyChangeListener() { 281 return new MyPropertyChangeHandler(); 282 } 283 284 protected ChangeListener createChangeListener() { 285 return new TabSelectionHandler(); 286 } 287 288 291 private void doLayout() { 292 tabPane.revalidate(); 293 tabPane.repaint(); 294 } 295 296 300 private void tabPlacementChanged() { 301 renderer = createRenderer(tabPane); 302 if (scrollableTabLayoutEnabled()) { 303 tabScroller.createButtons(); 304 } 305 doLayout(); 306 } 307 308 312 private void embeddedTabsPropertyChanged(Boolean newValue) { 313 embeddedTabs = newValue; 314 renderer = createRenderer(tabPane); 315 doLayout(); 316 } 317 318 323 private void noContentBorderPropertyChanged(Boolean newValue) { 324 noContentBorder = newValue; 325 tabPane.repaint(); 326 } 327 328 public void paint(Graphics g, JComponent c) { 329 int selectedIndex = tabPane.getSelectedIndex(); 330 int tabPlacement = tabPane.getTabPlacement(); 331 332 ensureCurrentLayout(); 333 334 if (!scrollableTabLayoutEnabled()) { paintTabArea(g, tabPlacement, selectedIndex); 340 } 341 342 paintContentBorder(g, tabPlacement, selectedIndex); 344 } 345 346 protected void paintTab(Graphics g, int tabPlacement, Rectangle [] rects, 347 int tabIndex, Rectangle iconRect, Rectangle textRect) { 348 Rectangle tabRect = rects[tabIndex]; 349 int selectedIndex = tabPane.getSelectedIndex(); 350 boolean isSelected = selectedIndex == tabIndex; 351 Graphics2D g2 = null; 352 Polygon cropShape = null; 353 Shape save = null; 354 int cropx = 0; 355 int cropy = 0; 356 357 if (scrollableTabLayoutEnabled()) { 358 if (g instanceof Graphics2D ) { 359 g2 = (Graphics2D ) g; 360 361 Rectangle viewRect = tabScroller.viewport.getViewRect(); 363 int cropline; 364 switch (tabPlacement) { 365 case LEFT: 366 case RIGHT: 367 cropline = viewRect.y + viewRect.height; 368 if ((tabRect.y < cropline) 369 && (tabRect.y + tabRect.height > cropline)) { 370 cropShape = createCroppedTabClip(tabPlacement, tabRect, 371 cropline); 372 cropx = tabRect.x; 373 cropy = cropline - 1; 374 } 375 break; 376 case TOP: 377 case BOTTOM: 378 default: 379 cropline = viewRect.x + viewRect.width; 380 if ((tabRect.x < cropline) 381 && (tabRect.x + tabRect.width > cropline)) { 382 cropShape = createCroppedTabClip(tabPlacement, tabRect, 383 cropline); 384 cropx = cropline - 1; 385 cropy = tabRect.y; 386 } 387 } 388 if (cropShape != null) { 389 save = g2.getClip(); 390 g2.clip(cropShape); 391 } 392 } 393 } 394 395 paintTabBackground(g, tabPlacement, tabIndex, tabRect.x, tabRect.y, 396 tabRect.width, tabRect.height, isSelected); 397 398 paintTabBorder(g, tabPlacement, tabIndex, tabRect.x, tabRect.y, 399 tabRect.width, tabRect.height, isSelected); 400 401 String title = tabPane.getTitleAt(tabIndex); 402 Font font = tabPane.getFont(); 403 FontMetrics metrics = g.getFontMetrics(font); 404 Icon icon = getIconForTab(tabIndex); 405 406 layoutLabel(tabPlacement, metrics, tabIndex, title, icon, tabRect, 407 iconRect, textRect, isSelected); 408 409 paintText(g, tabPlacement, font, metrics, tabIndex, title, textRect, 410 isSelected); 411 412 paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected); 413 414 paintFocusIndicator(g, tabPlacement, rects, tabIndex, iconRect, 415 textRect, isSelected); 416 417 if (cropShape != null) { 418 paintCroppedTabEdge(g, tabPlacement, tabIndex, isSelected, cropx, 419 cropy); 420 g2.setClip(save); 421 } 422 } 423 424 446 private int xCropLen[] = { 1, 1, 0, 0, 1, 1, 2, 2 }; 447 448 private int yCropLen[] = { 0, 3, 3, 6, 6, 9, 9, 12 }; 449 450 private static final int CROP_SEGMENT = 12; 451 452 private Polygon createCroppedTabClip(int tabPlacement, Rectangle tabRect, 453 int cropline) { 454 int rlen = 0; 455 int start = 0; 456 int end = 0; 457 int ostart = 0; 458 459 switch (tabPlacement) { 460 case LEFT: 461 case RIGHT: 462 rlen = tabRect.width; 463 start = tabRect.x; 464 end = tabRect.x + tabRect.width; 465 ostart = tabRect.y; 466 break; 467 case TOP: 468 case BOTTOM: 469 default: 470 rlen = tabRect.height; 471 start = tabRect.y; 472 end = tabRect.y + tabRect.height; 473 ostart = tabRect.x; 474 } 475 int rcnt = rlen / CROP_SEGMENT; 476 if (rlen % CROP_SEGMENT > 0) { 477 rcnt++; 478 } 479 int npts = 2 + (rcnt * 8); 480 int xp[] = new int[npts]; 481 int yp[] = new int[npts]; 482 int pcnt = 0; 483 484 xp[pcnt] = ostart; 485 yp[pcnt++] = end; 486 xp[pcnt] = ostart; 487 yp[pcnt++] = start; 488 for (int i = 0; i < rcnt; i++) { 489 for (int j = 0; j < xCropLen.length; j++) { 490 xp[pcnt] = cropline - xCropLen[j]; 491 yp[pcnt] = start + (i * CROP_SEGMENT) + yCropLen[j]; 492 if (yp[pcnt] >= end) { 493 yp[pcnt] = end; 494 pcnt++; 495 break; 496 } 497 pcnt++; 498 } 499 } 500 if (tabPlacement == SwingConstants.TOP 501 || tabPlacement == SwingConstants.BOTTOM) { 502 return new Polygon (xp, yp, pcnt); 503 504 } 505 return new Polygon (yp, xp, pcnt); 507 } 508 509 512 private void paintCroppedTabEdge(Graphics g, int tabPlacement, 513 int tabIndex, boolean isSelected, int x, int y) { 514 switch (tabPlacement) { 515 case LEFT: 516 case RIGHT: 517 int xx = x; 518 g.setColor(shadow); 519 while (xx <= x + rects[tabIndex].width) { 520 for (int i = 0; i < xCropLen.length; i += 2) { 521 g.drawLine(xx + yCropLen[i], y - xCropLen[i], xx 522 + yCropLen[i + 1] - 1, y - xCropLen[i + 1]); 523 } 524 xx += CROP_SEGMENT; 525 } 526 break; 527 case TOP: 528 case BOTTOM: 529 default: 530 int yy = y; 531 g.setColor(shadow); 532 while (yy <= y + rects[tabIndex].height) { 533 for (int i = 0; i < xCropLen.length; i += 2) { 534 g.drawLine(x - xCropLen[i], yy + yCropLen[i], x 535 - xCropLen[i + 1], yy + yCropLen[i + 1] - 1); 536 } 537 yy += CROP_SEGMENT; 538 } 539 } 540 } 541 542 private void ensureCurrentLayout() { 543 if (!tabPane.isValid()) { 544 tabPane.validate(); 545 } 546 550 if (!tabPane.isValid()) { 551 TabbedPaneLayout layout = (TabbedPaneLayout) tabPane.getLayout(); 552 layout.calculateLayoutInfo(); 553 } 554 } 555 556 560 public int tabForCoordinate(JTabbedPane pane, int x, int y) { 561 ensureCurrentLayout(); 562 Point p = new Point (x, y); 563 564 if (scrollableTabLayoutEnabled()) { 565 translatePointToTabPanel(x, y, p); 566 Rectangle viewRect = tabScroller.viewport.getViewRect(); 567 if (!viewRect.contains(p)) { 568 return -1; 569 } 570 } 571 int tabCount = tabPane.getTabCount(); 572 for (int i = 0; i < tabCount; i++) { 573 if (rects[i].contains(p.x, p.y)) { 574 return i; 575 } 576 } 577 return -1; 578 } 579 580 protected Rectangle getTabBounds(int tabIndex, Rectangle dest) { 581 dest.width = rects[tabIndex].width; 582 dest.height = rects[tabIndex].height; 583 if (scrollableTabLayoutEnabled()) { Point vpp = tabScroller.viewport.getLocation(); 587 Point viewp = tabScroller.viewport.getViewPosition(); 588 dest.x = rects[tabIndex].x + vpp.x - viewp.x; 589 dest.y = rects[tabIndex].y + vpp.y - viewp.y; 590 } else { dest.x = rects[tabIndex].x; 592 dest.y = rects[tabIndex].y; 593 } 594 return dest; 595 } 596 597 601 private int getClosestTab(int x, int y) { 602 int min = 0; 603 int tabCount = Math.min(rects.length, tabPane.getTabCount()); 604 int max = tabCount; 605 int tabPlacement = tabPane.getTabPlacement(); 606 boolean useX = (tabPlacement == TOP || tabPlacement == BOTTOM); 607 int want = (useX) ? x : y; 608 609 while (min != max) { 610 int current = (max + min) / 2; 611 int minLoc; 612 int maxLoc; 613 614 if (useX) { 615 minLoc = rects[current].x; 616 maxLoc = minLoc + rects[current].width; 617 } else { 618 minLoc = rects[current].y; 619 maxLoc = minLoc + rects[current].height; 620 } 621 if (want < minLoc) { 622 max = current; 623 if (min == max) { 624 return Math.max(0, current - 1); 625 } 626 } else if (want >= maxLoc) { 627 min = current; 628 if (max - min <= 1) { 629 return Math.max(current + 1, tabCount - 1); 630 } 631 } else { 632 return current; 633 } 634 } 635 return min; 636 } 637 638 643 private Point translatePointToTabPanel(int srcx, int srcy, Point dest) { 644 Point vpp = tabScroller.viewport.getLocation(); 645 Point viewp = tabScroller.viewport.getViewPosition(); 646 dest.x = srcx - vpp.x + viewp.x; 647 dest.y = srcy - vpp.y + viewp.y; 648 return dest; 649 } 650 651 protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex) { 652 int tabCount = tabPane.getTabCount(); 653 654 Rectangle iconRect = new Rectangle (), 655 textRect = new Rectangle (); 656 Rectangle clipRect = g.getClipBounds(); 657 658 for (int i = runCount - 1; i >= 0; i--) { 660 int start = tabRuns[i]; 661 int next = tabRuns[(i == runCount - 1)? 0 : i + 1]; 662 int end = (next != 0? next - 1: tabCount - 1); 663 for (int j = end; j >= start; j--) { 664 if (j != selectedIndex && rects[j].intersects(clipRect)) { 665 paintTab(g, tabPlacement, rects, j, iconRect, textRect); 666 } 667 } 668 } 669 670 if (selectedIndex >= 0 && rects[selectedIndex].intersects(clipRect)) { 673 paintTab(g, tabPlacement, rects, selectedIndex, iconRect, textRect); 674 } 675 } 676 677 681 protected void layoutLabel( 682 int tabPlacement, 683 FontMetrics metrics, 684 int tabIndex, 685 String title, 686 Icon icon, 687 Rectangle tabRect, 688 Rectangle iconRect, 689 Rectangle textRect, 690 boolean isSelected) { 691 textRect.x = textRect.y = iconRect.x = iconRect.y = 0; 692 View v = getTextViewForTab(tabIndex); 694 if (v != null) { 695 tabPane.putClientProperty("html", v); 696 } 697 698 Rectangle calcRectangle = new Rectangle (tabRect); 699 if (isSelected) { 700 Insets calcInsets = getSelectedTabPadInsets(tabPlacement); 701 calcRectangle.x += calcInsets.left; 702 calcRectangle.y += calcInsets.top; 703 calcRectangle.width -= calcInsets.left + calcInsets.right ; 704 calcRectangle.height -= calcInsets.bottom + calcInsets.top; 705 } 706 int xNudge = getTabLabelShiftX(tabPlacement, tabIndex, isSelected); 707 int yNudge = getTabLabelShiftY(tabPlacement, tabIndex, isSelected); 708 if ((tabPlacement == RIGHT || tabPlacement == LEFT) && icon != null && title != null && !title.equals("")) { 709 SwingUtilities.layoutCompoundLabel( 710 tabPane, 711 metrics, 712 title, 713 icon, 714 SwingConstants.CENTER, 715 SwingConstants.LEFT, 716 SwingConstants.CENTER, 717 SwingConstants.TRAILING, 718 calcRectangle, 719 iconRect, 720 textRect, 721 textIconGap); 722 xNudge += 4; 723 } else { 724 SwingUtilities.layoutCompoundLabel( 725 tabPane, 726 metrics, 727 title, 728 icon, 729 SwingConstants.CENTER, 730 SwingConstants.CENTER, 731 SwingConstants.CENTER, 732 SwingConstants.TRAILING, 733 calcRectangle, 734 iconRect, 735 textRect, 736 textIconGap); 737 iconRect.y += calcRectangle.height %2; 738 } 739 740 tabPane.putClientProperty("html", null); 742 743 iconRect.x += xNudge; 744 iconRect.y += yNudge; 745 textRect.x += xNudge; 746 textRect.y += yNudge; 747 } 748 749 754 protected Icon getIconForTab(int tabIndex) { 755 String title = tabPane.getTitleAt(tabIndex); 756 boolean hasTitle = (title != null) && (title.length() > 0); 757 return !isTabIconsEnabled && hasTitle 758 ? null 759 : super.getIconForTab(tabIndex); 760 } 761 762 765 protected LayoutManager createLayoutManager() { 766 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) { 767 return new TabbedPaneScrollLayout(); 768 } 769 770 return new TabbedPaneLayout(); 771 } 772 773 778 private boolean scrollableTabLayoutEnabled() { 779 return tabPane.getLayout() instanceof TabbedPaneScrollLayout; 780 } 781 782 protected boolean isTabInFirstRun(int tabIndex) { 783 return getRunForTab(tabPane.getTabCount(), tabIndex) == 0; 784 } 785 786 protected void paintContentBorder(Graphics g, int tabPlacement, int selectedIndex) { 787 int width = tabPane.getWidth(); 788 int height = tabPane.getHeight(); 789 Insets insets = tabPane.getInsets(); 790 791 int x = insets.left; 792 int y = insets.top; 793 int w = width - insets.right - insets.left; 794 int h = height - insets.top - insets.bottom; 795 796 switch (tabPlacement) { 797 case LEFT : 798 x += calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth); 799 w -= (x - insets.left); 800 break; 801 case RIGHT : 802 w -= calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth); 803 break; 804 case BOTTOM : 805 h -= calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight); 806 break; 807 case TOP : 808 &n
|