4632N/A/*
4632N/A * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
4632N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4632N/A *
4632N/A * This code is free software; you can redistribute it and/or modify it
4632N/A * under the terms of the GNU General Public License version 2 only, as
4632N/A * published by the Free Software Foundation. Oracle designates this
4632N/A * particular file as subject to the "Classpath" exception as provided
4632N/A * by Oracle in the LICENSE file that accompanied this code.
4632N/A *
4632N/A * This code is distributed in the hope that it will be useful, but WITHOUT
4632N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
4632N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
4632N/A * version 2 for more details (a copy is included in the LICENSE file that
4632N/A * accompanied this code).
4632N/A *
4632N/A * You should have received a copy of the GNU General Public License version
4632N/A * 2 along with this work; if not, write to the Free Software Foundation,
4632N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
4632N/A *
4632N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
4632N/A * or visit www.oracle.com if you need additional information or have any
4632N/A * questions.
4632N/A */
4632N/A
4632N/Apackage com.apple.laf;
4632N/A
4632N/Aimport java.awt.*;
4632N/Aimport java.awt.event.*;
4632N/Aimport java.awt.geom.AffineTransform;
4632N/Aimport java.beans.*;
4632N/A
4632N/Aimport javax.swing.*;
4632N/Aimport javax.swing.event.*;
4632N/Aimport javax.swing.plaf.*;
4632N/Aimport javax.swing.text.View;
4632N/A
4632N/Aimport sun.swing.SwingUtilities2;
4632N/Aimport apple.laf.*;
4632N/Aimport apple.laf.JRSUIConstants.*;
4632N/A
4632N/Apublic class AquaTabbedPaneUI extends AquaTabbedPaneCopyFromBasicUI {
4632N/A private static final int kSmallTabHeight = 20; // height of a small tab
4632N/A private static final int kLargeTabHeight = 23; // height of a large tab
4632N/A private static final int kMaxIconSize = kLargeTabHeight - 7;
4632N/A
4632N/A private static final double kNinetyDegrees = (Math.PI / 2.0); // used for rotation
4632N/A
4632N/A protected final Insets currentContentDrawingInsets = new Insets(0, 0, 0, 0);
4632N/A protected final Insets currentContentBorderInsets = new Insets(0, 0, 0, 0);
4632N/A protected final Insets contentDrawingInsets = new Insets(0, 0, 0, 0);
4632N/A
4632N/A protected int pressedTab = -3; // -2 is right scroller, -1 is left scroller
4632N/A protected boolean popupSelectionChanged;
4632N/A
4632N/A protected Boolean isDefaultFocusReceiver = null;
4632N/A protected boolean hasAvoidedFirstFocus = false;
4632N/A
4632N/A // Create PLAF
4632N/A public static ComponentUI createUI(final JComponent c) {
4632N/A return new AquaTabbedPaneUI();
4632N/A }
4632N/A
4632N/A protected final AquaTabbedPaneTabState visibleTabState = new AquaTabbedPaneTabState(this);
4632N/A protected final AquaPainter<JRSUIState> painter = AquaPainter.create(JRSUIStateFactory.getTab());
4632N/A
4632N/A public AquaTabbedPaneUI() { }
4632N/A
4632N/A protected void installListeners() {
4632N/A super.installListeners();
4632N/A
4632N/A // We're not just a mouseListener, we're a mouseMotionListener
4632N/A if (mouseListener != null) {
4632N/A tabPane.addMouseMotionListener((MouseMotionListener)mouseListener);
4632N/A }
4632N/A }
4632N/A
4632N/A protected void installDefaults() {
4632N/A super.installDefaults();
4632N/A
4632N/A if (tabPane.getFont() instanceof UIResource) {
4632N/A final Boolean b = (Boolean)UIManager.get("TabbedPane.useSmallLayout");
4632N/A if (b != null && b == Boolean.TRUE) {
4632N/A tabPane.setFont(UIManager.getFont("TabbedPane.smallFont"));
4632N/A painter.state.set(Size.SMALL);
4632N/A }
4632N/A }
4632N/A
4632N/A contentDrawingInsets.set(0, 11, 13, 10);
4632N/A tabPane.setOpaque(false);
4632N/A }
4632N/A
4632N/A protected void assureRectsCreated(final int tabCount) {
4632N/A visibleTabState.init(tabCount);
4632N/A super.assureRectsCreated(tabCount);
4632N/A }
4632N/A
4632N/A protected void uninstallDefaults() {
4632N/A contentDrawingInsets.set(0, 0, 0, 0);
4632N/A }
4632N/A
4632N/A protected MouseListener createMouseListener() {
4632N/A return new MouseHandler();
4632N/A }
4632N/A
4632N/A protected FocusListener createFocusListener() {
4632N/A return new FocusHandler();
4632N/A }
4632N/A
4632N/A protected PropertyChangeListener createPropertyChangeListener() {
4632N/A return new TabbedPanePropertyChangeHandler();
4632N/A }
4632N/A
4632N/A protected LayoutManager createLayoutManager() {
4632N/A return new AquaTruncatingTabbedPaneLayout();
4632N/A }
4632N/A
4632N/A protected boolean shouldRepaintSelectedTabOnMouseDown() {
4632N/A return false;
4632N/A }
4632N/A
4632N/A // Paint Methods
4632N/A // Cache for performance
4632N/A final Rectangle fContentRect = new Rectangle();
4632N/A final Rectangle fIconRect = new Rectangle();
4632N/A final Rectangle fTextRect = new Rectangle();
4632N/A
4632N/A // UI Rendering
4632N/A public void paint(final Graphics g, final JComponent c) {
4632N/A painter.state.set(getDirection());
4632N/A
4632N/A final int tabPlacement = tabPane.getTabPlacement();
4632N/A final int selectedIndex = tabPane.getSelectedIndex();
4632N/A paintContentBorder(g, tabPlacement, selectedIndex);
4632N/A
4632N/A // we want to call ensureCurrentLayout, but it's private
4632N/A ensureCurrentLayout();
4632N/A final Rectangle clipRect = g.getClipBounds();
4632N/A
4632N/A final boolean active = tabPane.isEnabled();
4632N/A final boolean frameActive = AquaFocusHandler.isActive(tabPane);
4632N/A final boolean isLeftToRight = tabPane.getComponentOrientation().isLeftToRight() || tabPlacement == LEFT || tabPlacement == RIGHT;
4632N/A
4632N/A // Paint tabRuns of tabs from back to front
4632N/A if (visibleTabState.needsScrollTabs()) {
4632N/A paintScrollingTabs(g, clipRect, tabPlacement, selectedIndex, active, frameActive, isLeftToRight);
4632N/A return;
4632N/A }
4632N/A
4632N/A // old way
4632N/A paintAllTabs(g, clipRect, tabPlacement, selectedIndex, active, frameActive, isLeftToRight);
4632N/A }
4632N/A
4632N/A protected void paintAllTabs(final Graphics g, final Rectangle clipRect, final int tabPlacement, final int selectedIndex, final boolean active, final boolean frameActive, final boolean isLeftToRight) {
4632N/A boolean drawSelectedLast = false;
4632N/A for (int i = 0; i < rects.length; i++) {
4632N/A if (i == selectedIndex) {
4632N/A drawSelectedLast = true;
4632N/A } else {
4632N/A if (rects[i].intersects(clipRect)) {
4632N/A paintTabNormal(g, tabPlacement, i, active, frameActive, isLeftToRight);
4632N/A }
4632N/A }
4632N/A }
4632N/A
4632N/A // paint the selected tab last.
4632N/A if (drawSelectedLast && rects[selectedIndex].intersects(clipRect)) {
4632N/A paintTabNormal(g, tabPlacement, selectedIndex, active, frameActive, isLeftToRight);
4632N/A }
4632N/A }
4632N/A
4632N/A protected void paintScrollingTabs(final Graphics g, final Rectangle clipRect, final int tabPlacement, final int selectedIndex, final boolean active, final boolean frameActive, final boolean isLeftToRight) {
4632N/A// final Graphics g2 = g.create();
4632N/A// g2.setColor(Color.cyan);
4632N/A// Rectangle r = new Rectangle();
4632N/A// for (int i = 0; i < visibleTabState.getTotal(); i++) {
4632N/A// r.add(rects[visibleTabState.getIndex(i)]);
4632N/A// }
4632N/A// g2.fillRect(r.x, r.y, r.width, r.height);
4632N/A// g2.dispose();
4632N/A// System.out.println(r);
4632N/A
4632N/A // for each visible tab, except the selected one
4632N/A for (int i = 0; i < visibleTabState.getTotal(); i++) {
4632N/A final int realIndex = visibleTabState.getIndex(i);
4632N/A if (realIndex != selectedIndex) {
4632N/A if (rects[realIndex].intersects(clipRect)) {
4632N/A paintTabNormal(g, tabPlacement, realIndex, active, frameActive, isLeftToRight);
4632N/A }
4632N/A }
4632N/A }
4632N/A
4632N/A final Rectangle leftScrollTabRect = visibleTabState.getLeftScrollTabRect();
4632N/A if (visibleTabState.needsLeftScrollTab() && leftScrollTabRect.intersects(clipRect)) {
4632N/A paintTabNormalFromRect(g, tabPlacement, leftScrollTabRect, -2, fIconRect, fTextRect, visibleTabState.needsLeftScrollTab(), frameActive, isLeftToRight);
4632N/A }
4632N/A
4632N/A final Rectangle rightScrollTabRect = visibleTabState.getRightScrollTabRect();
4632N/A if (visibleTabState.needsRightScrollTab() && rightScrollTabRect.intersects(clipRect)) {
4632N/A paintTabNormalFromRect(g, tabPlacement, rightScrollTabRect, -1, fIconRect, fTextRect, visibleTabState.needsRightScrollTab(), frameActive, isLeftToRight);
4632N/A }
4632N/A
4632N/A if (selectedIndex >= 0) { // && rects[selectedIndex].intersects(clipRect)) {
4632N/A paintTabNormal(g, tabPlacement, selectedIndex, active, frameActive, isLeftToRight);
4632N/A }
4632N/A }
4632N/A
4632N/A private static boolean isScrollTabIndex(final int index) {
4632N/A return index == -1 || index == -2;
4632N/A }
4632N/A
4632N/A protected static void transposeRect(final Rectangle r) {
4632N/A int temp = r.y;
4632N/A r.y = r.x;
4632N/A r.x = temp;
4632N/A temp = r.width;
4632N/A r.width = r.height;
4632N/A r.height = temp;
4632N/A }
4632N/A
4632N/A protected int getTabLabelShiftX(final int tabPlacement, final int tabIndex, final boolean isSelected) {
4632N/A final Rectangle tabRect = (tabIndex >= 0 ? rects[tabIndex] : visibleTabState.getRightScrollTabRect());
4632N/A int nudge = 0;
4632N/A switch (tabPlacement) {
4632N/A case LEFT:
4632N/A case RIGHT:
4632N/A nudge = tabRect.height % 2;
4632N/A break;
4632N/A case BOTTOM:
4632N/A case TOP:
4632N/A default:
4632N/A nudge = tabRect.width % 2;
4632N/A }
4632N/A return nudge;
4632N/A }
4632N/A
4632N/A protected int getTabLabelShiftY(final int tabPlacement, final int tabIndex, final boolean isSelected) {
4632N/A switch (tabPlacement) {
4632N/A case RIGHT:
4632N/A case LEFT:
4632N/A case BOTTOM:
4632N/A return -1;
4632N/A case TOP:
4632N/A default:
4632N/A return 0;
4632N/A }
4632N/A }
4632N/A
4632N/A protected Icon getIconForScrollTab(final int tabPlacement, final int tabIndex, final boolean enabled) {
4632N/A boolean shouldFlip = !AquaUtils.isLeftToRight(tabPane);
4632N/A if (tabPlacement == RIGHT) shouldFlip = false;
4632N/A if (tabPlacement == LEFT) shouldFlip = true;
4632N/A
4632N/A int direction = tabIndex == -1 ? EAST : WEST;
4632N/A if (shouldFlip) {
4632N/A if (direction == EAST) {
4632N/A direction = WEST;
4632N/A } else if (direction == WEST) {
4632N/A direction = EAST;
4632N/A }
4632N/A }
4632N/A
4632N/A if (enabled) return AquaImageFactory.getArrowIconForDirection(direction);
4632N/A
4632N/A final Image icon = AquaImageFactory.getArrowImageForDirection(direction);
4632N/A return new ImageIcon(AquaUtils.generateDisabledImage(icon));
4632N/A }
4632N/A
4632N/A protected void paintContents(final Graphics g, final int tabPlacement, final int tabIndex, final Rectangle tabRect, final Rectangle iconRect, final Rectangle textRect, final boolean isSelected) {
4632N/A final Shape temp = g.getClip();
4632N/A g.clipRect(fContentRect.x, fContentRect.y, fContentRect.width, fContentRect.height);
4632N/A
4632N/A final Component component;
4632N/A final String title;
4632N/A final Icon icon;
4632N/A if (isScrollTabIndex(tabIndex)) {
4632N/A component = null;
4632N/A title = null;
4632N/A icon = getIconForScrollTab(tabPlacement, tabIndex, true);
4632N/A } else {
4632N/A component = getTabComponentAt(tabIndex);
4632N/A if (component == null) {
4632N/A title = tabPane.getTitleAt(tabIndex);
4632N/A icon = getIconForTab(tabIndex);
4632N/A } else {
4632N/A title = null;
4632N/A icon = null;
4632N/A }
4632N/A }
4632N/A
4632N/A final boolean isVertical = tabPlacement == RIGHT || tabPlacement == LEFT;
4632N/A if (isVertical) {
4632N/A transposeRect(fContentRect);
4632N/A }
4632N/A
4632N/A final Font font = tabPane.getFont();
4632N/A final FontMetrics metrics = g.getFontMetrics(font);
4632N/A
4632N/A // our scrolling tabs
4632N/A layoutLabel(tabPlacement, metrics, tabIndex < 0 ? 0 : tabIndex, title, icon, fContentRect, iconRect, textRect, false); // Never give it "isSelected" - ApprMgr handles this
4632N/A if (isVertical) {
4632N/A transposeRect(fContentRect);
4632N/A transposeRect(iconRect);
4632N/A transposeRect(textRect);
4632N/A }
4632N/A
4632N/A // from super.paintText - its normal text painting is totally wrong for the Mac
4639N/A if (!(g instanceof Graphics2D)) {
4632N/A g.setClip(temp);
4632N/A return;
4632N/A }
4639N/A final Graphics2D g2d = (Graphics2D) g;
4632N/A
4632N/A AffineTransform savedAT = null;
4632N/A if (isVertical) {
4632N/A savedAT = g2d.getTransform();
4632N/A rotateGraphics(g2d, tabRect, textRect, iconRect, tabPlacement);
4632N/A }
4632N/A
4632N/A // not for the scrolling tabs
4832N/A if (component == null && tabIndex >= 0) {
4632N/A paintTitle(g2d, font, metrics, textRect, tabIndex, title);
4632N/A }
4632N/A
4632N/A if (icon != null) {
4632N/A paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected);
4632N/A }
4632N/A
4632N/A if (savedAT != null) {
4632N/A g2d.setTransform(savedAT);
4632N/A }
4632N/A
4632N/A g.setClip(temp);
4632N/A }
4632N/A
4632N/A protected void paintTitle(final Graphics2D g2d, final Font font, final FontMetrics metrics, final Rectangle textRect, final int tabIndex, final String title) {
4632N/A final View v = getTextViewForTab(tabIndex);
4632N/A if (v != null) {
4632N/A v.paint(g2d, textRect);
4632N/A return;
4632N/A }
4632N/A
4632N/A if (title == null) return;
4632N/A
4632N/A final Color color = tabPane.getForegroundAt(tabIndex);
4632N/A if (color instanceof UIResource) {
4632N/A // sja fix getTheme().setThemeTextColor(g, isSelected, isPressed && tracking, tabPane.isEnabledAt(tabIndex));
4632N/A if (tabPane.isEnabledAt(tabIndex)) {
4632N/A g2d.setColor(Color.black);
4632N/A } else {
4632N/A g2d.setColor(Color.gray);
4632N/A }
4632N/A } else {
4632N/A g2d.setColor(color);
4632N/A }
4632N/A
4632N/A g2d.setFont(font);
4632N/A SwingUtilities2.drawString(tabPane, g2d, title, textRect.x, textRect.y + metrics.getAscent());
4632N/A }
4632N/A
4632N/A protected void rotateGraphics(final Graphics2D g2d, final Rectangle tabRect, final Rectangle textRect, final Rectangle iconRect, final int tabPlacement) {
4632N/A int yDiff = 0; // textRect.y - tabRect.y;
4632N/A int xDiff = 0; // (tabRect.x+tabRect.width) - (textRect.x+textRect.width);
4632N/A int yIconDiff = 0; // iconRect.y - tabRect.y;
4632N/A int xIconDiff = 0; // (tabRect.x+tabRect.width) - (iconRect.x + iconRect.width);
4632N/A
4632N/A final double rotateAmount = (tabPlacement == LEFT ? -kNinetyDegrees : kNinetyDegrees);
4632N/A g2d.transform(AffineTransform.getRotateInstance(rotateAmount, tabRect.x, tabRect.y));
4632N/A
4632N/A // x and y diffs are named weirdly.
4632N/A // I will rename them, but what they mean now is
4632N/A // original x offset which will be used to adjust the y coordinate for the
4632N/A // rotated context
4632N/A if (tabPlacement == LEFT) {
4632N/A g2d.translate(-tabRect.height - 1, 1);
4632N/A xDiff = textRect.x - tabRect.x;
4632N/A yDiff = tabRect.height + tabRect.y - (textRect.y + textRect.height);
4632N/A xIconDiff = iconRect.x - tabRect.x;
4632N/A yIconDiff = tabRect.height + tabRect.y - (iconRect.y + iconRect.height);
4632N/A } else {
4632N/A g2d.translate(0, -tabRect.width - 1);
4632N/A yDiff = textRect.y - tabRect.y;
4632N/A xDiff = (tabRect.x + tabRect.width) - (textRect.x + textRect.width);
4632N/A yIconDiff = iconRect.y - tabRect.y;
4632N/A xIconDiff = (tabRect.x + tabRect.width) - (iconRect.x + iconRect.width);
4632N/A }
4632N/A
4632N/A // rotation changes needed for the rendering
4632N/A // we are rotating so we can't just use the rects wholesale.
4632N/A textRect.x = tabRect.x + yDiff;
4632N/A textRect.y = tabRect.y + xDiff;
4632N/A
4632N/A int tempVal = textRect.height;
4632N/A textRect.height = textRect.width;
4632N/A textRect.width = tempVal;
4632N/A // g.setColor(Color.red);
4632N/A // g.drawLine(textRect.x, textRect.y, textRect.x+textRect.height, textRect.y+textRect.width);
4632N/A // g.drawLine(textRect.x+textRect.height, textRect.y, textRect.x, textRect.y+textRect.width);
4632N/A
4632N/A iconRect.x = tabRect.x + yIconDiff;
4632N/A iconRect.y = tabRect.y + xIconDiff;
4632N/A
4632N/A tempVal = iconRect.height;
4632N/A iconRect.height = iconRect.width;
4632N/A iconRect.width = tempVal;
4632N/A }
4632N/A
4632N/A protected void paintTabNormal(final Graphics g, final int tabPlacement, final int tabIndex, final boolean active, final boolean frameActive, final boolean isLeftToRight) {
4632N/A paintTabNormalFromRect(g, tabPlacement, rects[tabIndex], tabIndex, fIconRect, fTextRect, active, frameActive, isLeftToRight);
4632N/A }
4632N/A
4632N/A protected void paintTabNormalFromRect(final Graphics g, final int tabPlacement, final Rectangle tabRect, final int nonRectIndex, final Rectangle iconRect, final Rectangle textRect, final boolean active, final boolean frameActive, final boolean isLeftToRight) {
4632N/A final int selectedIndex = tabPane.getSelectedIndex();
4632N/A final boolean isSelected = selectedIndex == nonRectIndex;
4632N/A
4632N/A paintCUITab(g, tabPlacement, tabRect, isSelected, frameActive, isLeftToRight, nonRectIndex);
4632N/A
4632N/A textRect.setBounds(tabRect);
4632N/A fContentRect.setBounds(tabRect);
4632N/A paintContents(g, tabPlacement, nonRectIndex, tabRect, iconRect, textRect, isSelected);
4632N/A }
4632N/A
4632N/A protected void paintCUITab(final Graphics g, final int tabPlacement, final Rectangle tabRect, final boolean isSelected, final boolean frameActive, final boolean isLeftToRight, final int nonRectIndex) {
4632N/A final int tabCount = tabPane.getTabCount();
4632N/A
4632N/A final boolean needsLeftScrollTab = visibleTabState.needsLeftScrollTab();
4632N/A final boolean needsRightScrollTab = visibleTabState.needsRightScrollTab();
4632N/A
4632N/A // first or last
4632N/A boolean first = nonRectIndex == 0;
4632N/A boolean last = nonRectIndex == tabCount - 1;
4632N/A if (needsLeftScrollTab || needsRightScrollTab) {
4632N/A if (nonRectIndex == -1) {
4632N/A first = false;
4632N/A last = true;
4632N/A } else if (nonRectIndex == -2) {
4632N/A first = true;
4632N/A last = false;
4632N/A } else {
4632N/A if (needsLeftScrollTab) first = false;
4632N/A if (needsRightScrollTab) last = false;
4632N/A }
4632N/A }
4632N/A
4632N/A if (tabPlacement == LEFT || tabPlacement == RIGHT) {
4632N/A final boolean tempSwap = last;
4632N/A last = first;
4632N/A first = tempSwap;
4632N/A }
4632N/A
4632N/A final State state = getState(nonRectIndex, frameActive, isSelected);
4632N/A painter.state.set(state);
4632N/A painter.state.set(isSelected || (state == State.INACTIVE && frameActive) ? BooleanValue.YES : BooleanValue.NO);
4632N/A painter.state.set(getSegmentPosition(first, last, isLeftToRight));
4632N/A final int selectedIndex = tabPane.getSelectedIndex();
4632N/A painter.state.set(getSegmentTrailingSeparator(nonRectIndex, selectedIndex, isLeftToRight));
4632N/A painter.state.set(getSegmentLeadingSeparator(nonRectIndex, selectedIndex, isLeftToRight));
4632N/A painter.state.set(tabPane.hasFocus() && isSelected ? Focused.YES : Focused.NO);
4632N/A painter.paint(g, tabPane, tabRect.x, tabRect.y, tabRect.width, tabRect.height);
4632N/A
4632N/A if (isScrollTabIndex(nonRectIndex)) return;
4632N/A
4632N/A final Color color = tabPane.getBackgroundAt(nonRectIndex);
4632N/A if (color == null || (color instanceof UIResource)) return;
4632N/A
4632N/A if (!isLeftToRight && (tabPlacement == TOP || tabPlacement == BOTTOM)) {
4632N/A final boolean tempSwap = last;
4632N/A last = first;
4632N/A first = tempSwap;
4632N/A }
4632N/A
4632N/A fillTabWithBackground(g, tabRect, tabPlacement, first, last, color);
4632N/A }
4632N/A
4632N/A protected Direction getDirection() {
4632N/A switch (tabPane.getTabPlacement()) {
4632N/A case SwingConstants.BOTTOM: return Direction.SOUTH;
4632N/A case SwingConstants.LEFT: return Direction.WEST;
4632N/A case SwingConstants.RIGHT: return Direction.EAST;
4632N/A }
4632N/A return Direction.NORTH;
4632N/A }
4632N/A
4632N/A protected static SegmentPosition getSegmentPosition(final boolean first, final boolean last, final boolean isLeftToRight) {
4632N/A if (first && last) return SegmentPosition.ONLY;
4632N/A if (first) return isLeftToRight ? SegmentPosition.FIRST : SegmentPosition.LAST;
4632N/A if (last) return isLeftToRight ? SegmentPosition.LAST : SegmentPosition.FIRST;
4632N/A return SegmentPosition.MIDDLE;
4632N/A }
4632N/A
4632N/A protected SegmentTrailingSeparator getSegmentTrailingSeparator(final int index, final int selectedIndex, final boolean isLeftToRight) {
4632N/A return SegmentTrailingSeparator.YES;
4632N/A }
4632N/A
4632N/A protected SegmentLeadingSeparator getSegmentLeadingSeparator(final int index, final int selectedIndex, final boolean isLeftToRight) {
4632N/A return SegmentLeadingSeparator.NO;
4632N/A }
4632N/A
4632N/A protected boolean isTabBeforeSelectedTab(final int index, final int selectedIndex, final boolean isLeftToRight) {
4632N/A if (index == -2 && visibleTabState.getIndex(0) == selectedIndex) return true;
4632N/A int indexBeforeSelectedIndex = isLeftToRight ? selectedIndex - 1 : selectedIndex + 1;
4632N/A return index == indexBeforeSelectedIndex ? true : false;
4632N/A }
4632N/A
4632N/A protected State getState(final int index, final boolean frameActive, final boolean isSelected) {
4632N/A if (!frameActive) return State.INACTIVE;
4632N/A if (!tabPane.isEnabled()) return State.DISABLED;
4632N/A if (JRSUIUtils.TabbedPane.useLegacyTabs()) {
4632N/A if (isSelected) return State.PRESSED;
4632N/A if (pressedTab == index) return State.INACTIVE;
4632N/A } else {
4632N/A if (isSelected) return State.ACTIVE;
4632N/A if (pressedTab == index) return State.PRESSED;
4632N/A }
4632N/A return State.ACTIVE;
4632N/A }
4632N/A
4632N/A /**
4632N/A * This routine adjusts the background fill rect so it just fits inside a tab, allowing for
4632N/A * whether we're talking about a first tab or last tab. NOTE that this code is very much
4632N/A * Aqua 2 dependent!
4632N/A */
4632N/A static class AlterRects {
4632N/A Rectangle standard, first, last;
4632N/A AlterRects(final int x, final int y, final int w, final int h) { standard = new Rectangle(x, y, w, h); }
4632N/A AlterRects start(final int x, final int y, final int w, final int h) { first = new Rectangle(x, y, w, h); return this; }
4632N/A AlterRects end(final int x, final int y, final int w, final int h) { last = new Rectangle(x, y, w, h); return this; }
4632N/A
4632N/A static Rectangle alter(final Rectangle r, final Rectangle o) {
4632N/A // r = new Rectangle(r);
4632N/A r.x += o.x;
4632N/A r.y += o.y;
4632N/A r.width += o.width;
4632N/A r.height += o.height;
4632N/A return r;
4632N/A }
4632N/A }
4632N/A
4632N/A static AlterRects[] alterRects = new AlterRects[5];
4632N/A
4632N/A protected static AlterRects getAlterationFor(final int tabPlacement) {
4632N/A if (alterRects[tabPlacement] != null) return alterRects[tabPlacement];
4632N/A
4632N/A switch (tabPlacement) {
4632N/A case LEFT: return alterRects[LEFT] = new AlterRects(2, 0, -4, 1).start(0, 0, 0, -4).end(0, 3, 0, -3);
4632N/A case RIGHT: return alterRects[RIGHT] = new AlterRects(1, 0, -4, 1).start(0, 0, 0, -4).end(0, 3, 0, -3);
4632N/A case BOTTOM: return alterRects[BOTTOM] = new AlterRects(0, 1, 0, -4).start(3, 0, -3, 0).end(0, 0, -3, 0);
4632N/A case TOP:
4632N/A default: return alterRects[TOP] = new AlterRects(0, 2, 0, -4).start(3, 0, -3, 0).end(0, 0, -3, 0);
4632N/A }
4632N/A }
4632N/A
4632N/A protected void fillTabWithBackground(final Graphics g, final Rectangle rect, final int tabPlacement, final boolean first, final boolean last, final Color color) {
4632N/A final Rectangle fillRect = new Rectangle(rect);
4632N/A
4632N/A final AlterRects alteration = getAlterationFor(tabPlacement);
4632N/A AlterRects.alter(fillRect, alteration.standard);
4632N/A if (first) AlterRects.alter(fillRect, alteration.first);
4632N/A if (last) AlterRects.alter(fillRect, alteration.last);
4632N/A
4632N/A g.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), (int)(color.getAlpha() * 0.25)));
4632N/A g.fillRoundRect(fillRect.x, fillRect.y, fillRect.width, fillRect.height, 3, 1);
4632N/A }
4632N/A
4632N/A protected Insets getContentBorderInsets(final int tabPlacement) {
4632N/A final Insets draw = getContentDrawingInsets(tabPlacement); // will be rotated
4632N/A
4632N/A rotateInsets(contentBorderInsets, currentContentBorderInsets, tabPlacement);
4632N/A
4632N/A currentContentBorderInsets.left += draw.left;
4632N/A currentContentBorderInsets.right += draw.right;
4632N/A currentContentBorderInsets.top += draw.top;
4632N/A currentContentBorderInsets.bottom += draw.bottom;
4632N/A
4632N/A return currentContentBorderInsets;
4632N/A }
4632N/A
4632N/A protected static void rotateInsets(final Insets topInsets, final Insets targetInsets, final int targetPlacement) {
4632N/A switch (targetPlacement) {
4632N/A case LEFT:
4632N/A targetInsets.top = topInsets.left;
4632N/A targetInsets.left = topInsets.top;
4632N/A targetInsets.bottom = topInsets.right;
4632N/A targetInsets.right = topInsets.bottom;
4632N/A break;
4632N/A case BOTTOM:
4632N/A targetInsets.top = topInsets.bottom;
4632N/A targetInsets.left = topInsets.left;
4632N/A targetInsets.bottom = topInsets.top;
4632N/A targetInsets.right = topInsets.right;
4632N/A break;
4632N/A case RIGHT:
4632N/A targetInsets.top = topInsets.right;
4632N/A targetInsets.left = topInsets.bottom;
4632N/A targetInsets.bottom = topInsets.left;
4632N/A targetInsets.right = topInsets.top;
4632N/A break;
4632N/A case TOP:
4632N/A default:
4632N/A targetInsets.top = topInsets.top;
4632N/A targetInsets.left = topInsets.left;
4632N/A targetInsets.bottom = topInsets.bottom;
4632N/A targetInsets.right = topInsets.right;
4632N/A }
4632N/A }
4632N/A
4632N/A protected Insets getContentDrawingInsets(final int tabPlacement) {
4632N/A rotateInsets(contentDrawingInsets, currentContentDrawingInsets, tabPlacement);
4632N/A return currentContentDrawingInsets;
4632N/A }
4632N/A
4632N/A protected Icon getIconForTab(final int tabIndex) {
4632N/A final Icon mainIcon = super.getIconForTab(tabIndex);
4632N/A if (mainIcon == null) return null;
4632N/A
4632N/A final int iconHeight = mainIcon.getIconHeight();
4632N/A if (iconHeight <= kMaxIconSize) return mainIcon;
4632N/A final float ratio = (float)kMaxIconSize / (float)iconHeight;
4632N/A
4632N/A final int iconWidth = mainIcon.getIconWidth();
4632N/A return new AquaIcon.CachingScalingIcon((int)(iconWidth * ratio), kMaxIconSize) {
4632N/A Image createImage() {
4632N/A return AquaIcon.getImageForIcon(mainIcon);
4632N/A }
4632N/A };
4632N/A }
4632N/A
4632N/A private static final int TAB_BORDER_INSET = 9;
4632N/A protected void paintContentBorder(final Graphics g, final int tabPlacement, final int selectedIndex) {
4632N/A final int width = tabPane.getWidth();
4632N/A final int height = tabPane.getHeight();
4632N/A final Insets insets = tabPane.getInsets();
4632N/A
4632N/A int x = insets.left;
4632N/A int y = insets.top;
4632N/A int w = width - insets.right - insets.left;
4632N/A int h = height - insets.top - insets.bottom;
4632N/A
4632N/A switch (tabPlacement) {
4632N/A case TOP:
4632N/A y += TAB_BORDER_INSET;
4632N/A h -= TAB_BORDER_INSET;
4632N/A break;
4632N/A case BOTTOM:
4632N/A h -= TAB_BORDER_INSET;// - 2;
4632N/A break;
4632N/A case LEFT:
4632N/A x += TAB_BORDER_INSET;// - 5;
4632N/A w -= TAB_BORDER_INSET;// + 1;
4632N/A break;
4632N/A case RIGHT:
4632N/A w -= TAB_BORDER_INSET;// + 1;
4632N/A break;
4632N/A }
4632N/A
4632N/A if (tabPane.isOpaque()) {
4632N/A g.setColor(tabPane.getBackground());
4632N/A g.fillRect(0, 0, width, height);
4632N/A }
4632N/A
4632N/A AquaGroupBorder.getTabbedPaneGroupBorder().paintBorder(tabPane, g, x, y, w, h);
4632N/A }
4632N/A
4632N/A // see paintContentBorder
4632N/A protected void repaintContentBorderEdge() {
4632N/A final int width = tabPane.getWidth();
4632N/A final int height = tabPane.getHeight();
4632N/A final Insets insets = tabPane.getInsets();
4632N/A final int tabPlacement = tabPane.getTabPlacement();
4632N/A final Insets localContentBorderInsets = getContentBorderInsets(tabPlacement);
4632N/A
4632N/A int x = insets.left;
4632N/A int y = insets.top;
4632N/A int w = width - insets.right - insets.left;
4632N/A int h = height - insets.top - insets.bottom;
4632N/A
4632N/A switch (tabPlacement) {
4632N/A case LEFT:
4632N/A x += calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
4632N/A w = localContentBorderInsets.left;
4632N/A break;
4632N/A case RIGHT:
4632N/A w = localContentBorderInsets.right;
4632N/A break;
4632N/A case BOTTOM:
4632N/A h = localContentBorderInsets.bottom;
4632N/A break;
4632N/A case TOP:
4632N/A default:
4632N/A y += calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
4632N/A h = localContentBorderInsets.top;
4632N/A }
4632N/A tabPane.repaint(x, y, w, h);
4632N/A }
4632N/A
4632N/A public boolean isTabVisible(final int index) {
4632N/A if (index == -1 || index == -2) return true;
4632N/A for (int i = 0; i < visibleTabState.getTotal(); i++) {
4632N/A if (visibleTabState.getIndex(i) == index) return true;
4632N/A }
4632N/A return false;
4632N/A }
4632N/A
4632N/A /**
4632N/A * Returns the tab index which intersects the specified point
4632N/A * in the JTabbedPane's coordinate space.
4632N/A */
4632N/A public int tabForCoordinate(final JTabbedPane pane, final int x, final int y) {
4632N/A ensureCurrentLayout();
4632N/A final Point p = new Point(x, y);
4632N/A if (visibleTabState.needsScrollTabs()) {
4632N/A for (int i = 0; i < visibleTabState.getTotal(); i++) {
4632N/A final int realOffset = visibleTabState.getIndex(i);
4632N/A if (rects[realOffset].contains(p.x, p.y)) return realOffset;
4632N/A }
4632N/A if (visibleTabState.getRightScrollTabRect().contains(p.x, p.y)) return -1; //tabPane.getTabCount();
4632N/A } else {
4632N/A //old way
4632N/A final int tabCount = tabPane.getTabCount();
4632N/A for (int i = 0; i < tabCount; i++) {
4632N/A if (rects[i].contains(p.x, p.y)) return i;
4632N/A }
4632N/A }
4632N/A return -1;
4632N/A }
4632N/A
4632N/A protected Insets getTabInsets(final int tabPlacement, final int tabIndex) {
4632N/A switch (tabPlacement) {
4632N/A case LEFT: return UIManager.getInsets("TabbedPane.leftTabInsets");
4632N/A case RIGHT: return UIManager.getInsets("TabbedPane.rightTabInsets");
4632N/A }
4632N/A return tabInsets;
4632N/A }
4632N/A
4632N/A // This is the preferred size - the layout manager will ignore if it has to
4632N/A protected int calculateTabHeight(final int tabPlacement, final int tabIndex, final int fontHeight) {
4632N/A // Constrain to what the Mac allows
4632N/A final int result = super.calculateTabHeight(tabPlacement, tabIndex, fontHeight);
4632N/A
4632N/A // force tabs to have a max height for aqua
4632N/A if (result <= kSmallTabHeight) return kSmallTabHeight;
4632N/A return kLargeTabHeight;
4632N/A }
4632N/A
4632N/A // JBuilder requested this - it's against HI, but then so are multiple rows
4632N/A protected boolean shouldRotateTabRuns(final int tabPlacement) {
4632N/A return false;
4632N/A }
4632N/A
4632N/A protected class TabbedPanePropertyChangeHandler extends PropertyChangeHandler {
4632N/A public void propertyChange(final PropertyChangeEvent e) {
4632N/A final String prop = e.getPropertyName();
4632N/A
4632N/A if (!AquaFocusHandler.FRAME_ACTIVE_PROPERTY.equals(prop)) {
4632N/A super.propertyChange(e);
4632N/A return;
4632N/A }
4632N/A
4632N/A final JTabbedPane comp = (JTabbedPane)e.getSource();
4632N/A comp.repaint();
4632N/A
4632N/A // Repaint the "front" tab and the border
4632N/A final int selected = tabPane.getSelectedIndex();
4632N/A final Rectangle[] theRects = rects;
4632N/A if (selected >= 0 && selected < theRects.length) comp.repaint(theRects[selected]);
4632N/A repaintContentBorderEdge();
4632N/A }
4632N/A }
4632N/A
4632N/A protected ChangeListener createChangeListener() {
4632N/A return new ChangeListener() {
4632N/A public void stateChanged(final ChangeEvent e) {
4632N/A if (!isTabVisible(tabPane.getSelectedIndex())) popupSelectionChanged = true;
4632N/A tabPane.revalidate();
4632N/A tabPane.repaint();
4632N/A }
4632N/A };
4632N/A }
4632N/A
4632N/A protected class FocusHandler extends FocusAdapter {
4632N/A Rectangle sWorkingRect = new Rectangle();
4632N/A
4632N/A public void focusGained(final FocusEvent e) {
4632N/A if (isDefaultFocusReceiver(tabPane) && !hasAvoidedFirstFocus) {
4632N/A KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent();
4632N/A hasAvoidedFirstFocus = true;
4632N/A }
4632N/A adjustPaintingRectForFocusRing(e);
4632N/A }
4632N/A
4632N/A public void focusLost(final FocusEvent e) {
4632N/A adjustPaintingRectForFocusRing(e);
4632N/A }
4632N/A
4632N/A void adjustPaintingRectForFocusRing(final FocusEvent e) {
4632N/A final JTabbedPane pane = (JTabbedPane)e.getSource();
4632N/A final int tabCount = pane.getTabCount();
4632N/A final int selectedIndex = pane.getSelectedIndex();
4632N/A
4632N/A if (selectedIndex != -1 && tabCount > 0 && tabCount == rects.length) {
4632N/A sWorkingRect.setBounds(rects[selectedIndex]);
4632N/A sWorkingRect.grow(4, 4);
4632N/A pane.repaint(sWorkingRect);
4632N/A }
4632N/A }
4632N/A
4632N/A boolean isDefaultFocusReceiver(final JComponent component) {
4632N/A if (isDefaultFocusReceiver == null) {
4632N/A Component defaultFocusReceiver = KeyboardFocusManager.getCurrentKeyboardFocusManager().getDefaultFocusTraversalPolicy().getDefaultComponent(getTopLevelFocusCycleRootAncestor(component));
4632N/A isDefaultFocusReceiver = new Boolean(defaultFocusReceiver != null && defaultFocusReceiver.equals(component));
4632N/A }
4632N/A return isDefaultFocusReceiver.booleanValue();
4632N/A }
4632N/A
4632N/A Container getTopLevelFocusCycleRootAncestor(Container container) {
4632N/A Container ancestor;
4632N/A while ((ancestor = container.getFocusCycleRootAncestor()) != null) {
4632N/A container = ancestor;
4632N/A }
4632N/A return container;
4632N/A }
4632N/A }
4632N/A
4632N/A public class MouseHandler extends MouseInputAdapter implements ActionListener {
4632N/A protected int trackingTab = -3;
4632N/A protected Timer popupTimer = new Timer(500, this);
4632N/A
4632N/A public MouseHandler() {
4632N/A popupTimer.setRepeats(false);
4632N/A }
4632N/A
4632N/A public void mousePressed(final MouseEvent e) {
4632N/A final JTabbedPane pane = (JTabbedPane)e.getSource();
4632N/A if (!pane.isEnabled()) {
4632N/A trackingTab = -3;
4632N/A return;
4632N/A }
4632N/A
4632N/A final Point p = e.getPoint();
4632N/A trackingTab = getCurrentTab(pane, p);
4632N/A if (trackingTab == -3 || (!shouldRepaintSelectedTabOnMouseDown() && trackingTab == pane.getSelectedIndex())) {
4632N/A trackingTab = -3;
4632N/A return;
4632N/A }
4632N/A
4632N/A if (trackingTab < 0 && trackingTab > -3) {
4632N/A popupTimer.start();
4632N/A }
4632N/A
4632N/A pressedTab = trackingTab;
4632N/A repaint(pane, pressedTab);
4632N/A }
4632N/A
4632N/A public void mouseDragged(final MouseEvent e) {
4632N/A if (trackingTab < -2) return;
4632N/A
4632N/A final JTabbedPane pane = (JTabbedPane)e.getSource();
4632N/A final int currentTab = getCurrentTab(pane, e.getPoint());
4632N/A
4632N/A if (currentTab != trackingTab) {
4632N/A pressedTab = -3;
4632N/A } else {
4632N/A pressedTab = trackingTab;
4632N/A }
4632N/A
4632N/A if (trackingTab < 0 && trackingTab > -3) {
4632N/A popupTimer.start();
4632N/A }
4632N/A
4632N/A repaint(pane, trackingTab);
4632N/A }
4632N/A
4632N/A public void mouseReleased(final MouseEvent e) {
4632N/A if (trackingTab < -2) return;
4632N/A
4632N/A popupTimer.stop();
4632N/A
4632N/A final JTabbedPane pane = (JTabbedPane)e.getSource();
4632N/A final Point p = e.getPoint();
4632N/A final int currentTab = getCurrentTab(pane, p);
4632N/A
4632N/A if (trackingTab == -1 && currentTab == -1) {
4632N/A pane.setSelectedIndex(pane.getSelectedIndex() + 1);
4632N/A }
4632N/A
4632N/A if (trackingTab == -2 && currentTab == -2) {
4632N/A pane.setSelectedIndex(pane.getSelectedIndex() - 1);
4632N/A }
4632N/A
4632N/A if (trackingTab >= 0 && currentTab == trackingTab) {
4632N/A pane.setSelectedIndex(trackingTab);
4632N/A }
4632N/A
4632N/A repaint(pane, trackingTab);
4632N/A
4632N/A pressedTab = -3;
4632N/A trackingTab = -3;
4632N/A }
4632N/A
4632N/A public void actionPerformed(final ActionEvent e) {
4632N/A if (trackingTab != pressedTab) {
4632N/A return;
4632N/A }
4632N/A
4632N/A if (trackingTab == -1) {
4632N/A showFullPopup(false);
4632N/A trackingTab = -3;
4632N/A }
4632N/A
4632N/A if (trackingTab == -2) {
4632N/A showFullPopup(true);
4632N/A trackingTab = -3;
4632N/A }
4632N/A }
4632N/A
4632N/A int getCurrentTab(final JTabbedPane pane, final Point p) {
4632N/A final int tabIndex = tabForCoordinate(pane, p.x, p.y);
4632N/A if (tabIndex >= 0 && pane.isEnabledAt(tabIndex)) return tabIndex;
4632N/A
4632N/A if (visibleTabState.needsLeftScrollTab() && visibleTabState.getLeftScrollTabRect().contains(p)) return -2;
4632N/A if (visibleTabState.needsRightScrollTab() && visibleTabState.getRightScrollTabRect().contains(p)) return -1;
4632N/A
4632N/A return -3;
4632N/A }
4632N/A
4632N/A void repaint(final JTabbedPane pane, final int tab) {
4632N/A switch (tab) {
4632N/A case -1:
4632N/A pane.repaint(visibleTabState.getRightScrollTabRect());
4632N/A return;
4632N/A case -2:
4632N/A pane.repaint(visibleTabState.getLeftScrollTabRect());
4632N/A return;
4632N/A default:
4632N/A if (trackingTab >= 0) pane.repaint(rects[trackingTab]);
4632N/A return;
4632N/A }
4632N/A }
4632N/A
4632N/A void showFullPopup(final boolean firstTab) {
4632N/A final JPopupMenu popup = new JPopupMenu();
4632N/A
4632N/A for (int i = 0; i < tabPane.getTabCount(); i++) {
4632N/A if (firstTab ? visibleTabState.isBefore(i) : visibleTabState.isAfter(i)) {
4632N/A popup.add(createMenuItem(i));
4632N/A }
4632N/A }
4632N/A
4632N/A if (firstTab) {
4632N/A final Rectangle leftScrollTabRect = visibleTabState.getLeftScrollTabRect();
4632N/A final Dimension popupRect = popup.getPreferredSize();
4632N/A popup.show(tabPane, leftScrollTabRect.x - popupRect.width, leftScrollTabRect.y + 7);
4632N/A } else {
4632N/A final Rectangle rightScrollTabRect = visibleTabState.getRightScrollTabRect();
4632N/A popup.show(tabPane, rightScrollTabRect.x + rightScrollTabRect.width, rightScrollTabRect.y + 7);
4632N/A }
4632N/A
4632N/A popup.addPopupMenuListener(new PopupMenuListener() {
4632N/A public void popupMenuCanceled(final PopupMenuEvent e) { }
4632N/A public void popupMenuWillBecomeVisible(final PopupMenuEvent e) { }
4632N/A
4632N/A public void popupMenuWillBecomeInvisible(final PopupMenuEvent e) {
4632N/A pressedTab = -3;
4632N/A tabPane.repaint(visibleTabState.getLeftScrollTabRect());
4632N/A tabPane.repaint(visibleTabState.getRightScrollTabRect());
4632N/A }
4632N/A });
4632N/A }
4632N/A
4632N/A JMenuItem createMenuItem(final int i) {
4632N/A final Component component = getTabComponentAt(i);
4632N/A final JMenuItem menuItem;
4632N/A if (component == null) {
4632N/A menuItem = new JMenuItem(tabPane.getTitleAt(i), tabPane.getIconAt(i));
4632N/A } else {
4632N/A menuItem = new JMenuItem() {
4632N/A public void paintComponent(final Graphics g) {
4632N/A super.paintComponent(g);
4632N/A final Dimension size = component.getSize();
4632N/A component.setSize(getSize());
4632N/A component.validate();
4632N/A component.paint(g);
4632N/A component.setSize(size);
4632N/A }
4632N/A
4632N/A public Dimension getPreferredSize() {
4632N/A return component.getPreferredSize();
4632N/A }
4632N/A };
4632N/A }
4632N/A
4632N/A final Color background = tabPane.getBackgroundAt(i);
4632N/A if (!(background instanceof UIResource)) {
4632N/A menuItem.setBackground(background);
4632N/A }
4632N/A
4632N/A menuItem.setForeground(tabPane.getForegroundAt(i));
4632N/A // for <rdar://problem/3520267> make sure to disable items that are disabled in the tab.
4632N/A if (!tabPane.isEnabledAt(i)) menuItem.setEnabled(false);
4632N/A
4632N/A final int fOffset = i;
4632N/A menuItem.addActionListener(new ActionListener() {
4632N/A public void actionPerformed(final ActionEvent ae) {
4632N/A boolean visible = isTabVisible(fOffset);
4632N/A tabPane.setSelectedIndex(fOffset);
4632N/A if (!visible) {
4632N/A popupSelectionChanged = true;
4632N/A tabPane.invalidate();
4632N/A tabPane.repaint();
4632N/A }
4632N/A }
4632N/A });
4632N/A
4632N/A return menuItem;
4632N/A }
4632N/A }
4632N/A
4632N/A protected class AquaTruncatingTabbedPaneLayout extends AquaTabbedPaneCopyFromBasicUI.TabbedPaneLayout {
4632N/A // fix for Radar #3346131
4632N/A protected int preferredTabAreaWidth(final int tabPlacement, final int height) {
4632N/A // Our superclass wants to stack tabs, but we rotate them,
4632N/A // so when tabs are on the left or right we know that
4632N/A // our width is actually the "height" of a tab which is then
4632N/A // rotated.
4632N/A if (tabPlacement == SwingConstants.LEFT || tabPlacement == SwingConstants.RIGHT) {
4632N/A return super.preferredTabAreaHeight(tabPlacement, height);
4632N/A }
4632N/A
4632N/A return super.preferredTabAreaWidth(tabPlacement, height);
4632N/A }
4632N/A
4632N/A protected int preferredTabAreaHeight(final int tabPlacement, final int width) {
4632N/A if (tabPlacement == SwingConstants.LEFT || tabPlacement == SwingConstants.RIGHT) {
4632N/A return super.preferredTabAreaWidth(tabPlacement, width);
4632N/A }
4632N/A
4632N/A return super.preferredTabAreaHeight(tabPlacement, width);
4632N/A }
4632N/A
4632N/A protected void calculateTabRects(final int tabPlacement, final int tabCount) {
4632N/A if (tabCount <= 0) return;
4632N/A
4632N/A superCalculateTabRects(tabPlacement, tabCount); // does most of the hard work
4632N/A
4632N/A // If they haven't been padded (which they only do when there are multiple rows) we should center them
4632N/A if (rects.length <= 0) return;
4632N/A
4632N/A visibleTabState.alignRectsRunFor(rects, tabPane.getSize(), tabPlacement, AquaUtils.isLeftToRight(tabPane));
4632N/A }
4632N/A
4632N/A protected void padTabRun(final int tabPlacement, final int start, final int end, final int max) {
4632N/A if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) {
4632N/A super.padTabRun(tabPlacement, start, end, max);
4632N/A return;
4632N/A }
4632N/A
4632N/A final Rectangle lastRect = rects[end];
4632N/A final int runHeight = (lastRect.y + lastRect.height) - rects[start].y;
4632N/A final int deltaHeight = max - (lastRect.y + lastRect.height);
4632N/A final float factor = (float)deltaHeight / (float)runHeight;
4632N/A for (int i = start; i <= end; i++) {
4632N/A final Rectangle pastRect = rects[i];
4632N/A if (i > start) {
4632N/A pastRect.y = rects[i - 1].y + rects[i - 1].height;
4632N/A }
4632N/A pastRect.height += Math.round(pastRect.height * factor);
4632N/A }
4632N/A lastRect.height = max - lastRect.y;
4632N/A }
4632N/A
4632N/A /**
4632N/A * This is a massive routine and I left it like this because the bulk of the code comes
4632N/A * from the BasicTabbedPaneUI class. Here is what it does:
4632N/A * 1. Calculate rects for the tabs - we have to play tricks here because our right and left tabs
4632N/A * should get widths calculated the same way as top and bottom, but they will be rotated so the
4632N/A * calculated width is stored as the rect height.
4632N/A * 2. Decide if we can fit all the tabs.
4632N/A * 3. When we cannot fit all the tabs we create a tab popup, and then layout the new tabs until
4632N/A * we can't fit them anymore. Laying them out is a matter of adding them into the visible list
4632N/A * and shifting them horizontally to the correct location.
4632N/A */
4632N/A protected synchronized void superCalculateTabRects(final int tabPlacement, final int tabCount) {
4632N/A final Dimension size = tabPane.getSize();
4632N/A final Insets insets = tabPane.getInsets();
4632N/A final Insets localTabAreaInsets = getTabAreaInsets(tabPlacement);
4632N/A
4632N/A // Calculate bounds within which a tab run must fit
4632N/A final int returnAt;
4632N/A final int x, y;
4632N/A switch (tabPlacement) {
4632N/A case SwingConstants.LEFT:
4632N/A maxTabWidth = calculateMaxTabHeight(tabPlacement);
4632N/A x = insets.left + localTabAreaInsets.left;
4632N/A y = insets.top + localTabAreaInsets.top;
4632N/A returnAt = size.height - (insets.bottom + localTabAreaInsets.bottom);
4632N/A break;
4632N/A case SwingConstants.RIGHT:
4632N/A maxTabWidth = calculateMaxTabHeight(tabPlacement);
4632N/A x = size.width - insets.right - localTabAreaInsets.right - maxTabWidth - 1;
4632N/A y = insets.top + localTabAreaInsets.top;
4632N/A returnAt = size.height - (insets.bottom + localTabAreaInsets.bottom);
4632N/A break;
4632N/A case SwingConstants.BOTTOM:
4632N/A maxTabHeight = calculateMaxTabHeight(tabPlacement);
4632N/A x = insets.left + localTabAreaInsets.left;
4632N/A y = size.height - insets.bottom - localTabAreaInsets.bottom - maxTabHeight;
4632N/A returnAt = size.width - (insets.right + localTabAreaInsets.right);
4632N/A break;
4632N/A case SwingConstants.TOP:
4632N/A default:
4632N/A maxTabHeight = calculateMaxTabHeight(tabPlacement);
4632N/A x = insets.left + localTabAreaInsets.left;
4632N/A y = insets.top + localTabAreaInsets.top;
4632N/A returnAt = size.width - (insets.right + localTabAreaInsets.right);
4632N/A break;
4632N/A }
4632N/A
4632N/A tabRunOverlay = getTabRunOverlay(tabPlacement);
4632N/A
4632N/A runCount = 0;
4632N/A selectedRun = 0;
4632N/A
4632N/A if (tabCount == 0) return;
4632N/A
4632N/A final FontMetrics metrics = getFontMetrics();
4632N/A final boolean verticalTabRuns = (tabPlacement == SwingConstants.LEFT || tabPlacement == SwingConstants.RIGHT);
4632N/A final int selectedIndex = tabPane.getSelectedIndex();
4632N/A
4632N/A // calculate all the widths
4632N/A // if they all fit we are done, if not
4632N/A // we have to do the dance of figuring out which ones to show.
4632N/A visibleTabState.setNeedsScrollers(false);
4632N/A for (int i = 0; i < tabCount; i++) {
4632N/A final Rectangle rect = rects[i];
4632N/A
4632N/A if (verticalTabRuns) {
4632N/A calculateVerticalTabRunRect(rect, metrics, tabPlacement, returnAt, i, x, y);
4632N/A
4632N/A // test if we need to scroll!
4632N/A if (rect.y + rect.height > returnAt) {
4632N/A visibleTabState.setNeedsScrollers(true);
4632N/A }
4632N/A } else {
4632N/A calculateHorizontalTabRunRect(rect, metrics, tabPlacement, returnAt, i, x, y);
4632N/A
4632N/A // test if we need to scroll!
4632N/A if (rect.x + rect.width > returnAt) {
4632N/A visibleTabState.setNeedsScrollers(true);
4632N/A }
4632N/A }
4632N/A }
4632N/A
4632N/A visibleTabState.relayoutForScrolling(rects, x, y, returnAt, selectedIndex, verticalTabRuns, tabCount, AquaUtils.isLeftToRight(tabPane));
4632N/A // Pad the selected tab so that it appears raised in front
4632N/A
4632N/A // if right to left and tab placement on the top or
4632N/A // the bottom, flip x positions and adjust by widths
4632N/A if (!AquaUtils.isLeftToRight(tabPane) && !verticalTabRuns) {
4632N/A final int rightMargin = size.width - (insets.right + localTabAreaInsets.right);
4632N/A for (int i = 0; i < tabCount; i++) {
4632N/A rects[i].x = rightMargin - rects[i].x - rects[i].width;
4632N/A }
4632N/A }
4632N/A }
4632N/A
4632N/A private void calculateHorizontalTabRunRect(final Rectangle rect, final FontMetrics metrics, final int tabPlacement, final int returnAt, final int i, final int x, final int y) {
4632N/A // Tabs on TOP or BOTTOM....
4632N/A if (i > 0) {
4632N/A rect.x = rects[i - 1].x + rects[i - 1].width;
4632N/A } else {
4632N/A tabRuns[0] = 0;
4632N/A runCount = 1;
4632N/A maxTabWidth = 0;
4632N/A rect.x = x;
4632N/A }
4632N/A
4632N/A rect.width = calculateTabWidth(tabPlacement, i, metrics);
4632N/A maxTabWidth = Math.max(maxTabWidth, rect.width);
4632N/A
4632N/A rect.y = y;
4632N/A rect.height = maxTabHeight;
4632N/A }
4632N/A
4632N/A private void calculateVerticalTabRunRect(final Rectangle rect, final FontMetrics metrics, final int tabPlacement, final int returnAt, final int i, final int x, final int y) {
4632N/A // Tabs on LEFT or RIGHT...
4632N/A if (i > 0) {
4632N/A rect.y = rects[i - 1].y + rects[i - 1].height;
4632N/A } else {
4632N/A tabRuns[0] = 0;
4632N/A runCount = 1;
4632N/A maxTabHeight = 0;
4632N/A rect.y = y;
4632N/A }
4632N/A
4632N/A rect.height = calculateTabWidth(tabPlacement, i, metrics);
4632N/A maxTabHeight = Math.max(maxTabHeight, rect.height);
4632N/A
4632N/A rect.x = x;
4632N/A rect.width = maxTabWidth;
4632N/A }
4632N/A
4632N/A protected void layoutTabComponents() {
4632N/A final Container tabContainer = getTabContainer();
4632N/A if (tabContainer == null) return;
4632N/A
4632N/A final int placement = tabPane.getTabPlacement();
4632N/A final Rectangle rect = new Rectangle();
4632N/A final Point delta = new Point(-tabContainer.getX(), -tabContainer.getY());
4632N/A
4632N/A for (int i = 0; i < tabPane.getTabCount(); i++) {
4632N/A final Component c = getTabComponentAt(i);
4632N/A if (c == null) continue;
4632N/A
4632N/A getTabBounds(i, rect);
4632N/A final Insets insets = getTabInsets(tabPane.getTabPlacement(), i);
4632N/A final boolean isSeleceted = i == tabPane.getSelectedIndex();
4632N/A
4632N/A if (placement == SwingConstants.TOP || placement == SwingConstants.BOTTOM) {
4632N/A rect.x += insets.left + delta.x + getTabLabelShiftX(placement, i, isSeleceted);
4632N/A rect.y += insets.top + delta.y + getTabLabelShiftY(placement, i, isSeleceted) + 1;
4632N/A rect.width -= insets.left + insets.right;
4632N/A rect.height -= insets.top + insets.bottom - 1;
4632N/A } else {
4632N/A rect.x += insets.top + delta.x + getTabLabelShiftY(placement, i, isSeleceted) + (placement == SwingConstants.LEFT ? 2 : 1);
4632N/A rect.y += insets.left + delta.y + getTabLabelShiftX(placement, i, isSeleceted);
4632N/A rect.width -= insets.top + insets.bottom - 1;
4632N/A rect.height -= insets.left + insets.right;
4632N/A }
4632N/A
4632N/A c.setBounds(rect);
4632N/A }
4632N/A }
4632N/A }
4632N/A}