0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: /**
0019: * @author Vadim L. Bogdanov
0020: * @version $Revision$
0021: */package javax.swing.plaf.basic;
0022:
0023: import java.awt.Color;
0024: import java.awt.Component;
0025: import java.awt.Container;
0026: import java.awt.Dimension;
0027: import java.awt.Font;
0028: import java.awt.FontMetrics;
0029: import java.awt.Graphics;
0030: import java.awt.Insets;
0031: import java.awt.KeyboardFocusManager;
0032: import java.awt.LayoutManager;
0033: import java.awt.Point;
0034: import java.awt.Rectangle;
0035: import java.awt.Shape;
0036:
0037: import java.awt.event.ActionEvent;
0038: import java.awt.event.ActionListener;
0039: import java.awt.event.FocusAdapter;
0040: import java.awt.event.FocusEvent;
0041: import java.awt.event.FocusListener;
0042: import java.awt.event.MouseAdapter;
0043: import java.awt.event.MouseEvent;
0044: import java.awt.event.MouseListener;
0045: import java.awt.event.MouseMotionAdapter;
0046:
0047: import java.beans.PropertyChangeEvent;
0048: import java.beans.PropertyChangeListener;
0049:
0050: import javax.swing.AbstractAction;
0051: import javax.swing.ActionMap;
0052: import javax.swing.Icon;
0053: import javax.swing.InputMap;
0054: import javax.swing.JButton;
0055: import javax.swing.JComponent;
0056: import javax.swing.JTabbedPane;
0057: import javax.swing.KeyStroke;
0058: import javax.swing.LookAndFeel;
0059: import javax.swing.SwingConstants;
0060: import javax.swing.SwingUtilities;
0061: import javax.swing.UIManager;
0062:
0063: import javax.swing.event.ChangeEvent;
0064: import javax.swing.event.ChangeListener;
0065:
0066: import javax.swing.plaf.ActionMapUIResource;
0067: import javax.swing.plaf.ComponentUI;
0068: import javax.swing.plaf.TabbedPaneUI;
0069: import javax.swing.plaf.UIResource;
0070:
0071: import javax.swing.text.View;
0072:
0073: import org.apache.harmony.x.swing.ButtonCommons;
0074: import org.apache.harmony.x.swing.StringConstants;
0075: import org.apache.harmony.x.swing.Utilities;
0076:
0077: public class BasicTabbedPaneUI extends TabbedPaneUI implements
0078: SwingConstants {
0079:
0080: public class FocusHandler extends FocusAdapter {
0081: public void focusGained(final FocusEvent e) {
0082: tabPane.repaint();
0083: }
0084:
0085: public void focusLost(final FocusEvent e) {
0086: tabPane.repaint();
0087: }
0088: }
0089:
0090: public class MouseHandler extends MouseAdapter {
0091: public void mousePressed(final MouseEvent e) {
0092: int index = calculateTabIndexByMouseEvent(e);
0093:
0094: if (index != -1 && tabPane.isEnabledAt(index)) {
0095: // tab is clicked
0096: if (!isSelectedTab(index)) {
0097: boolean visibleComponentFocused = getFocusIndex() == -1;
0098: tabPane.setSelectedIndex(index);
0099: if (visibleComponentFocused
0100: && getVisibleComponent() != null) {
0101: getVisibleComponent().requestFocus();
0102: }
0103: } else {
0104: tabPane.requestFocus();
0105: }
0106: }
0107: }
0108: }
0109:
0110: private class MouseMotionHandler extends MouseMotionAdapter {
0111: public void mouseMoved(final MouseEvent e) {
0112: setRolloverTab(calculateTabIndexByMouseEvent(e));
0113: }
0114: }
0115:
0116: public class PropertyChangeHandler implements
0117: PropertyChangeListener {
0118: public void propertyChange(final PropertyChangeEvent e) {
0119: if ("tabPlacement".equals(e.getPropertyName())) {
0120: if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
0121: installScrollableTabsComponents();
0122: }
0123: tabPane.revalidate();
0124: tabPane.repaint();
0125: } else if ("tabLayoutPolicy".equals(e.getPropertyName())) {
0126: if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
0127: installScrollableTabsComponents();
0128: } else {
0129: uninstallScrollableTabsComponents();
0130: }
0131: tabPane.setLayout(createLayoutManager());
0132: tabPane.revalidate();
0133: tabPane.repaint();
0134: }
0135: }
0136: }
0137:
0138: public class TabbedPaneLayout implements LayoutManager {
0139: public void addLayoutComponent(final String name,
0140: final Component comp) {
0141: if (!(comp instanceof UIResource)) {
0142: calculateLayoutInfo();
0143: }
0144: }
0145:
0146: public void calculateLayoutInfo() {
0147: if (tabPane == null) {
0148: return;
0149: }
0150: int tabCount = tabPane.getTabCount();
0151: final Component selectedComponent = tabPane
0152: .getSelectedComponent();
0153: if (selectedComponent != null) {
0154: setVisibleComponent(selectedComponent);
0155: }
0156:
0157: assureRectsCreated(tabCount);
0158: calculateTabRects(tabPane.getTabPlacement(), tabCount);
0159: }
0160:
0161: /**
0162: * Implements both minimum and preferred size calculations.
0163: */
0164: protected Dimension calculateSize(final boolean minimum) {
0165: int tabPlacement = tabPane.getTabPlacement();
0166: Dimension size = calculateTabAreaSize(tabPlacement);
0167:
0168: Dimension contentAreaSize = calculateContentAreaSize(minimum);
0169: Utilities.addInsets(contentAreaSize,
0170: getContentBorderInsets(tabPlacement));
0171:
0172: if (isVerticalRun(tabPlacement)) {
0173: size.width += contentAreaSize.width;
0174: size.height = Math.max(size.height,
0175: contentAreaSize.height);
0176: } else {
0177: size.height += contentAreaSize.height;
0178: size.width = Math
0179: .max(size.width, contentAreaSize.width);
0180: }
0181:
0182: Utilities.addInsets(size, tabPane.getInsets());
0183: return size;
0184: }
0185:
0186: private Dimension calculateTabAreaSize(final int tabPlacement) {
0187: Dimension size = new Dimension();
0188: Insets insets = getTabAreaInsets(tabPlacement);
0189:
0190: if (isVerticalRun(tabPlacement)) {
0191: size.height = calculateMaxTabHeight(tabPlacement);
0192: size.height += insets.top + insets.bottom;
0193: size.width = preferredTabAreaWidth(tabPlacement,
0194: size.height);
0195: } else {
0196: size.width = calculateMaxTabWidth(tabPlacement);
0197: size.width += insets.left + insets.right;
0198: size.height = preferredTabAreaHeight(tabPlacement,
0199: size.width);
0200: }
0201:
0202: return size;
0203: }
0204:
0205: private Dimension calculateContentAreaSize(final boolean minimum) {
0206: Dimension contentAreaSize = new Dimension();
0207:
0208: for (int i = 0; i < tabPane.getComponentCount(); i++) {
0209: Dimension size;
0210: if (minimum) {
0211: size = tabPane.getComponentAt(i).getMinimumSize();
0212: } else {
0213: size = tabPane.getComponentAt(i).getPreferredSize();
0214: }
0215: if (size.width > contentAreaSize.width) {
0216: contentAreaSize.width = size.width;
0217: }
0218: if (size.height > contentAreaSize.height) {
0219: contentAreaSize.height = size.height;
0220: }
0221: }
0222:
0223: return contentAreaSize;
0224: }
0225:
0226: private void getNewTabRunOffsets(final int tabPlacement,
0227: final int runCount, final Point initialOffset,
0228: final Point newTabRunOffset) {
0229: newTabRunOffset.setLocation(initialOffset);
0230: int tabRunIndent = getTabRunIndent(tabPlacement, runCount);
0231: if (isVerticalRun(tabPlacement)) {
0232: newTabRunOffset.y += tabRunIndent;
0233: } else {
0234: newTabRunOffset.x += tabRunIndent;
0235: }
0236: }
0237:
0238: void calculateAvailableRectangleToPlaceTabs(
0239: final int tabPlacement, final Rectangle tabsRect,
0240: final Rectangle tabAreaInnerBounds) {
0241: SwingUtilities.calculateInnerArea(tabPane, tabsRect);
0242: Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
0243: calculateInnerArea(tabsRect, tabAreaInsets);
0244: tabAreaInnerBounds.setBounds(tabsRect);
0245: }
0246:
0247: protected void calculateTabRects(final int tabPlacement,
0248: final int tabCount) {
0249: if (tabCount == 0) {
0250: return;
0251: }
0252:
0253: runCount = 1;
0254: tabRuns[0] = 0;
0255:
0256: FontMetrics fm = getFontMetrics();
0257: maxTabHeight = calculateMaxTabHeight(tabPlacement);
0258: maxTabWidth = calculateMaxTabWidth(tabPlacement);
0259:
0260: calculateAvailableRectangleToPlaceTabs(tabPlacement,
0261: calcRect, tabAreaInnerBounds);
0262:
0263: // split tabs into runs
0264: Point initialOffset = calcRect.getLocation();
0265: Point currentOffset = new Point();
0266: getNewTabRunOffsets(tabPlacement, runCount, initialOffset,
0267: currentOffset);
0268: boolean isVerticalRun = isVerticalRun(tabPlacement);
0269: boolean justStartedNewRun = true; // not very good to use this flag
0270: for (int i = 0; i < tabCount; i++) {
0271: int tabWidth = calculateTabWidth(tabPlacement, i, fm);
0272: int tabHeight = calculateTabHeight(tabPlacement, i, fm
0273: .getHeight());
0274: if (isVerticalRun) {
0275: rects[i].setSize(maxTabWidth, tabHeight);
0276: } else {
0277: rects[i].setSize(tabWidth, maxTabHeight);
0278: }
0279: rects[i].setLocation(currentOffset);
0280: if (!calcRect.contains(rects[i]) && !justStartedNewRun) {
0281: // start a new tab run
0282: getNewTabRunOffsets(tabPlacement, runCount,
0283: initialOffset, currentOffset);
0284: if (runCount >= tabRuns.length) {
0285: expandTabRunsArray();
0286: }
0287: tabRuns[runCount] = i;
0288: runCount++;
0289: }
0290: rects[i].setLocation(currentOffset);
0291: if (isVerticalRun) {
0292: currentOffset.y += tabHeight;
0293: } else {
0294: currentOffset.x += tabWidth;
0295: }
0296: justStartedNewRun = false;
0297: }
0298: int start = isVerticalRun ? tabAreaInnerBounds.y
0299: : tabAreaInnerBounds.x;
0300: int length = isVerticalRun ? tabAreaInnerBounds.height
0301: : tabAreaInnerBounds.width;
0302: normalizeTabRuns(tabPlacement, tabCount, start, start
0303: + length);
0304:
0305: selectedRun = getRunForTab(tabCount, tabPane
0306: .getSelectedIndex());
0307: if (shouldRotateTabRuns(tabPlacement)) {
0308: rotateTabRuns(tabPlacement, selectedRun);
0309: }
0310:
0311: // calculate remaining coordinate (x or y depending on tabPlacement)
0312: for (int i = 0; i < tabCount; i++) {
0313: int sign = 1;
0314: if (tabPlacement == JTabbedPane.BOTTOM) {
0315: sign = -1;
0316: rects[i].y += tabAreaInnerBounds.height
0317: - maxTabHeight;
0318: } else if (tabPlacement == JTabbedPane.RIGHT) {
0319: sign = -1;
0320: rects[i].x += tabAreaInnerBounds.width
0321: - maxTabWidth;
0322: }
0323: if (isVerticalRun) {
0324: rects[i].x += sign
0325: * (runCount - getRunForTab(tabCount, i) - 1)
0326: * (maxTabWidth - getTabRunOverlay(tabPlacement));
0327: } else {
0328: rects[i].y += sign
0329: * (runCount - getRunForTab(tabCount, i) - 1)
0330: * (maxTabHeight - getTabRunOverlay(tabPlacement));
0331: }
0332: }
0333:
0334: // pad tab runs
0335: int lastTabInRun = -1;
0336: int curRun = getRunForTab(tabCount, 0);
0337: for (int i = 0; i < runCount; i++) {
0338: int firstTabInRun = getNextElementInRing(lastTabInRun,
0339: tabCount);
0340: lastTabInRun = lastTabInRun(tabCount, curRun);
0341: if (shouldPadTabRun(tabPlacement, curRun)) {
0342: int max = isVerticalRun ? calcRect.height
0343: : calcRect.width;
0344: padTabRun(tabPlacement, firstTabInRun,
0345: lastTabInRun, max);
0346: }
0347: curRun = getNextTabRun(curRun);
0348: }
0349:
0350: padSelectedTab(tabPlacement, tabPane.getSelectedIndex());
0351: }
0352:
0353: public void layoutContainer(final Container parent) {
0354: calculateLayoutInfo();
0355:
0356: int tabPlacement = tabPane.getTabPlacement();
0357:
0358: int tabAreaSize;
0359: if (isVerticalRun(tabPlacement)) {
0360: tabAreaSize = calculateTabAreaWidth(tabPlacement,
0361: runCount, maxTabWidth);
0362: } else {
0363: tabAreaSize = calculateTabAreaHeight(tabPlacement,
0364: runCount, maxTabHeight);
0365: }
0366:
0367: Rectangle rect = SwingUtilities.calculateInnerArea(tabPane,
0368: null);
0369: if (tabPlacement == JTabbedPane.TOP) {
0370: rect.y += tabAreaSize;
0371: rect.height -= tabAreaSize;
0372: } else if (tabPlacement == JTabbedPane.BOTTOM) {
0373: rect.height -= tabAreaSize;
0374: } else if (tabPlacement == JTabbedPane.LEFT) {
0375: rect.x += tabAreaSize;
0376: rect.width -= tabAreaSize;
0377: } else if (tabPlacement == JTabbedPane.RIGHT) {
0378: rect.width -= tabAreaSize;
0379: }
0380: contentAreaBounds.setBounds(rect);
0381:
0382: calculateInnerArea(rect,
0383: getContentBorderInsets(tabPlacement));
0384:
0385: if (getVisibleComponent() != null) {
0386: getVisibleComponent().setBounds(rect);
0387: }
0388:
0389: calculateTabAreaClipRect(tabPlacement);
0390: }
0391:
0392: public Dimension minimumLayoutSize(final Container parent) {
0393: return calculateSize(true);
0394: }
0395:
0396: /**
0397: * This function prevents from appearing of runs with
0398: * small number of tabs. If it is necessary, tabs are
0399: * moved from the next to last tab run to the last tab run.
0400: */
0401: protected void normalizeTabRuns(final int tabPlacement,
0402: final int tabCount, final int start, final int max) {
0403: if (runCount < 2) {
0404: return;
0405: }
0406:
0407: final float NORMALIZE_THRESHOLD = 5f / 8f;
0408:
0409: // find out the last tab in the next to last tab run
0410: boolean isVerticalRun = isVerticalRun(tabPlacement);
0411: int lastTabPos = isVerticalRun ? (int) rects[tabCount - 1]
0412: .getMaxY() : (int) rects[tabCount - 1].getMaxX();
0413: int desiredPos = start
0414: + (int) ((max - start) * NORMALIZE_THRESHOLD);
0415: int lastTab = lastTabInRun(tabCount, runCount - 2);
0416: int firstTab = firstTabInRun(tabCount, runCount - 2);
0417: while (lastTab - firstTab >= 2 && lastTabPos < desiredPos) {
0418: lastTabPos += isVerticalRun ? rects[lastTab].height
0419: : rects[lastTab].width;
0420: lastTab--;
0421: }
0422:
0423: lastTab = getNextTabIndex(lastTab);
0424:
0425: if (tabRuns[runCount - 1] == lastTab) {
0426: return; // no tabs were moved
0427: }
0428:
0429: tabRuns[runCount - 1] = lastTab;
0430:
0431: // correct bounds of tabs in the last tab run
0432: lastTabPos = start;
0433: for (; lastTab < tabCount; lastTab++) {
0434: if (isVerticalRun) {
0435: rects[lastTab].y = lastTabPos;
0436: lastTabPos += rects[lastTab].height;
0437: } else {
0438: rects[lastTab].x = lastTabPos;
0439: lastTabPos += rects[lastTab].width;
0440: }
0441: }
0442: }
0443:
0444: /**
0445: * Increases size of the selected tab using selected tab pad insets.
0446: */
0447: protected void padSelectedTab(final int tabPlacement,
0448: final int selectedIndex) {
0449: if (selectedIndex == -1) {
0450: return;
0451: }
0452:
0453: Insets insets = getSelectedTabPadInsets(tabPlacement);
0454: Rectangle rect = rects[selectedIndex];
0455:
0456: rect.translate(-insets.left, -insets.top);
0457: rect.width += insets.left + insets.right;
0458: rect.height += insets.top + insets.bottom;
0459: }
0460:
0461: /**
0462: * Increases size of tabs in the given tab run to make width or height
0463: * of the tab run (depending on <code>tabPlacement</code>) equal
0464: * to <code>max</code>.
0465: *
0466: * @param tabPlacement the tab placement
0467: * @param start index of the first tab in the tab run
0468: * @param end index of the last tab in the tab run
0469: * @param max the desired width or height of the tab run
0470: */
0471: protected void padTabRun(final int tabPlacement,
0472: final int start, final int end, final int max) {
0473: int size = 0;
0474: for (int i = start; i <= end; i++) {
0475: size += isVerticalRun(tabPlacement) ? rects[i].height
0476: : rects[i].width;
0477: }
0478: int increment = (max - size) / (end - start + 1);
0479: int additionalIncrementToLast = (max - size)
0480: % (end - start + 1);
0481:
0482: for (int i = 0; i <= end - start; i++) {
0483: if (isVerticalRun(tabPlacement)) {
0484: rects[start + i].height += increment;
0485: rects[start + i].y += i * increment;
0486: } else {
0487: rects[start + i].width += increment;
0488: rects[start + i].x += i * increment;
0489: }
0490: }
0491: if (isVerticalRun(tabPlacement)) {
0492: rects[end].height += additionalIncrementToLast;
0493: } else {
0494: rects[end].width += additionalIncrementToLast;
0495: }
0496: }
0497:
0498: public Dimension preferredLayoutSize(final Container parent) {
0499: return calculateSize(false);
0500: }
0501:
0502: protected int preferredTabAreaHeight(final int tabPlacement,
0503: final int width) {
0504: int horizRunCount = isVerticalRun(tabPlacement) ? 0
0505: : getTabRunCount(tabPane);
0506: int height = calculateTabAreaHeight(tabPlacement,
0507: horizRunCount, calculateMaxTabHeight(tabPlacement));
0508: return height;
0509: }
0510:
0511: protected int preferredTabAreaWidth(final int tabPlacement,
0512: final int height) {
0513: int vertRunCount = isVerticalRun(tabPlacement) ? getTabRunCount(tabPane)
0514: : 0;
0515: int width = calculateTabAreaWidth(tabPlacement,
0516: vertRunCount, calculateMaxTabWidth(tabPlacement));
0517: return width;
0518: }
0519:
0520: public void removeLayoutComponent(final Component comp) {
0521: if (!(comp instanceof UIResource)) {
0522: calculateLayoutInfo();
0523: }
0524: }
0525:
0526: protected void rotateTabRuns(final int tabPlacement,
0527: final int selectedRun) {
0528: // rotate tabRuns to move selectedRun to the last position
0529: int[] temp = new int[selectedRun];
0530: System.arraycopy(tabRuns, 0, temp, 0, selectedRun);
0531: System.arraycopy(tabRuns, selectedRun, tabRuns, 0, runCount
0532: - selectedRun);
0533: System.arraycopy(temp, 0, tabRuns, runCount - selectedRun,
0534: selectedRun);
0535: }
0536:
0537: void calculateTabAreaClipRect(final int tabPlacement) {
0538: SwingUtilities.calculateInnerArea(tabPane, tabAreaClipRect);
0539: if (tabPlacement == TOP) {
0540: tabAreaClipRect.height = contentAreaBounds.y
0541: - tabAreaClipRect.y;
0542: } else if (tabPlacement == LEFT) {
0543: tabAreaClipRect.width = contentAreaBounds.x
0544: - tabAreaClipRect.x;
0545: } else if (tabPlacement == BOTTOM) {
0546: tabAreaClipRect.y = (int) contentAreaBounds.getMaxY();
0547: tabAreaClipRect.height -= tabAreaClipRect.y;
0548: } else if (tabPlacement == RIGHT) {
0549: tabAreaClipRect.x = (int) contentAreaBounds.getMaxX();
0550: tabAreaClipRect.width -= tabAreaClipRect.x;
0551: }
0552: }
0553: }
0554:
0555: private class ScrollableTabLayout extends TabbedPaneLayout {
0556: void calculateAvailableRectangleToPlaceTabs(
0557: final int tabPlacement, final Rectangle tabsRect,
0558: final Rectangle tabAreaInnerBounds) {
0559: super .calculateAvailableRectangleToPlaceTabs(tabPlacement,
0560: tabsRect, tabAreaInnerBounds);
0561: tabsRect.setSize(Short.MAX_VALUE, Short.MAX_VALUE);
0562:
0563: if (isVerticalRun(tabPlacement)) {
0564: tabAreaInnerBounds.height -= leftScrollButton
0565: .getHeight()
0566: + rightScrollButton.getHeight();
0567: } else {
0568: tabAreaInnerBounds.width -= leftScrollButton.getWidth()
0569: + rightScrollButton.getWidth();
0570: }
0571: }
0572:
0573: public void layoutContainer(final Container parent) {
0574: super .layoutContainer(parent);
0575: updateScrollButtons();
0576: layoutScrollButtons();
0577: }
0578:
0579: protected void padSelectedTab(final int tabPlacement,
0580: final int selectedIndex) {
0581: // overridden to do nothing
0582: }
0583:
0584: protected void calculateTabRects(final int tabPlacement,
0585: final int tabCount) {
0586: super .calculateTabRects(tabPlacement, tabCount);
0587: }
0588:
0589: private void layoutScrollButtons() {
0590: if (!leftScrollButton.isVisible()) {
0591: return;
0592: }
0593:
0594: rightScrollButton.getBounds(calcRect);
0595:
0596: int tabPlacement = tabPane.getTabPlacement();
0597: Rectangle c = contentAreaBounds;
0598: Rectangle b = rightScrollButton.getBounds();
0599:
0600: if (tabPlacement == TOP) {
0601: b.setLocation(c.x + c.width - b.width, c.y - b.height);
0602: rightScrollButton.setBounds(b);
0603: b.translate(-b.width, 0);
0604: leftScrollButton.setBounds(b);
0605: } else if (tabPlacement == BOTTOM) {
0606: b.setLocation(c.x + c.width - b.width, c.y + c.height);
0607: rightScrollButton.setBounds(b);
0608: b.translate(-b.width, 0);
0609: leftScrollButton.setBounds(b);
0610: } else if (tabPlacement == LEFT) {
0611: b.setLocation(c.x - b.width, c.y + c.height - b.height);
0612: rightScrollButton.setBounds(b);
0613: b.translate(0, -b.height);
0614: leftScrollButton.setBounds(b);
0615: } else if (tabPlacement == RIGHT) {
0616: b.setLocation(c.x + c.width, c.y + c.height - b.height);
0617: rightScrollButton.setBounds(b);
0618: b.translate(0, -b.height);
0619: leftScrollButton.setBounds(b);
0620: }
0621: }
0622:
0623: void calculateTabAreaClipRect(final int tabPlacement) {
0624: super .calculateTabAreaClipRect(tabPlacement);
0625: if (isVerticalRun(tabPlacement)) {
0626: tabAreaClipRect.y = tabAreaInnerBounds.y;
0627: tabAreaClipRect.height = tabAreaInnerBounds.height;
0628: } else {
0629: tabAreaClipRect.x = tabAreaInnerBounds.x;
0630: tabAreaClipRect.width = tabAreaInnerBounds.width;
0631: }
0632: }
0633: }
0634:
0635: private class ScrollButton extends BasicArrowButton implements
0636: UIResource, ActionListener {
0637:
0638: public ScrollButton(final int direction) {
0639: super (direction);
0640:
0641: setSize(16, 16);
0642: setFocusable(false);
0643: addActionListener(this );
0644: }
0645:
0646: public void actionPerformed(final ActionEvent e) {
0647: if (direction == EAST || direction == SOUTH) {
0648: scrollToShowTab(true);
0649: } else if (direction == WEST || direction == NORTH) {
0650: scrollToShowTab(false);
0651: }
0652: tabPane.repaint();
0653: }
0654: }
0655:
0656: public class TabSelectionHandler implements ChangeListener {
0657: public void stateChanged(final ChangeEvent e) {
0658: // we have to relayout immediatelly; in other case we can get
0659: // in listeners/actions inconsistent state (ex.: NavigateAction)
0660: tabPane.doLayout();
0661:
0662: scrollToShowTab(tabPane.getSelectedIndex());
0663: tabPane.revalidate();
0664: tabPane.repaint();
0665: }
0666: }
0667:
0668: private class NavigateAction extends AbstractAction {
0669: private int direction;
0670:
0671: public NavigateAction(final int direction) {
0672: this .direction = direction;
0673: }
0674:
0675: public void actionPerformed(final ActionEvent e) {
0676: boolean visibleComponentFocused = getFocusIndex() == -1;
0677: navigateSelectedTab(direction);
0678: if (visibleComponentFocused
0679: && getVisibleComponent() != null) {
0680: getVisibleComponent().requestFocus();
0681: }
0682: }
0683: }
0684:
0685: private static class MnemonicAction extends AbstractAction {
0686: public void actionPerformed(final ActionEvent e) {
0687: JTabbedPane tabPane = (JTabbedPane) e.getSource();
0688:
0689: int keyCode = Utilities.keyCharToKeyCode(e
0690: .getActionCommand().charAt(0));
0691: for (int i = 0; i < tabPane.getTabCount(); i++) {
0692: if (keyCode == tabPane.getMnemonicAt(i)) {
0693: tabPane.setSelectedIndex(i);
0694: break;
0695: }
0696: }
0697: }
0698: }
0699:
0700: public static ComponentUI createUI(final JComponent c) {
0701: return new BasicTabbedPaneUI();
0702: }
0703:
0704: protected static void rotateInsets(final Insets topInsets,
0705: final Insets targetInsets, final int targetPlacement) {
0706: if (targetPlacement == JTabbedPane.TOP) {
0707: targetInsets.set(topInsets.top, topInsets.left,
0708: topInsets.bottom, topInsets.right);
0709: } else if (targetPlacement == JTabbedPane.LEFT) {
0710: targetInsets.set(topInsets.left, topInsets.top,
0711: topInsets.right, topInsets.bottom);
0712: } else if (targetPlacement == JTabbedPane.BOTTOM) {
0713: targetInsets.set(topInsets.bottom, topInsets.left,
0714: topInsets.top, topInsets.right);
0715: } else if (targetPlacement == JTabbedPane.RIGHT) {
0716: targetInsets.set(topInsets.left, topInsets.bottom,
0717: topInsets.right, topInsets.top);
0718: } else {
0719: assert false : "incorrect targetPlacement";
0720: }
0721: }
0722:
0723: private static int TAB_RUNS_ARRAY_SIZE_INCREMENT = 5;
0724: private static AbstractAction MNEMONIC_ACTION = new MnemonicAction();
0725:
0726: protected transient Rectangle calcRect = new Rectangle();
0727:
0728: protected Color highlight;
0729: protected Color lightHighlight;
0730: protected Color shadow;
0731: protected Color darkShadow;
0732: protected Color focus;
0733:
0734: protected int maxTabHeight;
0735: protected int maxTabWidth;
0736:
0737: protected FocusListener focusListener;
0738: protected MouseListener mouseListener;
0739: protected ChangeListener tabChangeListener;
0740: protected PropertyChangeListener propertyChangeListener;
0741:
0742: protected JTabbedPane tabPane;
0743:
0744: protected Rectangle[] rects = { new Rectangle() };
0745: protected int runCount;
0746: protected int selectedRun;
0747:
0748: protected Insets contentBorderInsets;
0749: protected Insets selectedTabPadInsets;
0750: protected Insets tabAreaInsets;
0751: protected Insets tabInsets;
0752: protected int tabRunOverlay;
0753:
0754: private JButton leftScrollButton;
0755: private JButton rightScrollButton;
0756: private Component visibleComponent;
0757:
0758: private ActionMap actionMap;
0759:
0760: private MouseMotionAdapter mouseMotionListener;
0761:
0762: /**
0763: * Tab area's inner area bounds (excluding tab area insets).
0764: * It is calculated by a layout manager.
0765: */
0766: private Rectangle tabAreaInnerBounds = new Rectangle();
0767:
0768: /**
0769: * Tab area's clip region. It is used to prevent from painting on
0770: * content area and on scroll buttons when <code>SCROLL_TAB_LAYOUT</code>
0771: * is used.
0772: */
0773: private Rectangle tabAreaClipRect = new Rectangle();
0774: private int scrollableTabsOffset = 0;
0775:
0776: /**
0777: * Content area bounds including content area insets but excluding
0778: * tabbed pane insets. It is calculated by a layout manager.
0779: */
0780: private Rectangle contentAreaBounds = new Rectangle();
0781:
0782: private Color selectedTabBackground;
0783:
0784: private int rolloverTab = -1;
0785:
0786: protected int[] tabRuns = new int[5];
0787: protected int textIconGap;
0788:
0789: /**
0790: * @deprecated
0791: */
0792: protected KeyStroke leftKey;
0793: /**
0794: * @deprecated
0795: */
0796: protected KeyStroke rightKey;
0797: /**
0798: * @deprecated
0799: */
0800: protected KeyStroke upKey;
0801: /**
0802: * @deprecated
0803: */
0804: protected KeyStroke downKey;
0805:
0806: protected void assureRectsCreated(final int tabCount) {
0807: Rectangle[] oldRects = rects;
0808: if (rects.length < tabCount) {
0809: rects = new Rectangle[tabCount];
0810: }
0811:
0812: System.arraycopy(oldRects, 0, rects, 0, oldRects.length);
0813: for (int i = oldRects.length; i < rects.length; i++) {
0814: rects[i] = new Rectangle();
0815: }
0816: }
0817:
0818: protected int calculateMaxTabHeight(final int tabPlacement) {
0819: int height = 0;
0820: int fontHeight = getFontMetrics().getHeight();
0821: for (int i = 0; i < tabPane.getTabCount(); i++) {
0822: height = Math.max(calculateTabHeight(tabPlacement, i,
0823: fontHeight), height);
0824: }
0825: return height;
0826: }
0827:
0828: protected int calculateMaxTabWidth(final int tabPlacement) {
0829: int width = 0;
0830: FontMetrics fm = getFontMetrics();
0831: for (int i = 0; i < tabPane.getTabCount(); i++) {
0832: width = Math.max(calculateTabWidth(tabPlacement, i, fm),
0833: width);
0834: }
0835: return width;
0836: }
0837:
0838: protected int calculateTabAreaHeight(final int tabPlacement,
0839: final int horizRunCount, final int maxTabHeight) {
0840: int height = maxTabHeight * horizRunCount
0841: - getTabRunOverlay(tabPlacement) * (horizRunCount - 1);
0842: Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
0843: return height + tabAreaInsets.top + tabAreaInsets.bottom;
0844: }
0845:
0846: protected int calculateTabAreaWidth(final int tabPlacement,
0847: final int vertRunCount, final int maxTabWidth) {
0848: int width = maxTabWidth * vertRunCount
0849: - getTabRunOverlay(tabPlacement) * (vertRunCount - 1);
0850: Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
0851: return width + tabAreaInsets.left + tabAreaInsets.right;
0852: }
0853:
0854: protected int calculateTabHeight(final int tabPlacement,
0855: final int tabIndex, final int fontHeight) {
0856: int height = fontHeight;
0857:
0858: Icon icon = getIconForTab(tabIndex);
0859: if (icon != null) {
0860: height = Math.max(height, icon.getIconHeight());
0861: }
0862:
0863: height += Math.abs(getTabLabelShiftY(tabPlacement, tabIndex,
0864: true)
0865: - getTabLabelShiftY(tabPlacement, tabIndex, false));
0866:
0867: // add space to paint a focus indicator and a tab's border
0868: height += 4;
0869:
0870: Insets insets = getTabInsets(tabPlacement, tabIndex);
0871: return height + insets.top + insets.bottom;
0872: }
0873:
0874: protected int calculateTabWidth(final int tabPlacement,
0875: final int tabIndex, final FontMetrics fm) {
0876: int width = fm.stringWidth(tabPane.getTitleAt(tabIndex));
0877:
0878: Icon icon = getIconForTab(tabIndex);
0879: if (icon != null) {
0880: width += textIconGap + icon.getIconWidth();
0881: }
0882:
0883: width += Math.abs(getTabLabelShiftX(tabPlacement, tabIndex,
0884: true)
0885: - getTabLabelShiftX(tabPlacement, tabIndex, false));
0886:
0887: // add space to paint a focus indicator and a tab's border
0888: width += 3;
0889:
0890: Insets insets = getTabInsets(tabPlacement, tabIndex);
0891: return width + insets.left + insets.right;
0892: }
0893:
0894: protected ChangeListener createChangeListener() {
0895: return new TabSelectionHandler();
0896: }
0897:
0898: protected FocusListener createFocusListener() {
0899: return new FocusHandler();
0900: }
0901:
0902: protected LayoutManager createLayoutManager() {
0903: if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT) {
0904: return new TabbedPaneLayout();
0905: } else {
0906: return new ScrollableTabLayout();
0907: }
0908: }
0909:
0910: protected MouseListener createMouseListener() {
0911: return new MouseHandler();
0912: }
0913:
0914: protected PropertyChangeListener createPropertyChangeListener() {
0915: return new PropertyChangeHandler();
0916: }
0917:
0918: protected JButton createScrollButton(final int direction) {
0919: if (direction != NORTH && direction != SOUTH
0920: && direction != EAST && direction != WEST) {
0921: throw new IllegalArgumentException();
0922: }
0923:
0924: return new ScrollButton(direction);
0925: }
0926:
0927: protected void expandTabRunsArray() {
0928: int[] oldTabRuns = tabRuns;
0929: tabRuns = new int[oldTabRuns.length
0930: + TAB_RUNS_ARRAY_SIZE_INCREMENT];
0931: System.arraycopy(oldTabRuns, 0, tabRuns, 0, oldTabRuns.length);
0932: }
0933:
0934: protected Insets getContentBorderInsets(final int tabPlacement) {
0935: return contentBorderInsets;
0936: }
0937:
0938: protected int getFocusIndex() {
0939: Component focusOwner = KeyboardFocusManager
0940: .getCurrentKeyboardFocusManager().getFocusOwner();
0941: if (focusOwner != tabPane) {
0942: return -1;
0943: }
0944:
0945: return tabPane.getSelectedIndex();
0946: }
0947:
0948: protected FontMetrics getFontMetrics() {
0949: return Utilities.getFontMetrics(tabPane);
0950: }
0951:
0952: protected Icon getIconForTab(final int index) {
0953: return tabPane.isEnabledAt(index) ? tabPane.getIconAt(index)
0954: : tabPane.getDisabledIconAt(index);
0955: }
0956:
0957: public Dimension getMaximumSize(final JComponent c) {
0958: return null;
0959: }
0960:
0961: public Dimension getMinimumSize(final JComponent c) {
0962: return null;
0963: }
0964:
0965: protected int getNextTabIndex(final int base) {
0966: return getNextElementInRing(base, tabPane.getTabCount());
0967: }
0968:
0969: protected int getPreviousTabIndex(final int base) {
0970: return getPreviousElementInRing(base, tabPane.getTabCount());
0971: }
0972:
0973: protected int getNextTabIndexInRun(final int tabCount,
0974: final int base) {
0975: int run = getRunForTab(tabCount, base);
0976: int firstTabInRun = firstTabInRun(tabCount, run);
0977: int lastTabInRun = lastTabInRun(tabCount, run);
0978: return firstTabInRun
0979: + getNextElementInRing(base - firstTabInRun,
0980: lastTabInRun - firstTabInRun + 1);
0981: }
0982:
0983: protected int getPreviousTabIndexInRun(final int tabCount,
0984: final int base) {
0985: int run = getRunForTab(tabCount, base);
0986: int firstTabInRun = firstTabInRun(tabCount, run);
0987: int lastTabInRun = lastTabInRun(tabCount, run);
0988: return firstTabInRun
0989: + getPreviousElementInRing(base - firstTabInRun,
0990: lastTabInRun - firstTabInRun + 1);
0991: }
0992:
0993: protected int getNextTabRun(final int baseRun) {
0994: // using getTabRunCount() instead of runCount leads to stack overflow
0995: return getNextElementInRing(baseRun, runCount);
0996: }
0997:
0998: protected int getPreviousTabRun(final int baseRun) {
0999: return getPreviousElementInRing(baseRun, runCount);
1000: }
1001:
1002: protected int getRunForTab(final int tabCount, final int tabIndex) {
1003: for (int run = 0; run < runCount; run++) {
1004: int firstTabInRun = tabRuns[run];
1005: if (firstTabInRun <= tabIndex
1006: && tabIndex <= lastTabInRun(tabCount, run)) {
1007: return run;
1008: }
1009: }
1010: assert false : "incorrect tabIndex";
1011: return 0;
1012: }
1013:
1014: protected Insets getSelectedTabPadInsets(final int tabPlacement) {
1015: Insets rotatedInsets = new Insets(0, 0, 0, 0);
1016: rotateInsets(selectedTabPadInsets, rotatedInsets, tabPlacement);
1017: return rotatedInsets;
1018: }
1019:
1020: protected Insets getTabAreaInsets(final int tabPlacement) {
1021: Insets rotatedInsets = new Insets(0, 0, 0, 0);
1022: rotateInsets(tabAreaInsets, rotatedInsets, tabPlacement);
1023: return rotatedInsets;
1024: }
1025:
1026: protected Rectangle getTabBounds(final int tabIndex,
1027: final Rectangle dest) {
1028: dest.setBounds(rects[tabIndex]);
1029: if (isVerticalRun(tabPane.getTabPlacement())) {
1030: dest.translate(0, scrollableTabsOffset);
1031: } else {
1032: dest.translate(scrollableTabsOffset, 0);
1033: }
1034: return dest;
1035: }
1036:
1037: public Rectangle getTabBounds(final JTabbedPane pane,
1038: final int index) {
1039: Rectangle result = new Rectangle();
1040: return getTabBounds(index, result);
1041: }
1042:
1043: protected Insets getTabInsets(final int tabPlacement,
1044: final int tabIndex) {
1045: return tabInsets;
1046: }
1047:
1048: protected int getTabLabelShiftX(final int tabPlacement,
1049: final int tabIndex, final boolean isSelected) {
1050: int offset = -1;
1051: if (isSelected) {
1052: if (tabPlacement == RIGHT) {
1053: offset = 1;
1054: }
1055: } else {
1056: if (tabPlacement == LEFT) {
1057: offset = 1;
1058: }
1059: }
1060:
1061: return offset;
1062: }
1063:
1064: protected int getTabLabelShiftY(final int tabPlacement,
1065: final int tabIndex, final boolean isSelected) {
1066: int offset = 1;
1067: if (isSelected) {
1068: if (tabPlacement == TOP) {
1069: offset = -1;
1070: }
1071: } else {
1072: if (tabPlacement == BOTTOM) {
1073: offset = -1;
1074: }
1075: }
1076:
1077: return offset;
1078: }
1079:
1080: public int getTabRunCount(final JTabbedPane pane) {
1081: tabPane.doLayout();
1082: return runCount;
1083: }
1084:
1085: protected int getTabRunIndent(final int tabPlacement, final int run) {
1086: return 0;
1087: }
1088:
1089: protected int getTabRunOffset(final int tabPlacement,
1090: final int tabCount, final int tabIndex,
1091: final boolean forward) {
1092: int curRun = getRunForTab(tabCount, tabIndex);
1093: int newRun = !forward ? getNextTabRun(curRun)
1094: : getPreviousTabRun(curRun);
1095:
1096: int lastTab = lastTabInRun(tabCount, newRun);
1097: int rc;
1098: if (isVerticalRun(tabPlacement)) {
1099: rc = (int) rects[lastTab].getCenterX()
1100: - (int) rects[tabIndex].getCenterX();
1101: } else {
1102: rc = (int) rects[lastTab].getCenterY()
1103: - (int) rects[tabIndex].getCenterY();
1104: }
1105: return rc;
1106: }
1107:
1108: protected int getTabRunOverlay(final int tabPlacement) {
1109: return tabRunOverlay;
1110: }
1111:
1112: protected View getTextViewForTab(final int tabIndex) {
1113: //TODO: implement when HTML styled text is supported
1114: return null;
1115: }
1116:
1117: protected void installComponents() {
1118: if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
1119: installScrollableTabsComponents();
1120: }
1121: }
1122:
1123: protected void uninstallComponents() {
1124: if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
1125: uninstallScrollableTabsComponents();
1126: }
1127: }
1128:
1129: protected void installDefaults() {
1130: LookAndFeel.installColorsAndFont(tabPane,
1131: "TabbedPane.background", "TabbedPane.foreground",
1132: "TabbedPane.font");
1133:
1134: darkShadow = UIManager.getColor("TabbedPane.darkShadow");
1135: shadow = UIManager.getColor("TabbedPane.shadow");
1136: highlight = UIManager.getColor("TabbedPane.light");
1137: lightHighlight = UIManager.getColor("TabbedPane.highlight");
1138: focus = UIManager.getColor("TabbedPane.focus");
1139:
1140: contentBorderInsets = UIManager
1141: .getInsets("TabbedPane.contentBorderInsets");
1142: selectedTabPadInsets = UIManager
1143: .getInsets("TabbedPane.selectedTabPadInsets");
1144: tabAreaInsets = UIManager.getInsets("TabbedPane.tabAreaInsets");
1145: tabInsets = UIManager.getInsets("TabbedPane.tabInsets");
1146:
1147: tabRunOverlay = UIManager.getInt("TabbedPane.tabRunOverlay");
1148: textIconGap = UIManager.getInt("TabbedPane.textIconGap");
1149:
1150: selectedTabBackground = UIManager.getColor("control");
1151: }
1152:
1153: protected void uninstallDefaults() {
1154: }
1155:
1156: private ActionMap getUIActionMap() {
1157: if (actionMap != null) {
1158: return actionMap;
1159: }
1160:
1161: actionMap = new ActionMapUIResource();
1162: final AbstractAction navigateEastAction = new NavigateAction(
1163: EAST);
1164: actionMap.put("navigateRight", navigateEastAction);
1165: final AbstractAction navigateWestAction = new NavigateAction(
1166: WEST);
1167: actionMap.put("navigateLeft", navigateWestAction);
1168: actionMap.put("navigateUp", new NavigateAction(NORTH));
1169: actionMap.put("navigateDown", new NavigateAction(SOUTH));
1170:
1171: // "ctrl DOWN", "ctrl KP_DOWN"
1172: actionMap.put("requestFocusForVisibleComponent",
1173: new AbstractAction() {
1174: public void actionPerformed(final ActionEvent e) {
1175: if (getVisibleComponent() != null) {
1176: getVisibleComponent().requestFocus();
1177: }
1178: }
1179: });
1180:
1181: // "ctrl PAGE_DOWN", "navigatePageDown"
1182: actionMap.put("navigatePageDown", navigateWestAction);
1183: // "ctrl PAGE_UP", "navigatePageUp"
1184: actionMap.put("navigatePageUp", navigateEastAction);
1185:
1186: // "ctrl KP_UP", "ctrl UP"
1187: actionMap.put("requestFocus", new AbstractAction() {
1188: public void actionPerformed(final ActionEvent e) {
1189: tabPane.requestFocus();
1190: }
1191: });
1192:
1193: actionMap.put(StringConstants.MNEMONIC_ACTION, MNEMONIC_ACTION);
1194:
1195: return actionMap;
1196: }
1197:
1198: protected void installKeyboardActions() {
1199: SwingUtilities.replaceUIInputMap(tabPane,
1200: JComponent.WHEN_FOCUSED, (InputMap) UIManager
1201: .get("TabbedPane.focusInputMap"));
1202:
1203: SwingUtilities
1204: .replaceUIInputMap(tabPane,
1205: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
1206: (InputMap) UIManager
1207: .get("TabbedPane.ancestorInputMap"));
1208:
1209: SwingUtilities.replaceUIActionMap(tabPane, getUIActionMap());
1210: }
1211:
1212: protected void uninstallKeyboardActions() {
1213: SwingUtilities.replaceUIInputMap(tabPane,
1214: JComponent.WHEN_FOCUSED, null);
1215: SwingUtilities.replaceUIInputMap(tabPane,
1216: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
1217: SwingUtilities.replaceUIActionMap(tabPane, null);
1218: }
1219:
1220: protected void installListeners() {
1221: if (focusListener == null) {
1222: focusListener = createFocusListener();
1223: }
1224: tabPane.addFocusListener(focusListener);
1225:
1226: if (mouseListener == null) {
1227: mouseListener = createMouseListener();
1228: }
1229: tabPane.addMouseListener(mouseListener);
1230:
1231: if (mouseMotionListener == null) {
1232: mouseMotionListener = new MouseMotionHandler();
1233: }
1234: tabPane.addMouseMotionListener(mouseMotionListener);
1235:
1236: if (tabChangeListener == null) {
1237: tabChangeListener = createChangeListener();
1238: }
1239: tabPane.addChangeListener(tabChangeListener);
1240:
1241: if (propertyChangeListener == null) {
1242: propertyChangeListener = createPropertyChangeListener();
1243: }
1244: tabPane.addPropertyChangeListener(propertyChangeListener);
1245: }
1246:
1247: protected void uninstallListeners() {
1248: tabPane.removeFocusListener(focusListener);
1249: tabPane.removeMouseListener(mouseListener);
1250: tabPane.removeMouseMotionListener(mouseMotionListener);
1251: tabPane.removeChangeListener(tabChangeListener);
1252: tabPane.removePropertyChangeListener(propertyChangeListener);
1253: }
1254:
1255: public void installUI(final JComponent c) {
1256: tabPane = (JTabbedPane) c;
1257: setRolloverTab(-1);
1258:
1259: installDefaults();
1260: tabPane.setLayout(createLayoutManager());
1261: installComponents();
1262: installListeners();
1263: installKeyboardActions();
1264: }
1265:
1266: public void uninstallUI(final JComponent c) {
1267: uninstallDefaults();
1268: tabPane.setLayout(null);
1269: uninstallComponents();
1270: uninstallListeners();
1271: uninstallKeyboardActions();
1272: }
1273:
1274: protected int lastTabInRun(final int tabCount, final int run) {
1275: int nextRun = getNextTabRun(run);
1276: int firstIndex = firstTabInRun(tabCount, nextRun);
1277: return getPreviousTabIndex(firstIndex);
1278: }
1279:
1280: private int firstTabInRun(final int tabCount, final int run) {
1281: return tabRuns[run];
1282: }
1283:
1284: protected void layoutLabel(final int tabPlacement,
1285: final FontMetrics metrics, final int tabIndex,
1286: final String title, final Icon icon,
1287: final Rectangle tabRect, final Rectangle iconRect,
1288: final Rectangle textRect, final boolean isSelected) {
1289: iconRect.setBounds(0, 0, 0, 0);
1290: textRect.setBounds(0, 0, 0, 0);
1291:
1292: // calculate inner tab area
1293: calcRect.setBounds(tabRect);
1294: calculateInnerArea(calcRect, getTabInsets(tabPlacement,
1295: tabIndex));
1296: calcRect.translate(getTabLabelShiftX(tabPlacement, tabIndex,
1297: isSelected), getTabLabelShiftY(tabPlacement, tabIndex,
1298: isSelected));
1299: boolean isLTR = tabPane.getComponentOrientation()
1300: .isLeftToRight();
1301: SwingUtilities.layoutCompoundLabel(getFontMetrics(), title,
1302: icon, CENTER, CENTER, CENTER, isLTR ? RIGHT : LEFT,
1303: calcRect, iconRect, textRect, textIconGap);
1304: }
1305:
1306: protected void navigateSelectedTab(final int direction) {
1307: int tabPlacement = tabPane.getTabPlacement();
1308: int correctedDirection = rotateDirection(tabPlacement,
1309: direction);
1310: int selectedIndex = tabPane.getSelectedIndex();
1311:
1312: if (correctedDirection == EAST) {
1313: selectNextTabInRun(selectedIndex);
1314: } else if (correctedDirection == WEST) {
1315: selectPreviousTabInRun(selectedIndex);
1316: } else {
1317: int tabRunOffset = getTabRunOffset(tabPlacement, tabPane
1318: .getTabCount(), selectedIndex,
1319: correctedDirection == SOUTH);
1320: selectAdjacentRunTab(tabPlacement, selectedIndex,
1321: tabRunOffset);
1322: }
1323: }
1324:
1325: public void paint(final Graphics g, final JComponent c) {
1326: if (g == null) {
1327: throw new NullPointerException();
1328: }
1329:
1330: if (tabPane.getTabCount() == 0) {
1331: return;
1332: }
1333:
1334: int tabPlacement = tabPane.getTabPlacement();
1335: int selectedIndex = tabPane.getSelectedIndex();
1336:
1337: paintTabArea(g, tabPlacement, selectedIndex);
1338: paintContentBorder(g, tabPlacement, selectedIndex);
1339: }
1340:
1341: protected void paintContentBorder(final Graphics g,
1342: final int tabPlacement, final int selectedIndex) {
1343: paintContentBorderTopEdge(g, tabPlacement, selectedIndex,
1344: contentAreaBounds.x, contentAreaBounds.y,
1345: contentAreaBounds.width, contentAreaBounds.height);
1346: paintContentBorderLeftEdge(g, tabPlacement, selectedIndex,
1347: contentAreaBounds.x, contentAreaBounds.y,
1348: contentAreaBounds.width, contentAreaBounds.height);
1349: paintContentBorderBottomEdge(g, tabPlacement, selectedIndex,
1350: contentAreaBounds.x, contentAreaBounds.y,
1351: contentAreaBounds.width, contentAreaBounds.height);
1352: paintContentBorderRightEdge(g, tabPlacement, selectedIndex,
1353: contentAreaBounds.x, contentAreaBounds.y,
1354: contentAreaBounds.width, contentAreaBounds.height);
1355: }
1356:
1357: /**
1358: * x, y, w, h are bounds of the content area including content area
1359: * insets.
1360: */
1361: protected void paintContentBorderBottomEdge(final Graphics g,
1362: final int tabPlacement, final int selectedIndex,
1363: final int x, final int y, final int w, final int h) {
1364: int xx = x;
1365: if (tabPlacement == BOTTOM) {
1366: getTabBounds(selectedIndex, calcRect);
1367: if (calcRect.x == x) {
1368: xx++;
1369: calcRect.x++;
1370: calcRect.width--;
1371: }
1372: }
1373: g.setColor(darkShadow);
1374: g.fillRect(xx, y + h - 1, w, 1);
1375:
1376: g.setColor(highlight);
1377: g.fillRect(x + 2, y + h - 3, w - 4, 2);
1378:
1379: if (tabPlacement == BOTTOM
1380: && isTabAdjacentToContentAreaBorder(selectedIndex)) {
1381: g.setColor(selectedTabBackground);
1382: g.fillRect(calcRect.x, y + h - 2, calcRect.width, 1);
1383: g.fillRect(calcRect.x, y + h - 1, calcRect.width - 1, 1);
1384: }
1385: }
1386:
1387: /**
1388: * x, y, w, h are bounds of the content area including content area
1389: * insets.
1390: */
1391: protected void paintContentBorderLeftEdge(final Graphics g,
1392: final int tabPlacement, final int selectedIndex,
1393: final int x, final int y, final int w, final int h) {
1394: g.setColor(lightHighlight);
1395: g.fillRect(x, y, 1, h - 1);
1396:
1397: g.setColor(highlight);
1398: g.fillRect(x + 1, y + 1, 1, h - 3);
1399:
1400: if (tabPlacement == LEFT) {
1401: getTabBounds(selectedIndex, calcRect);
1402: if (calcRect.y == y) {
1403: calcRect.y++;
1404: calcRect.height--;
1405: }
1406: if (isTabAdjacentToContentAreaBorder(selectedIndex)) {
1407: g.setColor(selectedTabBackground);
1408: g.fillRect(x, calcRect.y, 1, calcRect.height);
1409: }
1410: }
1411: }
1412:
1413: /**
1414: * x, y, w, h are bounds of the content area including content area
1415: * insets.
1416: */
1417: protected void paintContentBorderRightEdge(final Graphics g,
1418: final int tabPlacement, final int selectedIndex,
1419: final int x, final int y, final int w, final int h) {
1420: int yy = y;
1421: if (tabPlacement == RIGHT) {
1422: getTabBounds(selectedIndex, calcRect);
1423: if (calcRect.y == y) {
1424: yy++;
1425: calcRect.y++;
1426: calcRect.height--;
1427: }
1428: }
1429: g.setColor(darkShadow);
1430: g.fillRect(x + w - 1, yy, 1, h);
1431:
1432: g.setColor(highlight);
1433: g.fillRect(x + w - 3, y + 2, 2, h - 4);
1434:
1435: if (tabPlacement == RIGHT
1436: && isTabAdjacentToContentAreaBorder(selectedIndex)) {
1437: g.setColor(selectedTabBackground);
1438: g.fillRect(x + w - 1, calcRect.y, 1, calcRect.height - 1);
1439: g.fillRect(x + w - 2, calcRect.y, 1, calcRect.height);
1440: }
1441: }
1442:
1443: /**
1444: * x, y, w, h are bounds of the content area including content area
1445: * insets.
1446: */
1447: protected void paintContentBorderTopEdge(final Graphics g,
1448: final int tabPlacement, final int selectedIndex,
1449: final int x, final int y, final int w, final int h) {
1450: g.setColor(lightHighlight);
1451: g.fillRect(x, y, w - 1, 1);
1452:
1453: g.setColor(highlight);
1454: g.fillRect(x + 1, y + 1, w - 3, 1);
1455:
1456: if (tabPlacement == TOP
1457: && isTabAdjacentToContentAreaBorder(selectedIndex)) {
1458: getTabBounds(selectedIndex, calcRect);
1459: g.setColor(selectedTabBackground);
1460: g.fillRect(calcRect.x, y, calcRect.width, 1);
1461: }
1462: }
1463:
1464: protected void paintFocusIndicator(final Graphics g,
1465: final int tabPlacement, final Rectangle[] rects,
1466: final int tabIndex, final Rectangle iconRect,
1467: final Rectangle textRect, final boolean isSelected) {
1468: if (!isFocusPainted(tabIndex)) {
1469: return;
1470: }
1471:
1472: calcRect.setBounds(rects[tabIndex]);
1473: calcRect.grow(-4, -4);
1474: ButtonCommons.paintFocus(g, calcRect, focus);
1475: }
1476:
1477: private boolean isFocusPainted(final int tabIndex) {
1478: if (tabIndex != getFocusIndex()) {
1479: return false;
1480: }
1481:
1482: return tabPane.getIconAt(tabIndex) != null
1483: || !Utilities.isEmptyString(tabPane
1484: .getTitleAt(tabIndex));
1485: }
1486:
1487: protected void paintIcon(final Graphics g, final int tabPlacement,
1488: final int tabIndex, final Icon icon,
1489: final Rectangle iconRect, final boolean isSelected) {
1490: if (icon != null) {
1491: icon.paintIcon(tabPane, g, iconRect.x, iconRect.y);
1492: }
1493: }
1494:
1495: protected void paintTab(final Graphics g, final int tabPlacement,
1496: final Rectangle[] rects, final int tabIndex,
1497: final Rectangle iconRect, final Rectangle textRect) {
1498: Rectangle r = rects[tabIndex];
1499: boolean isSelected = isSelectedTab(tabIndex);
1500: String title = tabPane.getTitleAt(tabIndex);
1501:
1502: paintTabBackground(g, tabPlacement, tabIndex, r.x, r.y,
1503: r.width, r.height, isSelected);
1504:
1505: Icon icon = getIconForTab(tabIndex);
1506: layoutLabel(tabPlacement, getFontMetrics(), tabIndex, title,
1507: icon, r, iconRect, textRect, isSelected);
1508:
1509: paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected);
1510: paintText(g, tabPlacement, tabPane.getFont(), getFontMetrics(),
1511: tabIndex, title, textRect, isSelected);
1512: paintFocusIndicator(g, tabPlacement, rects, tabIndex, iconRect,
1513: textRect, isSelected);
1514: paintTabBorder(g, tabPlacement, tabIndex, r.x, r.y, r.width,
1515: r.height, isSelected);
1516: }
1517:
1518: protected void paintTabArea(final Graphics g,
1519: final int tabPlacement, final int selectedIndex) {
1520: Shape oldClip = g.getClip();
1521: g.clipRect(tabAreaClipRect.x, tabAreaClipRect.y,
1522: tabAreaClipRect.width, tabAreaClipRect.height);
1523:
1524: Point translate = new Point();
1525: if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
1526: if (isVerticalRun(tabPlacement)) {
1527: translate.setLocation(0, scrollableTabsOffset);
1528: } else {
1529: translate.setLocation(scrollableTabsOffset, 0);
1530: }
1531: g.translate(translate.x, translate.y);
1532: }
1533:
1534: Rectangle iconRect = new Rectangle();
1535: Rectangle textRect = new Rectangle();
1536: int tabCount = tabPane.getTabCount();
1537:
1538: for (int run = runCount - 1; run >= 0; run--) {
1539: for (int i = firstTabInRun(tabCount, run); i <= lastTabInRun(
1540: tabCount, run); i++) {
1541:
1542: paintTab(g, tabPlacement, rects, i, iconRect, textRect);
1543: }
1544: if (run == selectedRun) {
1545: paintTab(g, tabPlacement, rects, tabPane
1546: .getSelectedIndex(), iconRect, textRect);
1547: }
1548: }
1549:
1550: g.setClip(oldClip);
1551: g.translate(-translate.x, -translate.y);
1552: }
1553:
1554: protected void paintTabBackground(final Graphics g,
1555: final int tabPlacement, final int tabIndex, final int x,
1556: final int y, final int w, final int h,
1557: final boolean isSelected) {
1558: Color background = isSelected ? selectedTabBackground : tabPane
1559: .getBackgroundAt(tabIndex);
1560: int xx = x;
1561: int yy = y;
1562: int ww = w;
1563: int hh = h;
1564: if (tabPlacement == JTabbedPane.TOP) {
1565: hh += 2;
1566: } else if (tabPlacement == JTabbedPane.BOTTOM) {
1567: hh += 2;
1568: yy -= 2;
1569: } else if (tabPlacement == JTabbedPane.LEFT) {
1570: ww += 2;
1571: } else if (tabPlacement == JTabbedPane.RIGHT) {
1572: ww += 2;
1573: xx -= 2;
1574: }
1575:
1576: g.setColor(background);
1577: g.fillRect(xx + 1, yy + 1, ww - 2, hh - 2);
1578: }
1579:
1580: protected void paintTabBorder(final Graphics g,
1581: final int tabPlacement, final int tabIndex, final int x,
1582: final int y, final int w, final int h,
1583: final boolean isSelected) {
1584: int xx = x;
1585: int yy = y;
1586: int ww = w;
1587: int hh = h;
1588: if (tabPlacement == JTabbedPane.TOP) {
1589: hh += 2;
1590: } else if (tabPlacement == JTabbedPane.BOTTOM) {
1591: hh += 2;
1592: yy -= 2;
1593: } else if (tabPlacement == JTabbedPane.LEFT) {
1594: ww += 2;
1595: } else if (tabPlacement == JTabbedPane.RIGHT) {
1596: ww += 2;
1597: xx -= 2;
1598: }
1599:
1600: int[] highlightX = { xx, xx, xx + ww - 2 };
1601: int[] highlightY = { yy + hh - 2, yy, yy };
1602: g.setColor(lightHighlight);
1603: g.drawPolyline(highlightX, highlightY, highlightX.length);
1604:
1605: int[] shadowX = { xx + ww - 1, xx + ww - 1, xx + 1 };
1606: int[] shadowY = { yy + 1, yy + hh - 1, yy + hh - 1 };
1607: g.setColor(darkShadow);
1608: g.drawPolyline(shadowX, shadowY, shadowX.length);
1609:
1610: g.setColor(shadow);
1611: g.drawRect(xx + ww - 2, yy + 1, 0, hh - 3);
1612: g.drawRect(xx + 1, yy + hh - 2, ww - 3, 0);
1613: }
1614:
1615: protected void paintText(final Graphics g, final int tabPlacement,
1616: final Font font, final FontMetrics metrics,
1617: final int tabIndex, final String title,
1618: final Rectangle textRect, final boolean isSelected) {
1619: Color color = tabPane.isEnabledAt(tabIndex) ? tabPane
1620: .getForegroundAt(tabIndex) : tabPane.getBackgroundAt(
1621: tabIndex).darker();
1622: ButtonCommons.paintText(g, metrics, title, tabPane
1623: .getDisplayedMnemonicIndexAt(tabIndex), textRect,
1624: title, color);
1625: }
1626:
1627: protected void selectAdjacentRunTab(final int tabPlacement,
1628: final int tabIndex, final int offset) {
1629: getTabBounds(tabIndex, calcRect);
1630: int x = (int) calcRect.getCenterX();
1631: int y = (int) calcRect.getCenterY();
1632: if (isVerticalRun(tabPlacement)) {
1633: x += offset;
1634: } else {
1635: y += offset;
1636: }
1637: int newTabIndex = tabForCoordinate(tabPane, x, y);
1638: if (newTabIndex != -1) {
1639: if (tabPane.isEnabledAt(newTabIndex)) {
1640: tabPane.setSelectedIndex(newTabIndex);
1641: } else {
1642: selectNextTab(newTabIndex);
1643: }
1644: }
1645: }
1646:
1647: protected void selectNextTab(final int current) {
1648: int i = current;
1649: do {
1650: i = getNextTabIndex(i);
1651: } while (i != current && !tabPane.isEnabledAt(i));
1652:
1653: tabPane.setSelectedIndex(i);
1654: }
1655:
1656: protected void selectNextTabInRun(final int current) {
1657: int tabCount = tabPane.getTabCount();
1658:
1659: int i = current;
1660: do {
1661: i = getNextTabIndexInRun(tabCount, i);
1662: } while (i != current && !tabPane.isEnabledAt(i));
1663:
1664: tabPane.setSelectedIndex(i);
1665: }
1666:
1667: protected void selectPreviousTab(final int current) {
1668: int i = current;
1669: do {
1670: i = getPreviousTabIndex(i);
1671: } while (i != current && !tabPane.isEnabledAt(i));
1672:
1673: tabPane.setSelectedIndex(i);
1674: }
1675:
1676: protected void selectPreviousTabInRun(final int current) {
1677: int tabCount = tabPane.getTabCount();
1678:
1679: int i = current;
1680: do {
1681: i = getPreviousTabIndexInRun(tabCount, i);
1682: } while (i != current && !tabPane.isEnabledAt(i));
1683:
1684: tabPane.setSelectedIndex(i);
1685: }
1686:
1687: protected void setRolloverTab(final int index) {
1688: rolloverTab = index;
1689: }
1690:
1691: protected int getRolloverTab() {
1692: return rolloverTab;
1693: }
1694:
1695: protected void setVisibleComponent(final Component component) {
1696: Component oldVisible = getVisibleComponent();
1697: if (oldVisible != component) {
1698: if (oldVisible != null) {
1699: oldVisible.setVisible(false);
1700: }
1701: visibleComponent = component;
1702: }
1703:
1704: if (visibleComponent != null) {
1705: visibleComponent.setVisible(true);
1706: }
1707: }
1708:
1709: protected Component getVisibleComponent() {
1710: return visibleComponent;
1711: }
1712:
1713: protected boolean shouldPadTabRun(final int tabPlacement,
1714: final int run) {
1715: return runCount > 1;
1716: }
1717:
1718: protected boolean shouldRotateTabRuns(final int tabPlacement) {
1719: return true;
1720: }
1721:
1722: public int tabForCoordinate(final JTabbedPane pane, final int x,
1723: final int y) {
1724: for (int i = 0; i < tabPane.getTabCount(); i++) {
1725: getTabBounds(i, calcRect);
1726: if (calcRect.contains(x, y)) {
1727: return i;
1728: }
1729: }
1730:
1731: return -1;
1732: }
1733:
1734: private int rotateDirection(final int tabPlacement,
1735: final int direction) {
1736: if (tabPlacement == LEFT || tabPlacement == RIGHT) {
1737: switch (direction) {
1738: case NORTH:
1739: return WEST;
1740: case SOUTH:
1741: return EAST;
1742: case WEST:
1743: return NORTH;
1744: case EAST:
1745: return SOUTH;
1746: }
1747: }
1748: return direction;
1749: }
1750:
1751: private int getNextElementInRing(final int index, final int ringSize) {
1752: return (index + 1) % ringSize;
1753: }
1754:
1755: private int getPreviousElementInRing(final int index,
1756: final int ringSize) {
1757: return (index + ringSize - 1) % ringSize;
1758: }
1759:
1760: private void installScrollableTabsComponents() {
1761: // if (scrollableTabArea == null) {
1762: // scrollableTabArea = new ScrollableTabPanel();
1763: // }
1764: // tabPane.add(scrollableTabArea);
1765: uninstallScrollableTabsComponents();
1766:
1767: if (isVerticalRun(tabPane.getTabPlacement())) {
1768: leftScrollButton = createScrollButton(NORTH);
1769: rightScrollButton = createScrollButton(SOUTH);
1770: } else {
1771: leftScrollButton = createScrollButton(WEST);
1772: rightScrollButton = createScrollButton(EAST);
1773: }
1774: tabPane.add(leftScrollButton);
1775: tabPane.add(rightScrollButton);
1776: }
1777:
1778: private void uninstallScrollableTabsComponents() {
1779: // tabPane.remove(scrollableTabArea);
1780: tabPane.remove(leftScrollButton);
1781: tabPane.remove(rightScrollButton);
1782: scrollableTabsOffset = 0;
1783: }
1784:
1785: private void calculateInnerArea(final Rectangle rect,
1786: final Insets insets) {
1787: rect.x += insets.left;
1788: rect.y += insets.top;
1789: rect.width -= insets.left + insets.right;
1790: rect.height -= insets.top + insets.bottom;
1791: }
1792:
1793: private boolean isVerticalRun(final int tabPlacement) {
1794: return tabPlacement == LEFT || tabPlacement == RIGHT;
1795: }
1796:
1797: private boolean isSelectedTab(final int tabIndex) {
1798: return tabPane.getSelectedIndex() == tabIndex;
1799: }
1800:
1801: private int findTruncatedTab(final boolean forward) {
1802: int inc;
1803: int start;
1804: int end;
1805:
1806: if (forward) {
1807: inc = -1;
1808: start = tabPane.getTabCount() - 1;
1809: end = 0;
1810: } else {
1811: inc = 1;
1812: start = 0;
1813: end = tabPane.getTabCount() - 1;
1814: }
1815:
1816: int rc = end;
1817: for (int i = start; i != end; i += inc) {
1818: if (tabAreaInnerBounds
1819: .intersects(getTabBounds(i, calcRect))) {
1820: rc = i;
1821: break;
1822: }
1823: }
1824:
1825: // correct the result if border of tabAreaInnerBounds lies
1826: // exactly _between_ tabs
1827: getTabBounds(rc, calcRect);
1828: if (forward) {
1829: // "- 1" because contains() returns false for points on the border
1830: if (tabAreaInnerBounds.contains(calcRect.getMaxX() - 1,
1831: calcRect.getMaxY() - 1)
1832: && rc < tabPane.getTabCount() - 1) {
1833: rc++;
1834: }
1835: } else {
1836: // "+ 1" because contains() returns false for points on the border
1837: if (tabAreaInnerBounds.contains(calcRect.x + 1,
1838: calcRect.y + 1)
1839: && rc > 0) {
1840: rc--;
1841: }
1842: }
1843:
1844: return rc;
1845: }
1846:
1847: private int calculateScrollDelta(final Rectangle r,
1848: final boolean forward) {
1849: int delta = 0;
1850:
1851: if (forward) {
1852: delta = Math.min(tabAreaInnerBounds.x
1853: + tabAreaInnerBounds.width - (r.x + r.width),
1854: tabAreaInnerBounds.y + tabAreaInnerBounds.height
1855: - (r.y + r.height));
1856: } else {
1857: delta = Math.max(tabAreaInnerBounds.x - r.x,
1858: tabAreaInnerBounds.y - r.y);
1859: }
1860:
1861: return delta;
1862: }
1863:
1864: private void scrollToShowTab(final boolean forward) {
1865: if (tabPane.getTabLayoutPolicy() != JTabbedPane.SCROLL_TAB_LAYOUT) {
1866: return;
1867: }
1868:
1869: int tabIndex = findTruncatedTab(forward);
1870: updateScrollableTabOffset(tabIndex, forward);
1871: }
1872:
1873: private void scrollToShowTab(final int selectedIndex) {
1874: if (tabPane.getTabLayoutPolicy() != JTabbedPane.SCROLL_TAB_LAYOUT) {
1875: return;
1876: }
1877:
1878: getTabBounds(selectedIndex, calcRect);
1879: if (tabAreaInnerBounds.contains(calcRect)) {
1880: return;
1881: }
1882:
1883: boolean forward = tabAreaInnerBounds.contains(calcRect.x,
1884: calcRect.y);
1885: updateScrollableTabOffset(selectedIndex, forward);
1886: }
1887:
1888: private void updateScrollableTabOffset(final int tabIndex,
1889: final boolean forward) {
1890: getTabBounds(tabIndex, calcRect);
1891: int delta = calculateScrollDelta(calcRect, forward);
1892: if (tabIndex > 0 && tabIndex < tabPane.getTabCount() - 1
1893: /* || tabIndex == 0 && forward
1894: || tabIndex == tabPane.getTabCount() - 1 && !forward*/) {
1895: // the idea of this code is to make visible part of the next tab
1896: if (delta > 0) {
1897: delta += 5;
1898: } else if (delta < 0) {
1899: delta -= 5;
1900: }
1901: }
1902: scrollableTabsOffset += delta;
1903: updateScrollButtons();
1904: }
1905:
1906: private int calculateTabIndexByMouseEvent(final MouseEvent e) {
1907: Point p = SwingUtilities.convertPoint(e.getComponent(), e
1908: .getX(), e.getY(), tabPane);
1909: if (!tabAreaInnerBounds.contains(p)) {
1910: return -1;
1911: }
1912: return tabForCoordinate(tabPane, p.x, p.y);
1913: }
1914:
1915: private boolean isTabAdjacentToContentAreaBorder(
1916: final int selectedIndex) {
1917: return getRunForTab(tabPane.getTabCount(), selectedIndex) == 0;
1918: }
1919:
1920: private void updateScrollButtons() {
1921: leftScrollButton.setEnabled(!tabAreaInnerBounds
1922: .contains(getTabBounds(0, calcRect)));
1923: rightScrollButton.setEnabled(!tabAreaInnerBounds
1924: .contains(getTabBounds(tabPane.getTabCount() - 1,
1925: calcRect)));
1926:
1927: if (!leftScrollButton.isEnabled()
1928: && !rightScrollButton.isEnabled()) {
1929: leftScrollButton.setVisible(false);
1930: rightScrollButton.setVisible(false);
1931: } else {
1932: leftScrollButton.setVisible(true);
1933: rightScrollButton.setVisible(true);
1934: }
1935: }
1936: }
|