1 /* 2 * Copyright (c) 2011, 2017, 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 String clippedTitle = SwingUtilities2.clipStringIfNecessary(null, metrics, 334 title, textRect.width); 335 paintTitle(g2d, font, metrics, textRect, tabIndex, clippedTitle); 336 } 337 338 if (icon != null) { 339 paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected); 340 } 341 342 if (savedAT != null) { 343 g2d.setTransform(savedAT); 344 } 345 346 g.setClip(temp); 347 } 348 349 protected void paintTitle(final Graphics2D g2d, final Font font, final FontMetrics metrics, final Rectangle textRect, final int tabIndex, final String title) { 350 final View v = getTextViewForTab(tabIndex); 351 if (v != null) { 352 v.paint(g2d, textRect); 353 return; 354 } 355 356 if (title == null) return; 357 358 final Color color = tabPane.getForegroundAt(tabIndex); 359 if (color instanceof UIResource) { 360 // sja fix getTheme().setThemeTextColor(g, isSelected, isPressed && tracking, tabPane.isEnabledAt(tabIndex)); 361 if (tabPane.isEnabledAt(tabIndex)) { 362 g2d.setColor(Color.black); 363 } else { 364 g2d.setColor(Color.gray); 365 } 366 } else { 367 g2d.setColor(color); 368 } 369 370 g2d.setFont(font); 371 SwingUtilities2.drawString(tabPane, g2d, title, textRect.x, textRect.y + metrics.getAscent()); 372 } 373 374 protected void rotateGraphics(final Graphics2D g2d, final Rectangle tabRect, final Rectangle textRect, final Rectangle iconRect, final int tabPlacement) { 375 int yDiff = 0; // textRect.y - tabRect.y; 376 int xDiff = 0; // (tabRect.x+tabRect.width) - (textRect.x+textRect.width); 377 int yIconDiff = 0; // iconRect.y - tabRect.y; 378 int xIconDiff = 0; // (tabRect.x+tabRect.width) - (iconRect.x + iconRect.width); 379 380 final double rotateAmount = (tabPlacement == LEFT ? -kNinetyDegrees : kNinetyDegrees); 381 g2d.transform(AffineTransform.getRotateInstance(rotateAmount, tabRect.x, tabRect.y)); 382 383 // x and y diffs are named weirdly. 384 // I will rename them, but what they mean now is 385 // original x offset which will be used to adjust the y coordinate for the 386 // rotated context 387 if (tabPlacement == LEFT) { 388 g2d.translate(-tabRect.height - 1, 1); 389 xDiff = textRect.x - tabRect.x; 390 yDiff = tabRect.height + tabRect.y - (textRect.y + textRect.height); 391 xIconDiff = iconRect.x - tabRect.x; 392 yIconDiff = tabRect.height + tabRect.y - (iconRect.y + iconRect.height); 393 } else { 394 g2d.translate(0, -tabRect.width - 1); 395 yDiff = textRect.y - tabRect.y; 396 xDiff = (tabRect.x + tabRect.width) - (textRect.x + textRect.width); 397 yIconDiff = iconRect.y - tabRect.y; 398 xIconDiff = (tabRect.x + tabRect.width) - (iconRect.x + iconRect.width); 399 } 400 401 // rotation changes needed for the rendering 402 // we are rotating so we can't just use the rects wholesale. 403 textRect.x = tabRect.x + yDiff; 404 textRect.y = tabRect.y + xDiff; 405 406 int tempVal = textRect.height; 407 textRect.height = textRect.width; 408 textRect.width = tempVal; 409 // g.setColor(Color.red); 410 // g.drawLine(textRect.x, textRect.y, textRect.x+textRect.height, textRect.y+textRect.width); 411 // g.drawLine(textRect.x+textRect.height, textRect.y, textRect.x, textRect.y+textRect.width); 412 413 iconRect.x = tabRect.x + yIconDiff; 414 iconRect.y = tabRect.y + xIconDiff; 415 416 tempVal = iconRect.height; 417 iconRect.height = iconRect.width; 418 iconRect.width = tempVal; 419 } 420 421 protected void paintTabNormal(final Graphics g, final int tabPlacement, final int tabIndex, final boolean active, final boolean frameActive, final boolean isLeftToRight) { 422 paintTabNormalFromRect(g, tabPlacement, rects[tabIndex], tabIndex, fIconRect, fTextRect, active, frameActive, isLeftToRight); 423 } 424 425 protected void paintTabNormalFromRect(final Graphics g, 426 final int tabPlacement, 427 final Rectangle tabRect, 428 final int nonRectIndex, 429 final Rectangle iconRect, 430 final Rectangle textRect, 431 final boolean active, 432 final boolean frameActive, 433 final boolean isLeftToRight) { 434 final int selectedIndex = tabPane.getSelectedIndex(); 435 final boolean isSelected = selectedIndex == nonRectIndex; 436 437 paintCUITab(g, tabPlacement, tabRect, isSelected, frameActive, isLeftToRight, nonRectIndex); 438 439 textRect.setBounds(tabRect); 440 fContentRect.setBounds(tabRect); 441 paintContents(g, tabPlacement, nonRectIndex, tabRect, iconRect, textRect, isSelected); 442 } 443 444 protected void paintCUITab(final Graphics g, final int tabPlacement, 445 final Rectangle tabRect, 446 final boolean isSelected, 447 final boolean frameActive, 448 final boolean isLeftToRight, 449 final int nonRectIndex) { 450 final int tabCount = tabPane.getTabCount(); 451 452 final boolean needsLeftScrollTab = visibleTabState.needsLeftScrollTab(); 453 final boolean needsRightScrollTab = visibleTabState.needsRightScrollTab(); 454 455 // first or last 456 boolean first = nonRectIndex == 0; 457 boolean last = nonRectIndex == tabCount - 1; 458 if (needsLeftScrollTab || needsRightScrollTab) { 459 if (nonRectIndex == -1) { 460 first = false; 461 last = true; 462 } else if (nonRectIndex == -2) { 463 first = true; 464 last = false; 465 } else { 466 if (needsLeftScrollTab) first = false; 467 if (needsRightScrollTab) last = false; 468 } 469 } 470 471 if (tabPlacement == LEFT || tabPlacement == RIGHT) { 472 final boolean tempSwap = last; 473 last = first; 474 first = tempSwap; 475 } 476 477 final State state = getState(nonRectIndex, frameActive, isSelected); 478 painter.state.set(state); 479 painter.state.set(isSelected || (state == State.INACTIVE && frameActive) ? BooleanValue.YES : BooleanValue.NO); 480 painter.state.set(getSegmentPosition(first, last, isLeftToRight)); 481 final int selectedIndex = tabPane.getSelectedIndex(); 482 painter.state.set(getSegmentTrailingSeparator(nonRectIndex, selectedIndex, isLeftToRight)); 483 painter.state.set(getSegmentLeadingSeparator(nonRectIndex, selectedIndex, isLeftToRight)); 484 painter.state.set(tabPane.hasFocus() && isSelected ? Focused.YES : Focused.NO); 485 painter.paint(g, tabPane, tabRect.x, tabRect.y, tabRect.width, tabRect.height); 486 487 if (isScrollTabIndex(nonRectIndex)) return; 488 489 final Color color = tabPane.getBackgroundAt(nonRectIndex); 490 if (color == null || (color instanceof UIResource)) return; 491 492 if (!isLeftToRight && (tabPlacement == TOP || tabPlacement == BOTTOM)) { 493 final boolean tempSwap = last; 494 last = first; 495 first = tempSwap; 496 } 497 498 fillTabWithBackground(g, tabRect, tabPlacement, first, last, color); 499 } 500 501 protected Direction getDirection() { 502 switch (tabPane.getTabPlacement()) { 503 case SwingConstants.BOTTOM: return Direction.SOUTH; 504 case SwingConstants.LEFT: return Direction.WEST; 505 case SwingConstants.RIGHT: return Direction.EAST; 506 } 507 return Direction.NORTH; 508 } 509 510 protected static SegmentPosition getSegmentPosition(final boolean first, final boolean last, final boolean isLeftToRight) { 511 if (first && last) return SegmentPosition.ONLY; 512 if (first) return isLeftToRight ? SegmentPosition.FIRST : SegmentPosition.LAST; 513 if (last) return isLeftToRight ? SegmentPosition.LAST : SegmentPosition.FIRST; 514 return SegmentPosition.MIDDLE; 515 } 516 517 protected SegmentTrailingSeparator getSegmentTrailingSeparator(final int index, final int selectedIndex, final boolean isLeftToRight) { 518 return SegmentTrailingSeparator.YES; 519 } 520 521 protected SegmentLeadingSeparator getSegmentLeadingSeparator(final int index, final int selectedIndex, final boolean isLeftToRight) { 522 return SegmentLeadingSeparator.NO; 523 } 524 525 protected boolean isTabBeforeSelectedTab(final int index, final int selectedIndex, final boolean isLeftToRight) { 526 if (index == -2 && visibleTabState.getIndex(0) == selectedIndex) return true; 527 int indexBeforeSelectedIndex = isLeftToRight ? selectedIndex - 1 : selectedIndex + 1; 528 return index == indexBeforeSelectedIndex ? true : false; 529 } 530 531 protected State getState(final int index, final boolean frameActive, final boolean isSelected) { 532 if (!frameActive) return State.INACTIVE; 533 if (!tabPane.isEnabled()) return State.DISABLED; 534 if (JRSUIUtils.TabbedPane.useLegacyTabs()) { 535 if (isSelected) return State.PRESSED; 536 if (pressedTab == index) return State.INACTIVE; 537 } else { 538 if (isSelected) return State.ACTIVE; 539 if (pressedTab == index) return State.PRESSED; 540 } 541 return State.ACTIVE; 542 } 543 544 /** 545 * This routine adjusts the background fill rect so it just fits inside a tab, allowing for 546 * whether we're talking about a first tab or last tab. NOTE that this code is very much 547 * Aqua 2 dependent! 548 */ 549 static class AlterRects { 550 Rectangle standard, first, last; 551 AlterRects(final int x, final int y, final int w, final int h) { standard = new Rectangle(x, y, w, h); } 552 AlterRects start(final int x, final int y, final int w, final int h) { first = new Rectangle(x, y, w, h); return this; } 553 AlterRects end(final int x, final int y, final int w, final int h) { last = new Rectangle(x, y, w, h); return this; } 554 555 static Rectangle alter(final Rectangle r, final Rectangle o) { 556 // r = new Rectangle(r); 557 r.x += o.x; 558 r.y += o.y; 559 r.width += o.width; 560 r.height += o.height; 561 return r; 562 } 563 } 564 565 static AlterRects[] alterRects = new AlterRects[5]; 566 567 protected static AlterRects getAlterationFor(final int tabPlacement) { 568 if (alterRects[tabPlacement] != null) return alterRects[tabPlacement]; 569 570 switch (tabPlacement) { 571 case LEFT: return alterRects[LEFT] = new AlterRects(2, 0, -4, 1).start(0, 0, 0, -4).end(0, 3, 0, -3); 572 case RIGHT: return alterRects[RIGHT] = new AlterRects(1, 0, -4, 1).start(0, 0, 0, -4).end(0, 3, 0, -3); 573 case BOTTOM: return alterRects[BOTTOM] = new AlterRects(0, 1, 0, -4).start(3, 0, -3, 0).end(0, 0, -3, 0); 574 case TOP: 575 default: return alterRects[TOP] = new AlterRects(0, 2, 0, -4).start(3, 0, -3, 0).end(0, 0, -3, 0); 576 } 577 } 578 579 protected void fillTabWithBackground(final Graphics g, final Rectangle rect, final int tabPlacement, final boolean first, final boolean last, final Color color) { 580 final Rectangle fillRect = new Rectangle(rect); 581 582 final AlterRects alteration = getAlterationFor(tabPlacement); 583 AlterRects.alter(fillRect, alteration.standard); 584 if (first) AlterRects.alter(fillRect, alteration.first); 585 if (last) AlterRects.alter(fillRect, alteration.last); 586 587 g.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), (int)(color.getAlpha() * 0.25))); 588 g.fillRoundRect(fillRect.x, fillRect.y, fillRect.width, fillRect.height, 3, 1); 589 } 590 591 protected Insets getContentBorderInsets(final int tabPlacement) { 592 final Insets draw = getContentDrawingInsets(tabPlacement); // will be rotated 593 594 rotateInsets(contentBorderInsets, currentContentBorderInsets, tabPlacement); 595 596 currentContentBorderInsets.left += draw.left; 597 currentContentBorderInsets.right += draw.right; 598 currentContentBorderInsets.top += draw.top; 599 currentContentBorderInsets.bottom += draw.bottom; 600 601 return currentContentBorderInsets; 602 } 603 604 protected static void rotateInsets(final Insets topInsets, final Insets targetInsets, final int targetPlacement) { 605 switch (targetPlacement) { 606 case LEFT: 607 targetInsets.top = topInsets.left; 608 targetInsets.left = topInsets.top; 609 targetInsets.bottom = topInsets.right; 610 targetInsets.right = topInsets.bottom; 611 break; 612 case BOTTOM: 613 targetInsets.top = topInsets.bottom; 614 targetInsets.left = topInsets.left; 615 targetInsets.bottom = topInsets.top; 616 targetInsets.right = topInsets.right; 617 break; 618 case RIGHT: 619 targetInsets.top = topInsets.right; 620 targetInsets.left = topInsets.bottom; 621 targetInsets.bottom = topInsets.left; 622 targetInsets.right = topInsets.top; 623 break; 624 case TOP: 625 default: 626 targetInsets.top = topInsets.top; 627 targetInsets.left = topInsets.left; 628 targetInsets.bottom = topInsets.bottom; 629 targetInsets.right = topInsets.right; 630 } 631 } 632 633 protected Insets getContentDrawingInsets(final int tabPlacement) { 634 rotateInsets(contentDrawingInsets, currentContentDrawingInsets, tabPlacement); 635 return currentContentDrawingInsets; 636 } 637 638 protected Icon getIconForTab(final int tabIndex) { 639 final Icon mainIcon = super.getIconForTab(tabIndex); 640 if (mainIcon == null) return null; 641 642 final int iconHeight = mainIcon.getIconHeight(); 643 if (iconHeight <= kMaxIconSize) return mainIcon; 644 final float ratio = (float)kMaxIconSize / (float)iconHeight; 645 646 final int iconWidth = mainIcon.getIconWidth(); 647 return new AquaIcon.CachingScalingIcon((int)(iconWidth * ratio), kMaxIconSize) { 648 Image createImage() { 649 return AquaIcon.getImageForIcon(mainIcon); 650 } 651 }; 652 } 653 654 private static final int TAB_BORDER_INSET = 9; 655 protected void paintContentBorder(final Graphics g, final int tabPlacement, final int selectedIndex) { 656 final int width = tabPane.getWidth(); 657 final int height = tabPane.getHeight(); 658 final Insets insets = tabPane.getInsets(); 659 660 int x = insets.left; 661 int y = insets.top; 662 int w = width - insets.right - insets.left; 663 int h = height - insets.top - insets.bottom; 664 665 switch (tabPlacement) { 666 case TOP: 667 y += TAB_BORDER_INSET; 668 h -= TAB_BORDER_INSET; 669 break; 670 case BOTTOM: 671 h -= TAB_BORDER_INSET;// - 2; 672 break; 673 case LEFT: 674 x += TAB_BORDER_INSET;// - 5; 675 w -= TAB_BORDER_INSET;// + 1; 676 break; 677 case RIGHT: 678 w -= TAB_BORDER_INSET;// + 1; 679 break; 680 } 681 682 if (tabPane.isOpaque()) { 683 g.setColor(tabPane.getBackground()); 684 g.fillRect(0, 0, width, height); 685 } 686 687 AquaGroupBorder.getTabbedPaneGroupBorder().paintBorder(tabPane, g, x, y, w, h); 688 } 689 690 // see paintContentBorder 691 protected void repaintContentBorderEdge() { 692 final int width = tabPane.getWidth(); 693 final int height = tabPane.getHeight(); 694 final Insets insets = tabPane.getInsets(); 695 final int tabPlacement = tabPane.getTabPlacement(); 696 final Insets localContentBorderInsets = getContentBorderInsets(tabPlacement); 697 698 int x = insets.left; 699 int y = insets.top; 700 int w = width - insets.right - insets.left; 701 int h = height - insets.top - insets.bottom; 702 703 switch (tabPlacement) { 704 case LEFT: 705 x += calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth); 706 w = localContentBorderInsets.left; 707 break; 708 case RIGHT: 709 w = localContentBorderInsets.right; 710 break; 711 case BOTTOM: 712 h = localContentBorderInsets.bottom; 713 break; 714 case TOP: 715 default: 716 y += calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight); 717 h = localContentBorderInsets.top; 718 } 719 tabPane.repaint(x, y, w, h); 720 } 721 722 public boolean isTabVisible(final int index) { 723 if (index == -1 || index == -2) return true; 724 for (int i = 0; i < visibleTabState.getTotal(); i++) { 725 if (visibleTabState.getIndex(i) == index) return true; 726 } 727 return false; 728 } 729 730 /** 731 * Returns the bounds of the specified tab index. The bounds are 732 * with respect to the JTabbedPane's coordinate space. If the tab at this 733 * index is not currently visible in the UI, then returns null. 734 */ 735 @Override 736 public Rectangle getTabBounds(final JTabbedPane pane, final int i) { 737 if (visibleTabState.needsScrollTabs() 738 && (visibleTabState.isBefore(i) || visibleTabState.isAfter(i))) { 739 return null; 740 } 741 return super.getTabBounds(pane, i); 742 } 743 744 /** 745 * Returns the tab index which intersects the specified point 746 * in the JTabbedPane's coordinate space. 747 */ 748 public int tabForCoordinate(final JTabbedPane pane, final int x, final int y) { 749 ensureCurrentLayout(); 750 final Point p = new Point(x, y); 751 if (visibleTabState.needsScrollTabs()) { 752 for (int i = 0; i < visibleTabState.getTotal(); i++) { 753 final int realOffset = visibleTabState.getIndex(i); 754 if (rects[realOffset].contains(p.x, p.y)) return realOffset; 755 } 756 if (visibleTabState.getRightScrollTabRect().contains(p.x, p.y)) return -1; //tabPane.getTabCount(); 757 } else { 758 //old way 759 final int tabCount = tabPane.getTabCount(); 760 for (int i = 0; i < tabCount; i++) { 761 if (rects[i].contains(p.x, p.y)) return i; 762 } 763 } 764 return -1; 765 } 766 767 protected Insets getTabInsets(final int tabPlacement, final int tabIndex) { 768 switch (tabPlacement) { 769 case LEFT: return UIManager.getInsets("TabbedPane.leftTabInsets"); 770 case RIGHT: return UIManager.getInsets("TabbedPane.rightTabInsets"); 771 } 772 return tabInsets; 773 } 774 775 // This is the preferred size - the layout manager will ignore if it has to 776 protected int calculateTabHeight(final int tabPlacement, final int tabIndex, final int fontHeight) { 777 // Constrain to what the Mac allows 778 final int result = super.calculateTabHeight(tabPlacement, tabIndex, fontHeight); 779 780 // force tabs to have a max height for aqua 781 if (result <= kSmallTabHeight) return kSmallTabHeight; 782 return kLargeTabHeight; 783 } 784 785 // JBuilder requested this - it's against HI, but then so are multiple rows 786 protected boolean shouldRotateTabRuns(final int tabPlacement) { 787 return false; 788 } 789 790 protected class TabbedPanePropertyChangeHandler extends PropertyChangeHandler { 791 public void propertyChange(final PropertyChangeEvent e) { 792 final String prop = e.getPropertyName(); 793 794 if (!AquaFocusHandler.FRAME_ACTIVE_PROPERTY.equals(prop)) { 795 super.propertyChange(e); 796 return; 797 } 798 799 final JTabbedPane comp = (JTabbedPane)e.getSource(); 800 comp.repaint(); 801 802 // Repaint the "front" tab and the border 803 final int selected = tabPane.getSelectedIndex(); 804 final Rectangle[] theRects = rects; 805 if (selected >= 0 && selected < theRects.length) comp.repaint(theRects[selected]); 806 repaintContentBorderEdge(); 807 } 808 } 809 810 protected ChangeListener createChangeListener() { 811 return new ChangeListener() { 812 public void stateChanged(final ChangeEvent e) { 813 if (!isTabVisible(tabPane.getSelectedIndex())) popupSelectionChanged = true; 814 tabPane.revalidate(); 815 tabPane.repaint(); 816 } 817 }; 818 } 819 820 protected class FocusHandler extends FocusAdapter { 821 Rectangle sWorkingRect = new Rectangle(); 822 823 public void focusGained(final FocusEvent e) { 824 if (isDefaultFocusReceiver(tabPane) && !hasAvoidedFirstFocus) { 825 KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent(); 826 hasAvoidedFirstFocus = true; 827 } 828 adjustPaintingRectForFocusRing(e); 829 } 830 831 public void focusLost(final FocusEvent e) { 832 adjustPaintingRectForFocusRing(e); 833 } 834 835 void adjustPaintingRectForFocusRing(final FocusEvent e) { 836 final JTabbedPane pane = (JTabbedPane)e.getSource(); 837 final int tabCount = pane.getTabCount(); 838 final int selectedIndex = pane.getSelectedIndex(); 839 840 if (selectedIndex != -1 && tabCount > 0 && tabCount == rects.length) { 841 sWorkingRect.setBounds(rects[selectedIndex]); 842 sWorkingRect.grow(4, 4); 843 pane.repaint(sWorkingRect); 844 } 845 } 846 847 boolean isDefaultFocusReceiver(final JComponent component) { 848 if (isDefaultFocusReceiver == null) { 849 Component defaultFocusReceiver = KeyboardFocusManager.getCurrentKeyboardFocusManager().getDefaultFocusTraversalPolicy().getDefaultComponent(getTopLevelFocusCycleRootAncestor(component)); 850 isDefaultFocusReceiver = defaultFocusReceiver != null && defaultFocusReceiver.equals(component); 851 } 852 return isDefaultFocusReceiver.booleanValue(); 853 } 854 855 Container getTopLevelFocusCycleRootAncestor(Container container) { 856 Container ancestor; 857 while ((ancestor = container.getFocusCycleRootAncestor()) != null) { 858 container = ancestor; 859 } 860 return container; 861 } 862 } 863 864 class MouseHandler extends MouseInputAdapter implements ActionListener { 865 866 int trackingTab = -3; 867 private final Timer popupTimer = new Timer(500, this); 868 869 MouseHandler() { 870 popupTimer.setRepeats(false); 871 } 872 873 void dispose (){ 874 popupTimer.removeActionListener(this); 875 popupTimer.stop(); 876 } 877 878 public void mousePressed(final MouseEvent e) { 879 final JTabbedPane pane = (JTabbedPane)e.getSource(); 880 if (!pane.isEnabled()) { 881 trackingTab = -3; 882 return; 883 } 884 885 final Point p = e.getPoint(); 886 trackingTab = getCurrentTab(pane, p); 887 if (trackingTab == -3 || (!shouldRepaintSelectedTabOnMouseDown() && trackingTab == pane.getSelectedIndex())) { 888 trackingTab = -3; 889 return; 890 } 891 892 if (trackingTab < 0 && trackingTab > -3) { 893 popupTimer.start(); 894 } 895 896 pressedTab = trackingTab; 897 repaint(pane, pressedTab); 898 } 899 900 public void mouseDragged(final MouseEvent e) { 901 if (trackingTab < -2) return; 902 903 final JTabbedPane pane = (JTabbedPane)e.getSource(); 904 final int currentTab = getCurrentTab(pane, e.getPoint()); 905 906 if (currentTab != trackingTab) { 907 pressedTab = -3; 908 } else { 909 pressedTab = trackingTab; 910 } 911 912 if (trackingTab < 0 && trackingTab > -3) { 913 popupTimer.start(); 914 } 915 916 repaint(pane, trackingTab); 917 } 918 919 public void mouseReleased(final MouseEvent e) { 920 if (trackingTab < -2) return; 921 922 popupTimer.stop(); 923 924 final JTabbedPane pane = (JTabbedPane)e.getSource(); 925 final Point p = e.getPoint(); 926 final int currentTab = getCurrentTab(pane, p); 927 928 if (trackingTab == -1 && currentTab == -1) { 929 pane.setSelectedIndex(pane.getSelectedIndex() + 1); 930 } 931 932 if (trackingTab == -2 && currentTab == -2) { 933 pane.setSelectedIndex(pane.getSelectedIndex() - 1); 934 } 935 936 if (trackingTab >= 0 && currentTab == trackingTab) { 937 pane.setSelectedIndex(trackingTab); 938 } 939 940 repaint(pane, trackingTab); 941 942 pressedTab = -3; 943 trackingTab = -3; 944 } 945 946 public void actionPerformed(final ActionEvent e) { 947 if (trackingTab != pressedTab) { 948 return; 949 } 950 951 if (trackingTab == -1) { 952 showFullPopup(false); 953 trackingTab = -3; 954 } 955 956 if (trackingTab == -2) { 957 showFullPopup(true); 958 trackingTab = -3; 959 } 960 } 961 962 int getCurrentTab(final JTabbedPane pane, final Point p) { 963 final int tabIndex = tabForCoordinate(pane, p.x, p.y); 964 if (tabIndex >= 0 && pane.isEnabledAt(tabIndex)) return tabIndex; 965 966 if (visibleTabState.needsLeftScrollTab() && visibleTabState.getLeftScrollTabRect().contains(p)) return -2; 967 if (visibleTabState.needsRightScrollTab() && visibleTabState.getRightScrollTabRect().contains(p)) return -1; 968 969 return -3; 970 } 971 972 void repaint(final JTabbedPane pane, final int tab) { 973 switch (tab) { 974 case -1: 975 pane.repaint(visibleTabState.getRightScrollTabRect()); 976 return; 977 case -2: 978 pane.repaint(visibleTabState.getLeftScrollTabRect()); 979 return; 980 default: 981 if (trackingTab >= 0) pane.repaint(rects[trackingTab]); 982 return; 983 } 984 } 985 986 void showFullPopup(final boolean firstTab) { 987 final JPopupMenu popup = new JPopupMenu(); 988 989 for (int i = 0; i < tabPane.getTabCount(); i++) { 990 if (firstTab ? visibleTabState.isBefore(i) : visibleTabState.isAfter(i)) { 991 popup.add(createMenuItem(i)); 992 } 993 } 994 995 if (firstTab) { 996 final Rectangle leftScrollTabRect = visibleTabState.getLeftScrollTabRect(); 997 final Dimension popupRect = popup.getPreferredSize(); 998 popup.show(tabPane, leftScrollTabRect.x - popupRect.width, leftScrollTabRect.y + 7); 999 } else { 1000 final Rectangle rightScrollTabRect = visibleTabState.getRightScrollTabRect(); 1001 popup.show(tabPane, rightScrollTabRect.x + rightScrollTabRect.width, rightScrollTabRect.y + 7); 1002 } 1003 1004 popup.addPopupMenuListener(new PopupMenuListener() { 1005 public void popupMenuCanceled(final PopupMenuEvent e) { } 1006 public void popupMenuWillBecomeVisible(final PopupMenuEvent e) { } 1007 1008 public void popupMenuWillBecomeInvisible(final PopupMenuEvent e) { 1009 pressedTab = -3; 1010 tabPane.repaint(visibleTabState.getLeftScrollTabRect()); 1011 tabPane.repaint(visibleTabState.getRightScrollTabRect()); 1012 } 1013 }); 1014 } 1015 1016 JMenuItem createMenuItem(final int i) { 1017 final Component component = getTabComponentAt(i); 1018 final JMenuItem menuItem; 1019 if (component == null) { 1020 menuItem = new JMenuItem(tabPane.getTitleAt(i), tabPane.getIconAt(i)); 1021 } else { 1022 @SuppressWarnings("serial") // anonymous class 1023 JMenuItem tmp = new JMenuItem() { 1024 public void paintComponent(final Graphics g) { 1025 super.paintComponent(g); 1026 final Dimension size = component.getSize(); 1027 component.setSize(getSize()); 1028 component.validate(); 1029 component.paint(g); 1030 component.setSize(size); 1031 } 1032 1033 public Dimension getPreferredSize() { 1034 return component.getPreferredSize(); 1035 } 1036 }; 1037 menuItem = tmp; 1038 } 1039 1040 final Color background = tabPane.getBackgroundAt(i); 1041 if (!(background instanceof UIResource)) { 1042 menuItem.setBackground(background); 1043 } 1044 1045 menuItem.setForeground(tabPane.getForegroundAt(i)); 1046 // for <rdar://problem/3520267> make sure to disable items that are disabled in the tab. 1047 if (!tabPane.isEnabledAt(i)) menuItem.setEnabled(false); 1048 1049 final int fOffset = i; 1050 menuItem.addActionListener(new ActionListener() { 1051 public void actionPerformed(final ActionEvent ae) { 1052 boolean visible = isTabVisible(fOffset); 1053 tabPane.setSelectedIndex(fOffset); 1054 if (!visible) { 1055 popupSelectionChanged = true; 1056 tabPane.invalidate(); 1057 tabPane.repaint(); 1058 } 1059 } 1060 }); 1061 1062 return menuItem; 1063 } 1064 } 1065 1066 protected class AquaTruncatingTabbedPaneLayout extends AquaTabbedPaneCopyFromBasicUI.TabbedPaneLayout { 1067 // fix for Radar #3346131 1068 protected int preferredTabAreaWidth(final int tabPlacement, final int height) { 1069 // Our superclass wants to stack tabs, but we rotate them, 1070 // so when tabs are on the left or right we know that 1071 // our width is actually the "height" of a tab which is then 1072 // rotated. 1073 if (tabPlacement == SwingConstants.LEFT || tabPlacement == SwingConstants.RIGHT) { 1074 return super.preferredTabAreaHeight(tabPlacement, height); 1075 } 1076 1077 return super.preferredTabAreaWidth(tabPlacement, height); 1078 } 1079 1080 protected int preferredTabAreaHeight(final int tabPlacement, final int width) { 1081 if (tabPlacement == SwingConstants.LEFT || tabPlacement == SwingConstants.RIGHT) { 1082 return super.preferredTabAreaWidth(tabPlacement, width); 1083 } 1084 1085 return super.preferredTabAreaHeight(tabPlacement, width); 1086 } 1087 1088 protected void calculateTabRects(final int tabPlacement, final int tabCount) { 1089 if (tabCount <= 0) return; 1090 1091 superCalculateTabRects(tabPlacement, tabCount); // does most of the hard work 1092 1093 // If they haven't been padded (which they only do when there are multiple rows) we should center them 1094 if (rects.length <= 0) return; 1095 1096 visibleTabState.alignRectsRunFor(rects, tabPane.getSize(), tabPlacement, AquaUtils.isLeftToRight(tabPane)); 1097 } 1098 1099 protected void padTabRun(final int tabPlacement, final int start, final int end, final int max) { 1100 if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { 1101 super.padTabRun(tabPlacement, start, end, max); 1102 return; 1103 } 1104 1105 final Rectangle lastRect = rects[end]; 1106 final int runHeight = (lastRect.y + lastRect.height) - rects[start].y; 1107 final int deltaHeight = max - (lastRect.y + lastRect.height); 1108 final float factor = (float)deltaHeight / (float)runHeight; 1109 for (int i = start; i <= end; i++) { 1110 final Rectangle pastRect = rects[i]; 1111 if (i > start) { 1112 pastRect.y = rects[i - 1].y + rects[i - 1].height; 1113 } 1114 pastRect.height += Math.round(pastRect.height * factor); 1115 } 1116 lastRect.height = max - lastRect.y; 1117 } 1118 1119 /** 1120 * This is a massive routine and I left it like this because the bulk of the code comes 1121 * from the BasicTabbedPaneUI class. Here is what it does: 1122 * 1. Calculate rects for the tabs - we have to play tricks here because our right and left tabs 1123 * should get widths calculated the same way as top and bottom, but they will be rotated so the 1124 * calculated width is stored as the rect height. 1125 * 2. Decide if we can fit all the tabs. 1126 * 3. When we cannot fit all the tabs we create a tab popup, and then layout the new tabs until 1127 * we can't fit them anymore. Laying them out is a matter of adding them into the visible list 1128 * and shifting them horizontally to the correct location. 1129 */ 1130 protected synchronized void superCalculateTabRects(final int tabPlacement, final int tabCount) { 1131 final Dimension size = tabPane.getSize(); 1132 final Insets insets = tabPane.getInsets(); 1133 final Insets localTabAreaInsets = getTabAreaInsets(tabPlacement); 1134 1135 // Calculate bounds within which a tab run must fit 1136 final int returnAt; 1137 final int x, y; 1138 switch (tabPlacement) { 1139 case SwingConstants.LEFT: 1140 maxTabWidth = calculateMaxTabHeight(tabPlacement); 1141 x = insets.left + localTabAreaInsets.left; 1142 y = insets.top + localTabAreaInsets.top; 1143 returnAt = size.height - (insets.bottom + localTabAreaInsets.bottom); 1144 break; 1145 case SwingConstants.RIGHT: 1146 maxTabWidth = calculateMaxTabHeight(tabPlacement); 1147 x = size.width - insets.right - localTabAreaInsets.right - maxTabWidth - 1; 1148 y = insets.top + localTabAreaInsets.top; 1149 returnAt = size.height - (insets.bottom + localTabAreaInsets.bottom); 1150 break; 1151 case SwingConstants.BOTTOM: 1152 maxTabHeight = calculateMaxTabHeight(tabPlacement); 1153 x = insets.left + localTabAreaInsets.left; 1154 y = size.height - insets.bottom - localTabAreaInsets.bottom - maxTabHeight; 1155 returnAt = size.width - (insets.right + localTabAreaInsets.right); 1156 break; 1157 case SwingConstants.TOP: 1158 default: 1159 maxTabHeight = calculateMaxTabHeight(tabPlacement); 1160 x = insets.left + localTabAreaInsets.left; 1161 y = insets.top + localTabAreaInsets.top; 1162 returnAt = size.width - (insets.right + localTabAreaInsets.right); 1163 break; 1164 } 1165 1166 tabRunOverlay = getTabRunOverlay(tabPlacement); 1167 1168 runCount = 0; 1169 selectedRun = 0; 1170 1171 if (tabCount == 0) return; 1172 1173 final FontMetrics metrics = getFontMetrics(); 1174 final boolean verticalTabRuns = (tabPlacement == SwingConstants.LEFT || tabPlacement == SwingConstants.RIGHT); 1175 final int selectedIndex = tabPane.getSelectedIndex(); 1176 1177 // calculate all the widths 1178 // if they all fit we are done, if not 1179 // we have to do the dance of figuring out which ones to show. 1180 visibleTabState.setNeedsScrollers(false); 1181 for (int i = 0; i < tabCount; i++) { 1182 final Rectangle rect = rects[i]; 1183 1184 if (verticalTabRuns) { 1185 calculateVerticalTabRunRect(rect, metrics, tabPlacement, returnAt, i, x, y); 1186 1187 // test if we need to scroll! 1188 if (rect.y + rect.height > returnAt) { 1189 visibleTabState.setNeedsScrollers(true); 1190 } 1191 } else { 1192 calculateHorizontalTabRunRect(rect, metrics, tabPlacement, returnAt, i, x, y); 1193 1194 // test if we need to scroll! 1195 if (rect.x + rect.width > returnAt) { 1196 visibleTabState.setNeedsScrollers(true); 1197 } 1198 } 1199 } 1200 1201 visibleTabState.relayoutForScrolling(rects, x, y, returnAt, selectedIndex, verticalTabRuns, tabCount, AquaUtils.isLeftToRight(tabPane)); 1202 // Pad the selected tab so that it appears raised in front 1203 1204 // if right to left and tab placement on the top or 1205 // the bottom, flip x positions and adjust by widths 1206 if (!AquaUtils.isLeftToRight(tabPane) && !verticalTabRuns) { 1207 final int rightMargin = size.width - (insets.right + localTabAreaInsets.right); 1208 for (int i = 0; i < tabCount; i++) { 1209 rects[i].x = rightMargin - rects[i].x - rects[i].width; 1210 } 1211 } 1212 } 1213 1214 private void calculateHorizontalTabRunRect(final Rectangle rect, final FontMetrics metrics, final int tabPlacement, final int returnAt, final int i, final int x, final int y) { 1215 // Tabs on TOP or BOTTOM.... 1216 if (i > 0) { 1217 rect.x = rects[i - 1].x + rects[i - 1].width; 1218 } else { 1219 tabRuns[0] = 0; 1220 runCount = 1; 1221 maxTabWidth = 0; 1222 rect.x = x; 1223 } 1224 1225 rect.width = calculateTabWidth(tabPlacement, i, metrics); 1226 maxTabWidth = Math.max(maxTabWidth, rect.width); 1227 1228 rect.y = y; 1229 rect.height = maxTabHeight; 1230 } 1231 1232 private void calculateVerticalTabRunRect(final Rectangle rect, final FontMetrics metrics, final int tabPlacement, final int returnAt, final int i, final int x, final int y) { 1233 // Tabs on LEFT or RIGHT... 1234 if (i > 0) { 1235 rect.y = rects[i - 1].y + rects[i - 1].height; 1236 } else { 1237 tabRuns[0] = 0; 1238 runCount = 1; 1239 maxTabHeight = 0; 1240 rect.y = y; 1241 } 1242 1243 rect.height = calculateTabWidth(tabPlacement, i, metrics); 1244 maxTabHeight = Math.max(maxTabHeight, rect.height); 1245 1246 rect.x = x; 1247 rect.width = maxTabWidth; 1248 } 1249 1250 protected void layoutTabComponents() { 1251 final Container tabContainer = getTabContainer(); 1252 if (tabContainer == null) return; 1253 1254 final int placement = tabPane.getTabPlacement(); 1255 final Rectangle rect = new Rectangle(); 1256 final Point delta = new Point(-tabContainer.getX(), -tabContainer.getY()); 1257 1258 for (int i = 0; i < tabPane.getTabCount(); i++) { 1259 final Component c = getTabComponentAt(i); 1260 if (c == null) continue; 1261 1262 getTabBounds(i, rect); 1263 final Insets insets = getTabInsets(tabPane.getTabPlacement(), i); 1264 final boolean isSeleceted = i == tabPane.getSelectedIndex(); 1265 1266 if (placement == SwingConstants.TOP || placement == SwingConstants.BOTTOM) { 1267 rect.x += insets.left + delta.x + getTabLabelShiftX(placement, i, isSeleceted); 1268 rect.y += insets.top + delta.y + getTabLabelShiftY(placement, i, isSeleceted) + 1; 1269 rect.width -= insets.left + insets.right; 1270 rect.height -= insets.top + insets.bottom - 1; 1271 } else { 1272 rect.x += insets.top + delta.x + getTabLabelShiftY(placement, i, isSeleceted) + (placement == SwingConstants.LEFT ? 2 : 1); 1273 rect.y += insets.left + delta.y + getTabLabelShiftX(placement, i, isSeleceted); 1274 rect.width -= insets.top + insets.bottom - 1; 1275 rect.height -= insets.left + insets.right; 1276 } 1277 1278 c.setBounds(rect); 1279 } 1280 } 1281 } 1282 }