1 /* 2 * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.apple.laf; 27 28 import java.awt.*; 29 import java.awt.event.*; 30 import java.awt.geom.AffineTransform; 31 import java.beans.*; 32 33 import javax.swing.*; 34 import javax.swing.event.*; 35 import javax.swing.plaf.*; 36 import javax.swing.text.View; 37 38 import sun.swing.SwingUtilities2; 39 import apple.laf.*; 40 import apple.laf.JRSUIConstants.*; 41 42 public class AquaTabbedPaneUI extends AquaTabbedPaneCopyFromBasicUI { 43 private static final int kSmallTabHeight = 20; // height of a small tab 44 private static final int kLargeTabHeight = 23; // height of a large tab 45 private static final int kMaxIconSize = kLargeTabHeight - 7; 46 47 private static final double kNinetyDegrees = (Math.PI / 2.0); // used for rotation 48 49 protected final Insets currentContentDrawingInsets = new Insets(0, 0, 0, 0); 50 protected final Insets currentContentBorderInsets = new Insets(0, 0, 0, 0); 51 protected final Insets contentDrawingInsets = new Insets(0, 0, 0, 0); 52 53 protected int pressedTab = -3; // -2 is right scroller, -1 is left scroller 54 protected boolean popupSelectionChanged; 55 56 protected Boolean isDefaultFocusReceiver = null; 57 protected boolean hasAvoidedFirstFocus = false; 58 59 // Create PLAF 60 public static ComponentUI createUI(final JComponent c) { 61 return new AquaTabbedPaneUI(); 62 } 63 64 protected final AquaTabbedPaneTabState visibleTabState = new AquaTabbedPaneTabState(this); 65 protected final AquaPainter<JRSUIState> painter = AquaPainter.create(JRSUIStateFactory.getTab()); 66 67 public AquaTabbedPaneUI() { } 68 69 protected void installListeners() { 70 super.installListeners(); 71 72 // We're not just a mouseListener, we're a mouseMotionListener 73 if (mouseListener != null) { 74 tabPane.addMouseMotionListener((MouseMotionListener)mouseListener); 75 } 76 } 77 78 protected void installDefaults() { 79 super.installDefaults(); 80 81 if (tabPane.getFont() instanceof UIResource) { 82 final Boolean b = (Boolean)UIManager.get("TabbedPane.useSmallLayout"); 83 if (b != null && b == Boolean.TRUE) { 84 tabPane.setFont(UIManager.getFont("TabbedPane.smallFont")); 85 painter.state.set(Size.SMALL); 86 } 87 } 88 89 contentDrawingInsets.set(0, 11, 13, 10); 90 tabPane.setOpaque(false); 91 } 92 93 protected void assureRectsCreated(final int tabCount) { 94 visibleTabState.init(tabCount); 95 super.assureRectsCreated(tabCount); 96 } 97 98 @Override 99 protected void uninstallListeners() { 100 // We're not just a mouseListener, we're a mouseMotionListener 101 if (mouseListener instanceof MouseHandler) { 102 final MouseHandler mh = (MouseHandler) mouseListener; 103 mh.dispose(); 104 tabPane.removeMouseMotionListener(mh); 105 } 106 super.uninstallListeners(); 107 } 108 109 protected void uninstallDefaults() { 110 contentDrawingInsets.set(0, 0, 0, 0); 111 } 112 113 protected MouseListener createMouseListener() { 114 return new MouseHandler(); 115 } 116 117 protected FocusListener createFocusListener() { 118 return new FocusHandler(); 119 } 120 121 protected PropertyChangeListener createPropertyChangeListener() { 122 return new TabbedPanePropertyChangeHandler(); 123 } 124 125 protected LayoutManager createLayoutManager() { 126 return new AquaTruncatingTabbedPaneLayout(); 127 } 128 129 protected boolean shouldRepaintSelectedTabOnMouseDown() { 130 return false; 131 } 132 133 // Paint Methods 134 // Cache for performance 135 final Rectangle fContentRect = new Rectangle(); 136 final Rectangle fIconRect = new Rectangle(); 137 final Rectangle fTextRect = new Rectangle(); 138 139 // UI Rendering 140 public void paint(final Graphics g, final JComponent c) { 141 painter.state.set(getDirection()); 142 143 final int tabPlacement = tabPane.getTabPlacement(); 144 final int selectedIndex = tabPane.getSelectedIndex(); 145 paintContentBorder(g, tabPlacement, selectedIndex); 146 147 // we want to call ensureCurrentLayout, but it's private 148 ensureCurrentLayout(); 149 final Rectangle clipRect = g.getClipBounds(); 150 151 final boolean active = tabPane.isEnabled(); 152 final boolean frameActive = AquaFocusHandler.isActive(tabPane); 153 final boolean isLeftToRight = tabPane.getComponentOrientation().isLeftToRight() || tabPlacement == LEFT || tabPlacement == RIGHT; 154 155 // Paint tabRuns of tabs from back to front 156 if (visibleTabState.needsScrollTabs()) { 157 paintScrollingTabs(g, clipRect, tabPlacement, selectedIndex, active, frameActive, isLeftToRight); 158 return; 159 } 160 161 // old way 162 paintAllTabs(g, clipRect, tabPlacement, selectedIndex, active, frameActive, isLeftToRight); 163 } 164 165 protected void paintAllTabs(final Graphics g, final Rectangle clipRect, final int tabPlacement, final int selectedIndex, final boolean active, final boolean frameActive, final boolean isLeftToRight) { 166 boolean drawSelectedLast = false; 167 for (int i = 0; i < rects.length; i++) { 168 if (i == selectedIndex) { 169 drawSelectedLast = true; 170 } else { 171 if (rects[i].intersects(clipRect)) { 172 paintTabNormal(g, tabPlacement, i, active, frameActive, isLeftToRight); 173 } 174 } 175 } 176 177 // paint the selected tab last. 178 if (drawSelectedLast && rects[selectedIndex].intersects(clipRect)) { 179 paintTabNormal(g, tabPlacement, selectedIndex, active, frameActive, isLeftToRight); 180 } 181 } 182 183 protected void paintScrollingTabs(final Graphics g, final Rectangle clipRect, final int tabPlacement, final int selectedIndex, final boolean active, final boolean frameActive, final boolean isLeftToRight) { 184 // final Graphics g2 = g.create(); 185 // g2.setColor(Color.cyan); 186 // Rectangle r = new Rectangle(); 187 // for (int i = 0; i < visibleTabState.getTotal(); i++) { 188 // r.add(rects[visibleTabState.getIndex(i)]); 189 // } 190 // g2.fillRect(r.x, r.y, r.width, r.height); 191 // g2.dispose(); 192 // System.out.println(r); 193 194 // for each visible tab, except the selected one 195 for (int i = 0; i < visibleTabState.getTotal(); i++) { 196 final int realIndex = visibleTabState.getIndex(i); 197 if (realIndex != selectedIndex) { 198 if (rects[realIndex].intersects(clipRect)) { 199 paintTabNormal(g, tabPlacement, realIndex, active, frameActive, isLeftToRight); 200 } 201 } 202 } 203 204 final Rectangle leftScrollTabRect = visibleTabState.getLeftScrollTabRect(); 205 if (visibleTabState.needsLeftScrollTab() && leftScrollTabRect.intersects(clipRect)) { 206 paintTabNormalFromRect(g, tabPlacement, leftScrollTabRect, -2, fIconRect, fTextRect, visibleTabState.needsLeftScrollTab(), frameActive, isLeftToRight); 207 } 208 209 final Rectangle rightScrollTabRect = visibleTabState.getRightScrollTabRect(); 210 if (visibleTabState.needsRightScrollTab() && rightScrollTabRect.intersects(clipRect)) { 211 paintTabNormalFromRect(g, tabPlacement, rightScrollTabRect, -1, fIconRect, fTextRect, visibleTabState.needsRightScrollTab(), frameActive, isLeftToRight); 212 } 213 214 if (selectedIndex >= 0) { // && rects[selectedIndex].intersects(clipRect)) { 215 paintTabNormal(g, tabPlacement, selectedIndex, active, frameActive, isLeftToRight); 216 } 217 } 218 219 private static boolean isScrollTabIndex(final int index) { 220 return index == -1 || index == -2; 221 } 222 223 protected static void transposeRect(final Rectangle r) { 224 int temp = r.y; 225 r.y = r.x; 226 r.x = temp; 227 temp = r.width; 228 r.width = r.height; 229 r.height = temp; 230 } 231 232 protected int getTabLabelShiftX(final int tabPlacement, final int tabIndex, final boolean isSelected) { 233 final Rectangle tabRect = (tabIndex >= 0 ? rects[tabIndex] : visibleTabState.getRightScrollTabRect()); 234 int nudge = 0; 235 switch (tabPlacement) { 236 case LEFT: 237 case RIGHT: 238 nudge = tabRect.height % 2; 239 break; 240 case BOTTOM: 241 case TOP: 242 default: 243 nudge = tabRect.width % 2; 244 } 245 return nudge; 246 } 247 248 protected int getTabLabelShiftY(final int tabPlacement, final int tabIndex, final boolean isSelected) { 249 switch (tabPlacement) { 250 case RIGHT: 251 case LEFT: 252 case BOTTOM: 253 return -1; 254 case TOP: 255 default: 256 return 0; 257 } 258 } 259 260 protected Icon getIconForScrollTab(final int tabPlacement, final int tabIndex, final boolean enabled) { 261 boolean shouldFlip = !AquaUtils.isLeftToRight(tabPane); 262 if (tabPlacement == RIGHT) shouldFlip = false; 263 if (tabPlacement == LEFT) shouldFlip = true; 264 265 int direction = tabIndex == -1 ? EAST : WEST; 266 if (shouldFlip) { 267 if (direction == EAST) { 268 direction = WEST; 269 } else if (direction == WEST) { 270 direction = EAST; 271 } 272 } 273 274 if (enabled) return AquaImageFactory.getArrowIconForDirection(direction); 275 276 final Image icon = AquaImageFactory.getArrowImageForDirection(direction); 277 return new ImageIcon(AquaUtils.generateDisabledImage(icon)); 278 } 279 280 protected void paintContents(final Graphics g, final int tabPlacement, final int tabIndex, final Rectangle tabRect, final Rectangle iconRect, final Rectangle textRect, final boolean isSelected) { 281 final Shape temp = g.getClip(); 282 g.clipRect(fContentRect.x, fContentRect.y, fContentRect.width, fContentRect.height); 283 284 final Component component; 285 final String title; 286 final Icon icon; 287 if (isScrollTabIndex(tabIndex)) { 288 component = null; 289 title = null; 290 icon = getIconForScrollTab(tabPlacement, tabIndex, true); 291 } else { 292 component = getTabComponentAt(tabIndex); 293 if (component == null) { 294 title = tabPane.getTitleAt(tabIndex); 295 icon = getIconForTab(tabIndex); 296 } else { 297 title = null; 298 icon = null; 299 } 300 } 301 302 final boolean isVertical = tabPlacement == RIGHT || tabPlacement == LEFT; 303 if (isVertical) { 304 transposeRect(fContentRect); 305 } 306 307 final Font font = tabPane.getFont(); 308 final FontMetrics metrics = g.getFontMetrics(font); 309 310 // our scrolling tabs 311 layoutLabel(tabPlacement, metrics, tabIndex < 0 ? 0 : tabIndex, title, icon, fContentRect, iconRect, textRect, false); // Never give it "isSelected" - ApprMgr handles this 312 if (isVertical) { 313 transposeRect(fContentRect); 314 transposeRect(iconRect); 315 transposeRect(textRect); 316 } 317 318 // from super.paintText - its normal text painting is totally wrong for the Mac 319 if (!(g instanceof Graphics2D)) { 320 g.setClip(temp); 321 return; 322 } 323 final Graphics2D g2d = (Graphics2D) g; 324 325 AffineTransform savedAT = null; 326 if (isVertical) { 327 savedAT = g2d.getTransform(); 328 rotateGraphics(g2d, tabRect, textRect, iconRect, tabPlacement); 329 } 330 331 // not for the scrolling tabs 332 if (component == null && tabIndex >= 0) { 333 paintTitle(g2d, font, metrics, textRect, tabIndex, title); 334 } 335 336 if (icon != null) { 337 paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected); 338 } 339 340 if (savedAT != null) { 341 g2d.setTransform(savedAT); 342 } 343 344 g.setClip(temp); 345 } 346 347 protected void paintTitle(final Graphics2D g2d, final Font font, final FontMetrics metrics, final Rectangle textRect, final int tabIndex, final String title) { 348 final View v = getTextViewForTab(tabIndex); 349 if (v != null) { 350 v.paint(g2d, textRect); 351 return; 352 } 353 354 if (title == null) return; 355 356 final Color color = tabPane.getForegroundAt(tabIndex); 357 if (color instanceof UIResource) { 358 // sja fix getTheme().setThemeTextColor(g, isSelected, isPressed && tracking, tabPane.isEnabledAt(tabIndex)); 359 if (tabPane.isEnabledAt(tabIndex)) { 360 g2d.setColor(Color.black); 361 } else { 362 g2d.setColor(Color.gray); 363 } 364 } else { 365 g2d.setColor(color); 366 } 367 368 g2d.setFont(font); 369 SwingUtilities2.drawString(tabPane, g2d, title, textRect.x, textRect.y + metrics.getAscent()); 370 } 371 372 protected void rotateGraphics(final Graphics2D g2d, final Rectangle tabRect, final Rectangle textRect, final Rectangle iconRect, final int tabPlacement) { 373 int yDiff = 0; // textRect.y - tabRect.y; 374 int xDiff = 0; // (tabRect.x+tabRect.width) - (textRect.x+textRect.width); 375 int yIconDiff = 0; // iconRect.y - tabRect.y; 376 int xIconDiff = 0; // (tabRect.x+tabRect.width) - (iconRect.x + iconRect.width); 377 378 final double rotateAmount = (tabPlacement == LEFT ? -kNinetyDegrees : kNinetyDegrees); 379 g2d.transform(AffineTransform.getRotateInstance(rotateAmount, tabRect.x, tabRect.y)); 380 381 // x and y diffs are named weirdly. 382 // I will rename them, but what they mean now is 383 // original x offset which will be used to adjust the y coordinate for the 384 // rotated context 385 if (tabPlacement == LEFT) { 386 g2d.translate(-tabRect.height - 1, 1); 387 xDiff = textRect.x - tabRect.x; 388 yDiff = tabRect.height + tabRect.y - (textRect.y + textRect.height); 389 xIconDiff = iconRect.x - tabRect.x; 390 yIconDiff = tabRect.height + tabRect.y - (iconRect.y + iconRect.height); 391 } else { 392 g2d.translate(0, -tabRect.width - 1); 393 yDiff = textRect.y - tabRect.y; 394 xDiff = (tabRect.x + tabRect.width) - (textRect.x + textRect.width); 395 yIconDiff = iconRect.y - tabRect.y; 396 xIconDiff = (tabRect.x + tabRect.width) - (iconRect.x + iconRect.width); 397 } 398 399 // rotation changes needed for the rendering 400 // we are rotating so we can't just use the rects wholesale. 401 textRect.x = tabRect.x + yDiff; 402 textRect.y = tabRect.y + xDiff; 403 404 int tempVal = textRect.height; 405 textRect.height = textRect.width; 406 textRect.width = tempVal; 407 // g.setColor(Color.red); 408 // g.drawLine(textRect.x, textRect.y, textRect.x+textRect.height, textRect.y+textRect.width); 409 // g.drawLine(textRect.x+textRect.height, textRect.y, textRect.x, textRect.y+textRect.width); 410 411 iconRect.x = tabRect.x + yIconDiff; 412 iconRect.y = tabRect.y + xIconDiff; 413 414 tempVal = iconRect.height; 415 iconRect.height = iconRect.width; 416 iconRect.width = tempVal; 417 } 418 419 protected void paintTabNormal(final Graphics g, final int tabPlacement, final int tabIndex, final boolean active, final boolean frameActive, final boolean isLeftToRight) { 420 paintTabNormalFromRect(g, tabPlacement, rects[tabIndex], tabIndex, fIconRect, fTextRect, active, frameActive, isLeftToRight); 421 } 422 423 protected void paintTabNormalFromRect(final Graphics g, 424 final int tabPlacement, 425 final Rectangle tabRect, 426 final int nonRectIndex, 427 final Rectangle iconRect, 428 final Rectangle textRect, 429 final boolean active, 430 final boolean frameActive, 431 final boolean isLeftToRight) { 432 final int selectedIndex = tabPane.getSelectedIndex(); 433 final boolean isSelected = selectedIndex == nonRectIndex; 434 435 paintCUITab(g, tabPlacement, tabRect, isSelected, frameActive, isLeftToRight, nonRectIndex); 436 437 textRect.setBounds(tabRect); 438 fContentRect.setBounds(tabRect); 439 paintContents(g, tabPlacement, nonRectIndex, tabRect, iconRect, textRect, isSelected); 440 } 441 442 protected void paintCUITab(final Graphics g, final int tabPlacement, 443 final Rectangle tabRect, 444 final boolean isSelected, 445 final boolean frameActive, 446 final boolean isLeftToRight, 447 final int nonRectIndex) { 448 final int tabCount = tabPane.getTabCount(); 449 450 final boolean needsLeftScrollTab = visibleTabState.needsLeftScrollTab(); 451 final boolean needsRightScrollTab = visibleTabState.needsRightScrollTab(); 452 453 // first or last 454 boolean first = nonRectIndex == 0; 455 boolean last = nonRectIndex == tabCount - 1; 456 if (needsLeftScrollTab || needsRightScrollTab) { 457 if (nonRectIndex == -1) { 458 first = false; 459 last = true; 460 } else if (nonRectIndex == -2) { 461 first = true; 462 last = false; 463 } else { 464 if (needsLeftScrollTab) first = false; 465 if (needsRightScrollTab) last = false; 466 } 467 } 468 469 if (tabPlacement == LEFT || tabPlacement == RIGHT) { 470 final boolean tempSwap = last; 471 last = first; 472 first = tempSwap; 473 } 474 475 final State state = getState(nonRectIndex, frameActive, isSelected); 476 painter.state.set(state); 477 painter.state.set(isSelected || (state == State.INACTIVE && frameActive) ? BooleanValue.YES : BooleanValue.NO); 478 painter.state.set(getSegmentPosition(first, last, isLeftToRight)); 479 final int selectedIndex = tabPane.getSelectedIndex(); 480 painter.state.set(getSegmentTrailingSeparator(nonRectIndex, selectedIndex, isLeftToRight)); 481 painter.state.set(getSegmentLeadingSeparator(nonRectIndex, selectedIndex, isLeftToRight)); 482 painter.state.set(tabPane.hasFocus() && isSelected ? Focused.YES : Focused.NO); 483 painter.paint(g, tabPane, tabRect.x, tabRect.y, tabRect.width, tabRect.height); 484 485 if (isScrollTabIndex(nonRectIndex)) return; 486 487 final Color color = tabPane.getBackgroundAt(nonRectIndex); 488 if (color == null || (color instanceof UIResource)) return; 489 490 if (!isLeftToRight && (tabPlacement == TOP || tabPlacement == BOTTOM)) { 491 final boolean tempSwap = last; 492 last = first; 493 first = tempSwap; 494 } 495 496 fillTabWithBackground(g, tabRect, tabPlacement, first, last, color); 497 } 498 499 protected Direction getDirection() { 500 switch (tabPane.getTabPlacement()) { 501 case SwingConstants.BOTTOM: return Direction.SOUTH; 502 case SwingConstants.LEFT: return Direction.WEST; 503 case SwingConstants.RIGHT: return Direction.EAST; 504 } 505 return Direction.NORTH; 506 } 507 508 protected static SegmentPosition getSegmentPosition(final boolean first, final boolean last, final boolean isLeftToRight) { 509 if (first && last) return SegmentPosition.ONLY; 510 if (first) return isLeftToRight ? SegmentPosition.FIRST : SegmentPosition.LAST; 511 if (last) return isLeftToRight ? SegmentPosition.LAST : SegmentPosition.FIRST; 512 return SegmentPosition.MIDDLE; 513 } 514 515 protected SegmentTrailingSeparator getSegmentTrailingSeparator(final int index, final int selectedIndex, final boolean isLeftToRight) { 516 return SegmentTrailingSeparator.YES; 517 } 518 519 protected SegmentLeadingSeparator getSegmentLeadingSeparator(final int index, final int selectedIndex, final boolean isLeftToRight) { 520 return SegmentLeadingSeparator.NO; 521 } 522 523 protected boolean isTabBeforeSelectedTab(final int index, final int selectedIndex, final boolean isLeftToRight) { 524 if (index == -2 && visibleTabState.getIndex(0) == selectedIndex) return true; 525 int indexBeforeSelectedIndex = isLeftToRight ? selectedIndex - 1 : selectedIndex + 1; 526 return index == indexBeforeSelectedIndex ? true : false; 527 } 528 529 protected State getState(final int index, final boolean frameActive, final boolean isSelected) { 530 if (!frameActive) return State.INACTIVE; 531 if (!tabPane.isEnabled()) return State.DISABLED; 532 if (JRSUIUtils.TabbedPane.useLegacyTabs()) { 533 if (isSelected) return State.PRESSED; 534 if (pressedTab == index) return State.INACTIVE; 535 } else { 536 if (isSelected) return State.ACTIVE; 537 if (pressedTab == index) return State.PRESSED; 538 } 539 return State.ACTIVE; 540 } 541 542 /** 543 * This routine adjusts the background fill rect so it just fits inside a tab, allowing for 544 * whether we're talking about a first tab or last tab. NOTE that this code is very much 545 * Aqua 2 dependent! 546 */ 547 static class AlterRects { 548 Rectangle standard, first, last; 549 AlterRects(final int x, final int y, final int w, final int h) { standard = new Rectangle(x, y, w, h); } 550 AlterRects start(final int x, final int y, final int w, final int h) { first = new Rectangle(x, y, w, h); return this; } 551 AlterRects end(final int x, final int y, final int w, final int h) { last = new Rectangle(x, y, w, h); return this; } 552 553 static Rectangle alter(final Rectangle r, final Rectangle o) { 554 // r = new Rectangle(r); 555 r.x += o.x; 556 r.y += o.y; 557 r.width += o.width; 558 r.height += o.height; 559 return r; 560 } 561 } 562 563 static AlterRects[] alterRects = new AlterRects[5]; 564 565 protected static AlterRects getAlterationFor(final int tabPlacement) { 566 if (alterRects[tabPlacement] != null) return alterRects[tabPlacement]; 567 568 switch (tabPlacement) { 569 case LEFT: return alterRects[LEFT] = new AlterRects(2, 0, -4, 1).start(0, 0, 0, -4).end(0, 3, 0, -3); 570 case RIGHT: return alterRects[RIGHT] = new AlterRects(1, 0, -4, 1).start(0, 0, 0, -4).end(0, 3, 0, -3); 571 case BOTTOM: return alterRects[BOTTOM] = new AlterRects(0, 1, 0, -4).start(3, 0, -3, 0).end(0, 0, -3, 0); 572 case TOP: 573 default: return alterRects[TOP] = new AlterRects(0, 2, 0, -4).start(3, 0, -3, 0).end(0, 0, -3, 0); 574 } 575 } 576 577 protected void fillTabWithBackground(final Graphics g, final Rectangle rect, final int tabPlacement, final boolean first, final boolean last, final Color color) { 578 final Rectangle fillRect = new Rectangle(rect); 579 580 final AlterRects alteration = getAlterationFor(tabPlacement); 581 AlterRects.alter(fillRect, alteration.standard); 582 if (first) AlterRects.alter(fillRect, alteration.first); 583 if (last) AlterRects.alter(fillRect, alteration.last); 584 585 g.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), (int)(color.getAlpha() * 0.25))); 586 g.fillRoundRect(fillRect.x, fillRect.y, fillRect.width, fillRect.height, 3, 1); 587 } 588 589 protected Insets getContentBorderInsets(final int tabPlacement) { 590 final Insets draw = getContentDrawingInsets(tabPlacement); // will be rotated 591 592 rotateInsets(contentBorderInsets, currentContentBorderInsets, tabPlacement); 593 594 currentContentBorderInsets.left += draw.left; 595 currentContentBorderInsets.right += draw.right; 596 currentContentBorderInsets.top += draw.top; 597 currentContentBorderInsets.bottom += draw.bottom; 598 599 return currentContentBorderInsets; 600 } 601 602 protected static void rotateInsets(final Insets topInsets, final Insets targetInsets, final int targetPlacement) { 603 switch (targetPlacement) { 604 case LEFT: 605 targetInsets.top = topInsets.left; 606 targetInsets.left = topInsets.top; 607 targetInsets.bottom = topInsets.right; 608 targetInsets.right = topInsets.bottom; 609 break; 610 case BOTTOM: 611 targetInsets.top = topInsets.bottom; 612 targetInsets.left = topInsets.left; 613 targetInsets.bottom = topInsets.top; 614 targetInsets.right = topInsets.right; 615 break; 616 case RIGHT: 617 targetInsets.top = topInsets.right; 618 targetInsets.left = topInsets.bottom; 619 targetInsets.bottom = topInsets.left; 620 targetInsets.right = topInsets.top; 621 break; 622 case TOP: 623 default: 624 targetInsets.top = topInsets.top; 625 targetInsets.left = topInsets.left; 626 targetInsets.bottom = topInsets.bottom; 627 targetInsets.right = topInsets.right; 628 } 629 } 630 631 protected Insets getContentDrawingInsets(final int tabPlacement) { 632 rotateInsets(contentDrawingInsets, currentContentDrawingInsets, tabPlacement); 633 return currentContentDrawingInsets; 634 } 635 636 protected Icon getIconForTab(final int tabIndex) { 637 final Icon mainIcon = super.getIconForTab(tabIndex); 638 if (mainIcon == null) return null; 639 640 final int iconHeight = mainIcon.getIconHeight(); 641 if (iconHeight <= kMaxIconSize) return mainIcon; 642 final float ratio = (float)kMaxIconSize / (float)iconHeight; 643 644 final int iconWidth = mainIcon.getIconWidth(); 645 return new AquaIcon.CachingScalingIcon((int)(iconWidth * ratio), kMaxIconSize) { 646 Image createImage() { 647 return AquaIcon.getImageForIcon(mainIcon); 648 } 649 }; 650 } 651 652 private static final int TAB_BORDER_INSET = 9; 653 protected void paintContentBorder(final Graphics g, final int tabPlacement, final int selectedIndex) { 654 final int width = tabPane.getWidth(); 655 final int height = tabPane.getHeight(); 656 final Insets insets = tabPane.getInsets(); 657 658 int x = insets.left; 659 int y = insets.top; 660 int w = width - insets.right - insets.left; 661 int h = height - insets.top - insets.bottom; 662 663 switch (tabPlacement) { 664 case TOP: 665 y += TAB_BORDER_INSET; 666 h -= TAB_BORDER_INSET; 667 break; 668 case BOTTOM: 669 h -= TAB_BORDER_INSET;// - 2; 670 break; 671 case LEFT: 672 x += TAB_BORDER_INSET;// - 5; 673 w -= TAB_BORDER_INSET;// + 1; 674 break; 675 case RIGHT: 676 w -= TAB_BORDER_INSET;// + 1; 677 break; 678 } 679 680 if (tabPane.isOpaque()) { 681 g.setColor(tabPane.getBackground()); 682 g.fillRect(0, 0, width, height); 683 } 684 685 AquaGroupBorder.getTabbedPaneGroupBorder().paintBorder(tabPane, g, x, y, w, h); 686 } 687 688 // see paintContentBorder 689 protected void repaintContentBorderEdge() { 690 final int width = tabPane.getWidth(); 691 final int height = tabPane.getHeight(); 692 final Insets insets = tabPane.getInsets(); 693 final int tabPlacement = tabPane.getTabPlacement(); 694 final Insets localContentBorderInsets = getContentBorderInsets(tabPlacement); 695 696 int x = insets.left; 697 int y = insets.top; 698 int w = width - insets.right - insets.left; 699 int h = height - insets.top - insets.bottom; 700 701 switch (tabPlacement) { 702 case LEFT: 703 x += calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth); 704 w = localContentBorderInsets.left; 705 break; 706 case RIGHT: 707 w = localContentBorderInsets.right; 708 break; 709 case BOTTOM: 710 h = localContentBorderInsets.bottom; 711 break; 712 case TOP: 713 default: 714 y += calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight); 715 h = localContentBorderInsets.top; 716 } 717 tabPane.repaint(x, y, w, h); 718 } 719 720 public boolean isTabVisible(final int index) { 721 if (index == -1 || index == -2) return true; 722 for (int i = 0; i < visibleTabState.getTotal(); i++) { 723 if (visibleTabState.getIndex(i) == index) return true; 724 } 725 return false; 726 } 727 728 /** 729 * Returns the bounds of the specified tab index. The bounds are 730 * with respect to the JTabbedPane's coordinate space. If the tab at this 731 * index is not currently visible in the UI, then returns null. 732 */ 733 @Override 734 public Rectangle getTabBounds(final JTabbedPane pane, final int i) { 735 if (visibleTabState.needsScrollTabs() 736 && (visibleTabState.isBefore(i) || visibleTabState.isAfter(i))) { 737 return null; 738 } 739 return super.getTabBounds(pane, i); 740 } 741 742 /** 743 * Returns the tab index which intersects the specified point 744 * in the JTabbedPane's coordinate space. 745 */ 746 public int tabForCoordinate(final JTabbedPane pane, final int x, final int y) { 747 ensureCurrentLayout(); 748 final Point p = new Point(x, y); 749 if (visibleTabState.needsScrollTabs()) { 750 for (int i = 0; i < visibleTabState.getTotal(); i++) { 751 final int realOffset = visibleTabState.getIndex(i); 752 if (rects[realOffset].contains(p.x, p.y)) return realOffset; 753 } 754 if (visibleTabState.getRightScrollTabRect().contains(p.x, p.y)) return -1; //tabPane.getTabCount(); 755 } else { 756 //old way 757 final int tabCount = tabPane.getTabCount(); 758 for (int i = 0; i < tabCount; i++) { 759 if (rects[i].contains(p.x, p.y)) return i; 760 } 761 } 762 return -1; 763 } 764 765 protected Insets getTabInsets(final int tabPlacement, final int tabIndex) { 766 switch (tabPlacement) { 767 case LEFT: return UIManager.getInsets("TabbedPane.leftTabInsets"); 768 case RIGHT: return UIManager.getInsets("TabbedPane.rightTabInsets"); 769 } 770 return tabInsets; 771 } 772 773 // This is the preferred size - the layout manager will ignore if it has to 774 protected int calculateTabHeight(final int tabPlacement, final int tabIndex, final int fontHeight) { 775 // Constrain to what the Mac allows 776 final int result = super.calculateTabHeight(tabPlacement, tabIndex, fontHeight); 777 778 // force tabs to have a max height for aqua 779 if (result <= kSmallTabHeight) return kSmallTabHeight; 780 return kLargeTabHeight; 781 } 782 783 // JBuilder requested this - it's against HI, but then so are multiple rows 784 protected boolean shouldRotateTabRuns(final int tabPlacement) { 785 return false; 786 } 787 788 protected class TabbedPanePropertyChangeHandler extends PropertyChangeHandler { 789 public void propertyChange(final PropertyChangeEvent e) { 790 final String prop = e.getPropertyName(); 791 792 if (!AquaFocusHandler.FRAME_ACTIVE_PROPERTY.equals(prop)) { 793 super.propertyChange(e); 794 return; 795 } 796 797 final JTabbedPane comp = (JTabbedPane)e.getSource(); 798 comp.repaint(); 799 800 // Repaint the "front" tab and the border 801 final int selected = tabPane.getSelectedIndex(); 802 final Rectangle[] theRects = rects; 803 if (selected >= 0 && selected < theRects.length) comp.repaint(theRects[selected]); 804 repaintContentBorderEdge(); 805 } 806 } 807 808 protected ChangeListener createChangeListener() { 809 return new ChangeListener() { 810 public void stateChanged(final ChangeEvent e) { 811 if (!isTabVisible(tabPane.getSelectedIndex())) popupSelectionChanged = true; 812 tabPane.revalidate(); 813 tabPane.repaint(); 814 } 815 }; 816 } 817 818 protected class FocusHandler extends FocusAdapter { 819 Rectangle sWorkingRect = new Rectangle(); 820 821 public void focusGained(final FocusEvent e) { 822 if (isDefaultFocusReceiver(tabPane) && !hasAvoidedFirstFocus) { 823 KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent(); 824 hasAvoidedFirstFocus = true; 825 } 826 adjustPaintingRectForFocusRing(e); 827 } 828 829 public void focusLost(final FocusEvent e) { 830 adjustPaintingRectForFocusRing(e); 831 } 832 833 void adjustPaintingRectForFocusRing(final FocusEvent e) { 834 final JTabbedPane pane = (JTabbedPane)e.getSource(); 835 final int tabCount = pane.getTabCount(); 836 final int selectedIndex = pane.getSelectedIndex(); 837 838 if (selectedIndex != -1 && tabCount > 0 && tabCount == rects.length) { 839 sWorkingRect.setBounds(rects[selectedIndex]); 840 sWorkingRect.grow(4, 4); 841 pane.repaint(sWorkingRect); 842 } 843 } 844 845 boolean isDefaultFocusReceiver(final JComponent component) { 846 if (isDefaultFocusReceiver == null) { 847 Component defaultFocusReceiver = KeyboardFocusManager.getCurrentKeyboardFocusManager().getDefaultFocusTraversalPolicy().getDefaultComponent(getTopLevelFocusCycleRootAncestor(component)); 848 isDefaultFocusReceiver = defaultFocusReceiver != null && defaultFocusReceiver.equals(component); 849 } 850 return isDefaultFocusReceiver.booleanValue(); 851 } 852 853 Container getTopLevelFocusCycleRootAncestor(Container container) { 854 Container ancestor; 855 while ((ancestor = container.getFocusCycleRootAncestor()) != null) { 856 container = ancestor; 857 } 858 return container; 859 } 860 } 861 862 class MouseHandler extends MouseInputAdapter implements ActionListener { 863 864 int trackingTab = -3; 865 private final Timer popupTimer = new Timer(500, this); 866 867 MouseHandler() { 868 popupTimer.setRepeats(false); 869 } 870 871 void dispose (){ 872 popupTimer.removeActionListener(this); 873 popupTimer.stop(); 874 } 875 876 public void mousePressed(final MouseEvent e) { 877 final JTabbedPane pane = (JTabbedPane)e.getSource(); 878 if (!pane.isEnabled()) { 879 trackingTab = -3; 880 return; 881 } 882 883 final Point p = e.getPoint(); 884 trackingTab = getCurrentTab(pane, p); 885 if (trackingTab == -3 || (!shouldRepaintSelectedTabOnMouseDown() && trackingTab == pane.getSelectedIndex())) { 886 trackingTab = -3; 887 return; 888 } 889 890 if (trackingTab < 0 && trackingTab > -3) { 891 popupTimer.start(); 892 } 893 894 pressedTab = trackingTab; 895 repaint(pane, pressedTab); 896 } 897 898 public void mouseDragged(final MouseEvent e) { 899 if (trackingTab < -2) return; 900 901 final JTabbedPane pane = (JTabbedPane)e.getSource(); 902 final int currentTab = getCurrentTab(pane, e.getPoint()); 903 904 if (currentTab != trackingTab) { 905 pressedTab = -3; 906 } else { 907 pressedTab = trackingTab; 908 } 909 910 if (trackingTab < 0 && trackingTab > -3) { 911 popupTimer.start(); 912 } 913 914 repaint(pane, trackingTab); 915 } 916 917 public void mouseReleased(final MouseEvent e) { 918 if (trackingTab < -2) return; 919 920 popupTimer.stop(); 921 922 final JTabbedPane pane = (JTabbedPane)e.getSource(); 923 final Point p = e.getPoint(); 924 final int currentTab = getCurrentTab(pane, p); 925 926 if (trackingTab == -1 && currentTab == -1) { 927 pane.setSelectedIndex(pane.getSelectedIndex() + 1); 928 } 929 930 if (trackingTab == -2 && currentTab == -2) { 931 pane.setSelectedIndex(pane.getSelectedIndex() - 1); 932 } 933 934 if (trackingTab >= 0 && currentTab == trackingTab) { 935 pane.setSelectedIndex(trackingTab); 936 } 937 938 repaint(pane, trackingTab); 939 940 pressedTab = -3; 941 trackingTab = -3; 942 } 943 944 public void actionPerformed(final ActionEvent e) { 945 if (trackingTab != pressedTab) { 946 return; 947 } 948 949 if (trackingTab == -1) { 950 showFullPopup(false); 951 trackingTab = -3; 952 } 953 954 if (trackingTab == -2) { 955 showFullPopup(true); 956 trackingTab = -3; 957 } 958 } 959 960 int getCurrentTab(final JTabbedPane pane, final Point p) { 961 final int tabIndex = tabForCoordinate(pane, p.x, p.y); 962 if (tabIndex >= 0 && pane.isEnabledAt(tabIndex)) return tabIndex; 963 964 if (visibleTabState.needsLeftScrollTab() && visibleTabState.getLeftScrollTabRect().contains(p)) return -2; 965 if (visibleTabState.needsRightScrollTab() && visibleTabState.getRightScrollTabRect().contains(p)) return -1; 966 967 return -3; 968 } 969 970 void repaint(final JTabbedPane pane, final int tab) { 971 switch (tab) { 972 case -1: 973 pane.repaint(visibleTabState.getRightScrollTabRect()); 974 return; 975 case -2: 976 pane.repaint(visibleTabState.getLeftScrollTabRect()); 977 return; 978 default: 979 if (trackingTab >= 0) pane.repaint(rects[trackingTab]); 980 return; 981 } 982 } 983 984 void showFullPopup(final boolean firstTab) { 985 final JPopupMenu popup = new JPopupMenu(); 986 987 for (int i = 0; i < tabPane.getTabCount(); i++) { 988 if (firstTab ? visibleTabState.isBefore(i) : visibleTabState.isAfter(i)) { 989 popup.add(createMenuItem(i)); 990 } 991 } 992 993 if (firstTab) { 994 final Rectangle leftScrollTabRect = visibleTabState.getLeftScrollTabRect(); 995 final Dimension popupRect = popup.getPreferredSize(); 996 popup.show(tabPane, leftScrollTabRect.x - popupRect.width, leftScrollTabRect.y + 7); 997 } else { 998 final Rectangle rightScrollTabRect = visibleTabState.getRightScrollTabRect(); 999 popup.show(tabPane, rightScrollTabRect.x + rightScrollTabRect.width, rightScrollTabRect.y + 7); 1000 } 1001 1002 popup.addPopupMenuListener(new PopupMenuListener() { 1003 public void popupMenuCanceled(final PopupMenuEvent e) { } 1004 public void popupMenuWillBecomeVisible(final PopupMenuEvent e) { } 1005 1006 public void popupMenuWillBecomeInvisible(final PopupMenuEvent e) { 1007 pressedTab = -3; 1008 tabPane.repaint(visibleTabState.getLeftScrollTabRect()); 1009 tabPane.repaint(visibleTabState.getRightScrollTabRect()); 1010 } 1011 }); 1012 } 1013 1014 JMenuItem createMenuItem(final int i) { 1015 final Component component = getTabComponentAt(i); 1016 final JMenuItem menuItem; 1017 if (component == null) { 1018 menuItem = new JMenuItem(tabPane.getTitleAt(i), tabPane.getIconAt(i)); 1019 } else { 1020 @SuppressWarnings("serial") // anonymous class 1021 JMenuItem tmp = new JMenuItem() { 1022 public void paintComponent(final Graphics g) { 1023 super.paintComponent(g); 1024 final Dimension size = component.getSize(); 1025 component.setSize(getSize()); 1026 component.validate(); 1027 component.paint(g); 1028 component.setSize(size); 1029 } 1030 1031 public Dimension getPreferredSize() { 1032 return component.getPreferredSize(); 1033 } 1034 }; 1035 menuItem = tmp; 1036 } 1037 1038 final Color background = tabPane.getBackgroundAt(i); 1039 if (!(background instanceof UIResource)) { 1040 menuItem.setBackground(background); 1041 } 1042 1043 menuItem.setForeground(tabPane.getForegroundAt(i)); 1044 // for <rdar://problem/3520267> make sure to disable items that are disabled in the tab. 1045 if (!tabPane.isEnabledAt(i)) menuItem.setEnabled(false); 1046 1047 final int fOffset = i; 1048 menuItem.addActionListener(new ActionListener() { 1049 public void actionPerformed(final ActionEvent ae) { 1050 boolean visible = isTabVisible(fOffset); 1051 tabPane.setSelectedIndex(fOffset); 1052 if (!visible) { 1053 popupSelectionChanged = true; 1054 tabPane.invalidate(); 1055 tabPane.repaint(); 1056 } 1057 } 1058 }); 1059 1060 return menuItem; 1061 } 1062 } 1063 1064 protected class AquaTruncatingTabbedPaneLayout extends AquaTabbedPaneCopyFromBasicUI.TabbedPaneLayout { 1065 // fix for Radar #3346131 1066 protected int preferredTabAreaWidth(final int tabPlacement, final int height) { 1067 // Our superclass wants to stack tabs, but we rotate them, 1068 // so when tabs are on the left or right we know that 1069 // our width is actually the "height" of a tab which is then 1070 // rotated. 1071 if (tabPlacement == SwingConstants.LEFT || tabPlacement == SwingConstants.RIGHT) { 1072 return super.preferredTabAreaHeight(tabPlacement, height); 1073 } 1074 1075 return super.preferredTabAreaWidth(tabPlacement, height); 1076 } 1077 1078 protected int preferredTabAreaHeight(final int tabPlacement, final int width) { 1079 if (tabPlacement == SwingConstants.LEFT || tabPlacement == SwingConstants.RIGHT) { 1080 return super.preferredTabAreaWidth(tabPlacement, width); 1081 } 1082 1083 return super.preferredTabAreaHeight(tabPlacement, width); 1084 } 1085 1086 protected void calculateTabRects(final int tabPlacement, final int tabCount) { 1087 if (tabCount <= 0) return; 1088 1089 superCalculateTabRects(tabPlacement, tabCount); // does most of the hard work 1090 1091 // If they haven't been padded (which they only do when there are multiple rows) we should center them 1092 if (rects.length <= 0) return; 1093 1094 visibleTabState.alignRectsRunFor(rects, tabPane.getSize(), tabPlacement, AquaUtils.isLeftToRight(tabPane)); 1095 } 1096 1097 protected void padTabRun(final int tabPlacement, final int start, final int end, final int max) { 1098 if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { 1099 super.padTabRun(tabPlacement, start, end, max); 1100 return; 1101 } 1102 1103 final Rectangle lastRect = rects[end]; 1104 final int runHeight = (lastRect.y + lastRect.height) - rects[start].y; 1105 final int deltaHeight = max - (lastRect.y + lastRect.height); 1106 final float factor = (float)deltaHeight / (float)runHeight; 1107 for (int i = start; i <= end; i++) { 1108 final Rectangle pastRect = rects[i]; 1109 if (i > start) { 1110 pastRect.y = rects[i - 1].y + rects[i - 1].height; 1111 } 1112 pastRect.height += Math.round(pastRect.height * factor); 1113 } 1114 lastRect.height = max - lastRect.y; 1115 } 1116 1117 /** 1118 * This is a massive routine and I left it like this because the bulk of the code comes 1119 * from the BasicTabbedPaneUI class. Here is what it does: 1120 * 1. Calculate rects for the tabs - we have to play tricks here because our right and left tabs 1121 * should get widths calculated the same way as top and bottom, but they will be rotated so the 1122 * calculated width is stored as the rect height. 1123 * 2. Decide if we can fit all the tabs. 1124 * 3. When we cannot fit all the tabs we create a tab popup, and then layout the new tabs until 1125 * we can't fit them anymore. Laying them out is a matter of adding them into the visible list 1126 * and shifting them horizontally to the correct location. 1127 */ 1128 protected synchronized void superCalculateTabRects(final int tabPlacement, final int tabCount) { 1129 final Dimension size = tabPane.getSize(); 1130 final Insets insets = tabPane.getInsets(); 1131 final Insets localTabAreaInsets = getTabAreaInsets(tabPlacement); 1132 1133 // Calculate bounds within which a tab run must fit 1134 final int returnAt; 1135 final int x, y; 1136 switch (tabPlacement) { 1137 case SwingConstants.LEFT: 1138 maxTabWidth = calculateMaxTabHeight(tabPlacement); 1139 x = insets.left + localTabAreaInsets.left; 1140 y = insets.top + localTabAreaInsets.top; 1141 returnAt = size.height - (insets.bottom + localTabAreaInsets.bottom); 1142 break; 1143 case SwingConstants.RIGHT: 1144 maxTabWidth = calculateMaxTabHeight(tabPlacement); 1145 x = size.width - insets.right - localTabAreaInsets.right - maxTabWidth - 1; 1146 y = insets.top + localTabAreaInsets.top; 1147 returnAt = size.height - (insets.bottom + localTabAreaInsets.bottom); 1148 break; 1149 case SwingConstants.BOTTOM: 1150 maxTabHeight = calculateMaxTabHeight(tabPlacement); 1151 x = insets.left + localTabAreaInsets.left; 1152 y = size.height - insets.bottom - localTabAreaInsets.bottom - maxTabHeight; 1153 returnAt = size.width - (insets.right + localTabAreaInsets.right); 1154 break; 1155 case SwingConstants.TOP: 1156 default: 1157 maxTabHeight = calculateMaxTabHeight(tabPlacement); 1158 x = insets.left + localTabAreaInsets.left; 1159 y = insets.top + localTabAreaInsets.top; 1160 returnAt = size.width - (insets.right + localTabAreaInsets.right); 1161 break; 1162 } 1163 1164 tabRunOverlay = getTabRunOverlay(tabPlacement); 1165 1166 runCount = 0; 1167 selectedRun = 0; 1168 1169 if (tabCount == 0) return; 1170 1171 final FontMetrics metrics = getFontMetrics(); 1172 final boolean verticalTabRuns = (tabPlacement == SwingConstants.LEFT || tabPlacement == SwingConstants.RIGHT); 1173 final int selectedIndex = tabPane.getSelectedIndex(); 1174 1175 // calculate all the widths 1176 // if they all fit we are done, if not 1177 // we have to do the dance of figuring out which ones to show. 1178 visibleTabState.setNeedsScrollers(false); 1179 for (int i = 0; i < tabCount; i++) { 1180 final Rectangle rect = rects[i]; 1181 1182 if (verticalTabRuns) { 1183 calculateVerticalTabRunRect(rect, metrics, tabPlacement, returnAt, i, x, y); 1184 1185 // test if we need to scroll! 1186 if (rect.y + rect.height > returnAt) { 1187 visibleTabState.setNeedsScrollers(true); 1188 } 1189 } else { 1190 calculateHorizontalTabRunRect(rect, metrics, tabPlacement, returnAt, i, x, y); 1191 1192 // test if we need to scroll! 1193 if (rect.x + rect.width > returnAt) { 1194 visibleTabState.setNeedsScrollers(true); 1195 } 1196 } 1197 } 1198 1199 visibleTabState.relayoutForScrolling(rects, x, y, returnAt, selectedIndex, verticalTabRuns, tabCount, AquaUtils.isLeftToRight(tabPane)); 1200 // Pad the selected tab so that it appears raised in front 1201 1202 // if right to left and tab placement on the top or 1203 // the bottom, flip x positions and adjust by widths 1204 if (!AquaUtils.isLeftToRight(tabPane) && !verticalTabRuns) { 1205 final int rightMargin = size.width - (insets.right + localTabAreaInsets.right); 1206 for (int i = 0; i < tabCount; i++) { 1207 rects[i].x = rightMargin - rects[i].x - rects[i].width; 1208 } 1209 } 1210 } 1211 1212 private void calculateHorizontalTabRunRect(final Rectangle rect, final FontMetrics metrics, final int tabPlacement, final int returnAt, final int i, final int x, final int y) { 1213 // Tabs on TOP or BOTTOM.... 1214 if (i > 0) { 1215 rect.x = rects[i - 1].x + rects[i - 1].width; 1216 } else { 1217 tabRuns[0] = 0; 1218 runCount = 1; 1219 maxTabWidth = 0; 1220 rect.x = x; 1221 } 1222 1223 rect.width = calculateTabWidth(tabPlacement, i, metrics); 1224 maxTabWidth = Math.max(maxTabWidth, rect.width); 1225 1226 rect.y = y; 1227 rect.height = maxTabHeight; 1228 } 1229 1230 private void calculateVerticalTabRunRect(final Rectangle rect, final FontMetrics metrics, final int tabPlacement, final int returnAt, final int i, final int x, final int y) { 1231 // Tabs on LEFT or RIGHT... 1232 if (i > 0) { 1233 rect.y = rects[i - 1].y + rects[i - 1].height; 1234 } else { 1235 tabRuns[0] = 0; 1236 runCount = 1; 1237 maxTabHeight = 0; 1238 rect.y = y; 1239 } 1240 1241 rect.height = calculateTabWidth(tabPlacement, i, metrics); 1242 maxTabHeight = Math.max(maxTabHeight, rect.height); 1243 1244 rect.x = x; 1245 rect.width = maxTabWidth; 1246 } 1247 1248 protected void layoutTabComponents() { 1249 final Container tabContainer = getTabContainer(); 1250 if (tabContainer == null) return; 1251 1252 final int placement = tabPane.getTabPlacement(); 1253 final Rectangle rect = new Rectangle(); 1254 final Point delta = new Point(-tabContainer.getX(), -tabContainer.getY()); 1255 1256 for (int i = 0; i < tabPane.getTabCount(); i++) { 1257 final Component c = getTabComponentAt(i); 1258 if (c == null) continue; 1259 1260 getTabBounds(i, rect); 1261 final Insets insets = getTabInsets(tabPane.getTabPlacement(), i); 1262 final boolean isSeleceted = i == tabPane.getSelectedIndex(); 1263 1264 if (placement == SwingConstants.TOP || placement == SwingConstants.BOTTOM) { 1265 rect.x += insets.left + delta.x + getTabLabelShiftX(placement, i, isSeleceted); 1266 rect.y += insets.top + delta.y + getTabLabelShiftY(placement, i, isSeleceted) + 1; 1267 rect.width -= insets.left + insets.right; 1268 rect.height -= insets.top + insets.bottom - 1; 1269 } else { 1270 rect.x += insets.top + delta.x + getTabLabelShiftY(placement, i, isSeleceted) + (placement == SwingConstants.LEFT ? 2 : 1); 1271 rect.y += insets.left + delta.y + getTabLabelShiftX(placement, i, isSeleceted); 1272 rect.width -= insets.top + insets.bottom - 1; 1273 rect.height -= insets.left + insets.right; 1274 } 1275 1276 c.setBounds(rect); 1277 } 1278 } 1279 } 1280 }