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/A
4632N/Aimport javax.swing.SwingConstants;
4632N/A
4632N/Aclass AquaTabbedPaneTabState {
4632N/A static final int FIXED_SCROLL_TAB_LENGTH = 27;
4632N/A
4632N/A protected final Rectangle leftScrollTabRect = new Rectangle();
4632N/A protected final Rectangle rightScrollTabRect = new Rectangle();
4632N/A
4632N/A protected int numberOfVisibleTabs = 0;
4632N/A protected int visibleTabList[] = new int[10];
4632N/A protected int lastLeftmostTab;
4632N/A protected int lastReturnAt;
4632N/A
4632N/A private boolean needsScrollers;
4632N/A private boolean hasMoreLeftTabs;
4632N/A private boolean hasMoreRightTabs;
4632N/A
4632N/A private final AquaTabbedPaneUI pane;
4632N/A
4632N/A protected AquaTabbedPaneTabState(final AquaTabbedPaneUI pane) {
4632N/A this.pane = pane;
4632N/A }
4632N/A
4632N/A protected int getIndex(final int i) {
4632N/A if (i >= visibleTabList.length) return Integer.MIN_VALUE;
4632N/A return visibleTabList[i];
4632N/A }
4632N/A
4632N/A protected void init(final int tabCount) {
4632N/A if (tabCount < 1) needsScrollers = false;
4632N/A if (tabCount == visibleTabList.length) return;
4632N/A final int[] tempVisibleTabs = new int[tabCount];
4632N/A System.arraycopy(visibleTabList, 0, tempVisibleTabs, 0, Math.min(visibleTabList.length, tabCount));
4632N/A visibleTabList = tempVisibleTabs;
4632N/A }
4632N/A
4632N/A int getTotal() {
4632N/A return numberOfVisibleTabs;
4632N/A }
4632N/A
4632N/A boolean needsScrollTabs() {
4632N/A return needsScrollers;
4632N/A }
4632N/A
4632N/A void setNeedsScrollers(final boolean needsScrollers) {
4632N/A this.needsScrollers = needsScrollers;
4632N/A }
4632N/A
4632N/A boolean needsLeftScrollTab() {
4632N/A return hasMoreLeftTabs;
4632N/A }
4632N/A
4632N/A boolean needsRightScrollTab() {
4632N/A return hasMoreRightTabs;
4632N/A }
4632N/A
4632N/A Rectangle getLeftScrollTabRect() {
4632N/A return leftScrollTabRect;
4632N/A }
4632N/A
4632N/A Rectangle getRightScrollTabRect() {
4632N/A return rightScrollTabRect;
4632N/A }
4632N/A
4632N/A boolean isBefore(final int i) {
4632N/A if (numberOfVisibleTabs == 0) return true;
4632N/A if (i < visibleTabList[0]) return true;
4632N/A return false;
4632N/A }
4632N/A
4632N/A boolean isAfter(final int i) {
4632N/A if (i > visibleTabList[numberOfVisibleTabs - 1]) return true;
4632N/A return false;
4632N/A }
4632N/A
4632N/A private void addToEnd(final int idToAdd, final int length) {
4632N/A visibleTabList[length] = idToAdd;
4632N/A }
4632N/A
4632N/A private void addToBeginning(final int idToAdd, final int length) {
4632N/A System.arraycopy(visibleTabList, 0, visibleTabList, 1, length);
4632N/A visibleTabList[0] = idToAdd;
4632N/A }
4632N/A
4632N/A
4632N/A void relayoutForScrolling(final Rectangle[] rects, final int startX, final int startY, final int returnAt, final int selectedIndex, final boolean verticalTabRuns, final int tabCount, final boolean isLeftToRight) {
4632N/A if (!needsScrollers) {
4632N/A hasMoreLeftTabs = false;
4632N/A hasMoreRightTabs = false;
4632N/A return;
4632N/A }
4632N/A
4632N/A // we don't fit, so we need to figure the space based on the size of the popup
4632N/A // tab, then add the tabs, centering the selected tab as much as possible.
4632N/A
4632N/A // Tabs on TOP or BOTTOM or LEFT or RIGHT
4632N/A // if top or bottom, width is hardocoded
4632N/A // if left or right height should be hardcoded.
4632N/A if (verticalTabRuns) {
4632N/A rightScrollTabRect.height = FIXED_SCROLL_TAB_LENGTH;
4632N/A leftScrollTabRect.height = FIXED_SCROLL_TAB_LENGTH;
4632N/A } else {
4632N/A rightScrollTabRect.width = FIXED_SCROLL_TAB_LENGTH;
4632N/A leftScrollTabRect.width = FIXED_SCROLL_TAB_LENGTH;
4632N/A }
4632N/A
4632N/A // we have all the tab rects, we just need to adjust the x coordinates
4632N/A // and populate the visible list
4632N/A
4632N/A // sja fix what do we do if remaining width is <0??
4632N/A
4632N/A // we could try to center it based on width of tabs, but for now
4632N/A // we try to center based on number of tabs on each side, putting the extra
4632N/A // on the left (since the first right is the selected tab).
4632N/A // if we have 0 selected we will just go right, and if we have
4632N/A
4632N/A // the logic here is start with the selected tab, and then fit
4632N/A // in as many tabs as possible on each side until we don't fit any more.
4632N/A // but if all we did was change selection then we need to try to keep the same
4632N/A // tabs on screen so we don't get a jarring tab moving out from under the mouse
4632N/A // effect.
4632N/A
4632N/A final boolean sizeChanged = returnAt != lastReturnAt;
4632N/A // so if we stay the same, make right the first tab and say left done = true
4632N/A if (pane.popupSelectionChanged || sizeChanged) {
4632N/A pane.popupSelectionChanged = false;
4632N/A lastLeftmostTab = -1;
4632N/A }
4632N/A
4632N/A int right = selectedIndex;
4632N/A int left = selectedIndex - 1;
4632N/A
4632N/A // if we had a good last leftmost tab then we set left to unused and
4632N/A // start at that tab.
4632N/A if (lastLeftmostTab >= 0) {
4632N/A right = lastLeftmostTab;
4632N/A left = -1;
4632N/A } else if (selectedIndex < 0) {
4632N/A // this is if there is none selected see radar 3138137
4632N/A right = 0;
4632N/A left = -1;
4632N/A }
4632N/A
4632N/A int remainingSpace = returnAt - pane.tabAreaInsets.right - pane.tabAreaInsets.left - FIXED_SCROLL_TAB_LENGTH * 2;
4632N/A int visibleCount = 0;
4632N/A
4632N/A final Rectangle firstRect = rects[right];
4632N/A if ((verticalTabRuns ? firstRect.height : firstRect.width) > remainingSpace) {
4632N/A // always show at least the selected one!
4632N/A addToEnd(right, visibleCount);
4632N/A if (verticalTabRuns) {
4632N/A firstRect.height = remainingSpace; // force it to fit!
4632N/A } else {
4632N/A firstRect.width = remainingSpace; // force it to fit!
4632N/A }
4632N/A visibleCount++;
4632N/A } else {
4632N/A boolean rightDone = false;
4632N/A boolean leftDone = false;
4632N/A
4632N/A // at least one if not more will fit
4632N/A while ((visibleCount < tabCount) && !(rightDone && leftDone)) {
4632N/A if (!rightDone && right >= 0 && right < tabCount) {
4632N/A final Rectangle rightRect = rects[right];
4632N/A if ((verticalTabRuns ? rightRect.height : rightRect.width) > remainingSpace) {
4632N/A rightDone = true;
4632N/A } else {
4632N/A addToEnd(right, visibleCount);
4632N/A visibleCount++;
4632N/A remainingSpace -= (verticalTabRuns ? rightRect.height : rightRect.width);
4632N/A right++;
4632N/A continue; // this gives a bias to "paging forward", and "inching backward"
4632N/A }
4632N/A } else {
4632N/A rightDone = true;
4632N/A }
4632N/A
4632N/A if (!leftDone && left >= 0 && left < tabCount) {
4632N/A final Rectangle leftRect = rects[left];
4632N/A if ((verticalTabRuns ? leftRect.height : leftRect.width) > remainingSpace) {
4632N/A leftDone = true;
4632N/A } else {
4632N/A addToBeginning(left, visibleCount);
4632N/A visibleCount++;
4632N/A remainingSpace -= (verticalTabRuns ? leftRect.height : leftRect.width);
4632N/A left--;
4632N/A }
4632N/A } else {
4632N/A leftDone = true;
4632N/A }
4632N/A }
4632N/A }
4632N/A
4632N/A if (visibleCount > visibleTabList.length) visibleCount = visibleTabList.length;
4632N/A
4632N/A hasMoreLeftTabs = visibleTabList[0] > 0;
4632N/A hasMoreRightTabs = visibleTabList[visibleCount - 1] < visibleTabList.length - 1;
4632N/A
4632N/A numberOfVisibleTabs = visibleCount;
4632N/A // add the scroll tab at the end;
4632N/A lastLeftmostTab = getIndex(0);
4632N/A lastReturnAt = returnAt;
4632N/A
4632N/A final int firstTabIndex = getIndex(0);
4632N/A final int lastTabIndex = getIndex(visibleCount - 1);
4632N/A
4632N/A // move all "invisible" tabs beyond the edge of known space...
4632N/A for (int i = 0; i < tabCount; i++) {
4632N/A if (i < firstTabIndex || i > lastTabIndex) {
4632N/A final Rectangle rect = rects[i];
4632N/A rect.x = Short.MAX_VALUE;
4632N/A rect.y = Short.MAX_VALUE;
4632N/A }
4632N/A }
4632N/A }
4632N/A
4632N/A protected void alignRectsRunFor(final Rectangle[] rects, final Dimension tabPaneSize, final int tabPlacement, final boolean isRightToLeft) {
4632N/A final boolean isVertical = tabPlacement == SwingConstants.LEFT || tabPlacement == SwingConstants.RIGHT;
4632N/A
4632N/A if (isVertical) {
4632N/A if (needsScrollers) {
4632N/A stretchScrollingVerticalRun(rects, tabPaneSize);
4632N/A } else {
4632N/A centerVerticalRun(rects, tabPaneSize);
4632N/A }
4632N/A } else {
4632N/A if (needsScrollers) {
4632N/A stretchScrollingHorizontalRun(rects, tabPaneSize, isRightToLeft);
4632N/A } else {
4632N/A centerHorizontalRun(rects, tabPaneSize, isRightToLeft);
4632N/A }
4632N/A }
4632N/A }
4632N/A
4632N/A private void centerHorizontalRun(final Rectangle[] rects, final Dimension size, final boolean isRightToLeft) {
4632N/A int totalLength = 0;
4632N/A for (final Rectangle element : rects) {
4632N/A totalLength += element.width;
4632N/A }
4632N/A
4632N/A int x = size.width / 2 - totalLength / 2;
4632N/A
4632N/A if (isRightToLeft) {
4632N/A for (final Rectangle rect : rects) {
4632N/A rect.x = x;
4632N/A x += rect.width;
4632N/A }
4632N/A } else {
4632N/A for (int i = rects.length - 1; i >= 0; i--) {
4632N/A final Rectangle rect = rects[i];
4632N/A rect.x = x;
4632N/A x += rect.width;
4632N/A }
4632N/A }
4632N/A }
4632N/A
4632N/A private void centerVerticalRun(final Rectangle[] rects, final Dimension size) {
4632N/A int totalLength = 0;
4632N/A for (final Rectangle element : rects) {
4632N/A totalLength += element.height;
4632N/A }
4632N/A
4632N/A int y = size.height / 2 - totalLength / 2;
4632N/A
4632N/A if (true) {
4632N/A for (final Rectangle rect : rects) {
4632N/A rect.y = y;
4632N/A y += rect.height;
4632N/A }
4632N/A } else {
4632N/A for (int i = rects.length - 1; i >= 0; i--) {
4632N/A final Rectangle rect = rects[i];
4632N/A rect.y = y;
4632N/A y += rect.height;
4632N/A }
4632N/A }
4632N/A }
4632N/A
4632N/A private void stretchScrollingHorizontalRun(final Rectangle[] rects, final Dimension size, final boolean isRightToLeft) {
4632N/A final int totalTabs = getTotal();
4632N/A final int firstTabIndex = getIndex(0);
4632N/A final int lastTabIndex = getIndex(totalTabs - 1);
4632N/A
4632N/A int totalRunLength = 0;
4632N/A for (int i = firstTabIndex; i <= lastTabIndex; i++) {
4632N/A totalRunLength += rects[i].width;
4632N/A }
4632N/A
4632N/A int slack = size.width - totalRunLength - pane.tabAreaInsets.left - pane.tabAreaInsets.right;
4632N/A if (needsLeftScrollTab()) {
4632N/A slack -= FIXED_SCROLL_TAB_LENGTH;
4632N/A }
4632N/A if (needsRightScrollTab()) {
4632N/A slack -= FIXED_SCROLL_TAB_LENGTH;
4632N/A }
4632N/A
4632N/A final int minSlack = (int)((float)(slack) / (float)(totalTabs));
4632N/A int extraSlack = slack - (minSlack * totalTabs);
4632N/A int runningLength = 0;
4632N/A final int xOffset = pane.tabAreaInsets.left + (needsLeftScrollTab() ? FIXED_SCROLL_TAB_LENGTH : 0);
4632N/A
4632N/A if (isRightToLeft) {
4632N/A for (int i = firstTabIndex; i <= lastTabIndex; i++) {
4632N/A final Rectangle rect = rects[i];
4632N/A int slackToAdd = minSlack;
4632N/A if (extraSlack > 0) {
4632N/A slackToAdd++;
4632N/A extraSlack--;
4632N/A }
4632N/A rect.x = runningLength + xOffset;
4632N/A rect.width += slackToAdd;
4632N/A runningLength += rect.width;
4632N/A }
4632N/A } else {
4632N/A for (int i = lastTabIndex; i >= firstTabIndex; i--) {
4632N/A final Rectangle rect = rects[i];
4632N/A int slackToAdd = minSlack;
4632N/A if (extraSlack > 0) {
4632N/A slackToAdd++;
4632N/A extraSlack--;
4632N/A }
4632N/A rect.x = runningLength + xOffset;
4632N/A rect.width += slackToAdd;
4632N/A runningLength += rect.width;
4632N/A }
4632N/A }
4632N/A
4632N/A if (isRightToLeft) {
4632N/A leftScrollTabRect.x = pane.tabAreaInsets.left;
4632N/A leftScrollTabRect.y = rects[firstTabIndex].y;
4632N/A leftScrollTabRect.height = rects[firstTabIndex].height;
4632N/A
4632N/A rightScrollTabRect.x = size.width - pane.tabAreaInsets.right - rightScrollTabRect.width;
4632N/A rightScrollTabRect.y = rects[lastTabIndex].y;
4632N/A rightScrollTabRect.height = rects[lastTabIndex].height;
4632N/A } else {
4632N/A rightScrollTabRect.x = pane.tabAreaInsets.left;
4632N/A rightScrollTabRect.y = rects[firstTabIndex].y;
4632N/A rightScrollTabRect.height = rects[firstTabIndex].height;
4632N/A
4632N/A leftScrollTabRect.x = size.width - pane.tabAreaInsets.right - rightScrollTabRect.width;
4632N/A leftScrollTabRect.y = rects[lastTabIndex].y;
4632N/A leftScrollTabRect.height = rects[lastTabIndex].height;
4632N/A
4632N/A if (needsLeftScrollTab()) {
4632N/A for (int i = lastTabIndex; i >= firstTabIndex; i--) {
4632N/A final Rectangle rect = rects[i];
4632N/A rect.x -= FIXED_SCROLL_TAB_LENGTH;
4632N/A }
4632N/A }
4632N/A
4632N/A if (needsRightScrollTab()) {
4632N/A for (int i = lastTabIndex; i >= firstTabIndex; i--) {
4632N/A final Rectangle rect = rects[i];
4632N/A rect.x += FIXED_SCROLL_TAB_LENGTH;
4632N/A }
4632N/A }
4632N/A }
4632N/A }
4632N/A
4632N/A private void stretchScrollingVerticalRun(final Rectangle[] rects, final Dimension size) {
4632N/A final int totalTabs = getTotal();
4632N/A final int firstTabIndex = getIndex(0);
4632N/A final int lastTabIndex = getIndex(totalTabs - 1);
4632N/A
4632N/A int totalRunLength = 0;
4632N/A for (int i = firstTabIndex; i <= lastTabIndex; i++) {
4632N/A totalRunLength += rects[i].height;
4632N/A }
4632N/A
4632N/A int slack = size.height - totalRunLength - pane.tabAreaInsets.top - pane.tabAreaInsets.bottom;
4632N/A if (needsLeftScrollTab()) {
4632N/A slack -= FIXED_SCROLL_TAB_LENGTH;
4632N/A }
4632N/A if (needsRightScrollTab()) {
4632N/A slack -= FIXED_SCROLL_TAB_LENGTH;
4632N/A }
4632N/A
4632N/A final int minSlack = (int)((float)(slack) / (float)(totalTabs));
4632N/A int extraSlack = slack - (minSlack * totalTabs);
4632N/A int runningLength = 0;
4632N/A final int yOffset = pane.tabAreaInsets.top + (needsLeftScrollTab() ? FIXED_SCROLL_TAB_LENGTH : 0);
4632N/A
4632N/A for (int i = firstTabIndex; i <= lastTabIndex; i++) {
4632N/A final Rectangle rect = rects[i];
4632N/A int slackToAdd = minSlack;
4632N/A if (extraSlack > 0) {
4632N/A slackToAdd++;
4632N/A extraSlack--;
4632N/A }
4632N/A rect.y = runningLength + yOffset;
4632N/A rect.height += slackToAdd;
4632N/A runningLength += rect.height;
4632N/A }
4632N/A
4632N/A leftScrollTabRect.x = rects[firstTabIndex].x;
4632N/A leftScrollTabRect.y = pane.tabAreaInsets.top;
4632N/A leftScrollTabRect.width = rects[firstTabIndex].width;
4632N/A
4632N/A rightScrollTabRect.x = rects[lastTabIndex].x;
4632N/A rightScrollTabRect.y = size.height - pane.tabAreaInsets.bottom - rightScrollTabRect.height;
4632N/A rightScrollTabRect.width = rects[lastTabIndex].width;
4632N/A }
4632N/A}