0001: /*
0002: * FlatTabbedPaneUI.java
0003: *
0004: * Copyright (C) 2002, 2003, 2004, 2005, 2006 Takis Diakoumis
0005: *
0006: * This program is free software; you can redistribute it and/or
0007: * modify it under the terms of the GNU General Public License
0008: * as published by the Free Software Foundation; either version 2
0009: * of the License, or any later version.
0010: *
0011: * This program is distributed in the hope that it will be useful,
0012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0014: * GNU General Public License for more details.
0015: *
0016: * You should have received a copy of the GNU General Public License
0017: * along with this program; if not, write to the Free Software
0018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
0019: *
0020: */
0021:
0022: package org.underworldlabs.swing.plaf;
0023:
0024: import java.awt.Color;
0025: import java.awt.Component;
0026: import java.awt.Container;
0027: import java.awt.Dimension;
0028: import java.awt.Event;
0029: import java.awt.Font;
0030: import java.awt.FontMetrics;
0031: import java.awt.Graphics;
0032: import java.awt.Graphics2D;
0033: import java.awt.Insets;
0034: import java.awt.LayoutManager;
0035: import java.awt.Point;
0036: import java.awt.Polygon;
0037: import java.awt.Rectangle;
0038: import java.awt.Shape; //import java.awt.Toolkit;
0039:
0040: import java.awt.event.ActionEvent;
0041: import java.awt.event.ActionListener;
0042: import java.awt.event.ContainerEvent;
0043: import java.awt.event.ContainerListener;
0044: import java.awt.event.FocusAdapter;
0045: import java.awt.event.FocusEvent;
0046: import java.awt.event.FocusListener;
0047: import java.awt.event.MouseAdapter;
0048: import java.awt.event.MouseEvent;
0049: import java.awt.event.MouseListener;
0050:
0051: import java.beans.PropertyChangeEvent;
0052: import java.beans.PropertyChangeListener;
0053:
0054: import java.util.Hashtable;
0055: import java.util.Vector;
0056:
0057: import javax.swing.AbstractAction;
0058: import javax.swing.ActionMap;
0059: import javax.swing.Icon;
0060: import javax.swing.InputMap;
0061: import javax.swing.JComponent;
0062: import javax.swing.JPanel;
0063: import javax.swing.JTabbedPane;
0064: import javax.swing.JViewport;
0065: import javax.swing.KeyStroke;
0066: import javax.swing.LookAndFeel;
0067: import javax.swing.SwingConstants;
0068: import javax.swing.SwingUtilities;
0069: import javax.swing.UIManager;
0070:
0071: import javax.swing.event.ChangeEvent;
0072: import javax.swing.event.ChangeListener;
0073:
0074: import javax.swing.plaf.ActionMapUIResource;
0075: import javax.swing.plaf.ComponentUI;
0076: import javax.swing.plaf.InputMapUIResource;
0077: import javax.swing.plaf.TabbedPaneUI;
0078: import javax.swing.plaf.UIResource;
0079:
0080: import javax.swing.plaf.basic.BasicArrowButton;
0081: import javax.swing.plaf.basic.BasicGraphicsUtils;
0082: import javax.swing.plaf.basic.BasicHTML;
0083:
0084: import javax.swing.text.View;
0085:
0086: /*
0087: * @(#)FlatTabbedPaneUI.java 1.126 03/01/23
0088: *
0089: * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
0090: * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
0091: */
0092:
0093: /* ----------------------------------------------------------
0094: * CVS NOTE: Changes to the CVS repository prior to the
0095: * release of version 3.0.0beta1 has meant a
0096: * resetting of CVS revision numbers.
0097: * ----------------------------------------------------------
0098: */
0099:
0100: /** This is a slight modification to the original BasicTabbedPaneUI.
0101: * It removes the heavy border and makes the selected tab bg white.
0102: * This is the beginning of a larger modification - at the moment it
0103: * really is purpose built for the nav panel and its white content panels
0104: * and gray lines.
0105: */
0106: /**
0107: *
0108: * @author Takis Diakoumis
0109: * @version $Revision: 1.4 $
0110: * @date $Date: 2006/05/14 06:56:07 $
0111: */
0112: public class FlatTabbedPaneUI extends TabbedPaneUI implements
0113: SwingConstants {
0114:
0115: // Instance variables initialized at installation
0116:
0117: protected JTabbedPane tabPane;
0118:
0119: protected Color highlight;
0120: protected Color lightHighlight;
0121: protected Color shadow;
0122: protected Color darkShadow;
0123: protected Color focus;
0124: private Color selectedColor;
0125: private Color controlShadow;
0126:
0127: protected int textIconGap;
0128:
0129: protected int tabRunOverlay;
0130:
0131: protected Insets tabInsets;
0132: protected Insets selectedTabPadInsets;
0133: protected Insets tabAreaInsets;
0134: protected Insets contentBorderInsets;
0135:
0136: // Transient variables (recalculated each time TabbedPane is layed out)
0137:
0138: protected int tabRuns[] = new int[10];
0139: protected int runCount = 0;
0140: protected int selectedRun = -1;
0141: protected Rectangle rects[] = new Rectangle[0];
0142: protected int maxTabHeight;
0143: protected int maxTabWidth;
0144:
0145: // Listeners
0146:
0147: protected ChangeListener tabChangeListener;
0148: protected PropertyChangeListener propertyChangeListener;
0149: protected MouseListener mouseListener;
0150: protected FocusListener focusListener;
0151: // PENDING(api): See comment for ContainerHandler
0152: private ContainerListener containerListener;
0153:
0154: // Private instance data
0155:
0156: private Insets currentPadInsets = new Insets(0, 0, 0, 0);
0157: private Insets currentTabAreaInsets = new Insets(0, 0, 0, 0);
0158:
0159: private Component visibleComponent;
0160: // PENDING(api): See comment for ContainerHandler
0161: private Vector htmlViews;
0162:
0163: private Hashtable mnemonicToIndexMap;
0164:
0165: /**
0166: * InputMap used for mnemonics. Only non-null if the JTabbedPane has
0167: * mnemonics associated with it. Lazily created in initMnemonics.
0168: */
0169: private InputMap mnemonicInputMap;
0170:
0171: // For use when tabLayoutPolicy = SCROLL_TAB_LAYOUT
0172: private ScrollableTabSupport tabScroller;
0173:
0174: /**
0175: * A rectangle used for general layout calculations in order
0176: * to avoid constructing many new Rectangles on the fly.
0177: */
0178: protected transient Rectangle calcRect = new Rectangle(0, 0, 0, 0);
0179:
0180: /**
0181: * Number of tabs. When the count differs, the mnemonics are updated.
0182: */
0183: // PENDING: This wouldn't be necessary if JTabbedPane had a better
0184: // way of notifying listeners when the count changed.
0185: private int tabCount;
0186:
0187: // UI creation
0188:
0189: public static ComponentUI createUI(JComponent c) {
0190: return new FlatTabbedPaneUI();
0191: }
0192:
0193: // UI Installation/De-installation
0194:
0195: public void installUI(JComponent c) {
0196: this .tabPane = (JTabbedPane) c;
0197:
0198: c.setLayout(createLayoutManager());
0199: installComponents();
0200: installDefaults();
0201: installListeners();
0202: installKeyboardActions();
0203: }
0204:
0205: public void uninstallUI(JComponent c) {
0206: uninstallKeyboardActions();
0207: uninstallListeners();
0208: uninstallDefaults();
0209: uninstallComponents();
0210: c.setLayout(null);
0211:
0212: this .tabPane = null;
0213: }
0214:
0215: /**
0216: * Invoked by <code>installUI</code> to create
0217: * a layout manager object to manage
0218: * the <code>JTabbedPane</code>.
0219: *
0220: * @return a layout manager object
0221: *
0222: * @see TabbedPaneLayout
0223: * @see javax.swing.JTabbedPane#getTabLayoutPolicy
0224: */
0225: protected LayoutManager createLayoutManager() {
0226: if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
0227: return new TabbedPaneScrollLayout();
0228: } else { /* WRAP_TAB_LAYOUT */
0229: return new TabbedPaneLayout();
0230: }
0231: }
0232:
0233: /* In an attempt to preserve backward compatibility for programs
0234: * which have extended FlatTabbedPaneUI to do their own layout, the
0235: * UI uses the installed layoutManager (and not tabLayoutPolicy) to
0236: * determine if scrollTabLayout is enabled.
0237: */
0238: private boolean scrollableTabLayoutEnabled() {
0239: return (tabPane.getLayout() instanceof TabbedPaneScrollLayout);
0240: }
0241:
0242: /**
0243: * Creates and installs any required subcomponents for the JTabbedPane.
0244: * Invoked by installUI.
0245: *
0246: * @since 1.4
0247: */
0248: protected void installComponents() {
0249: if (scrollableTabLayoutEnabled()) {
0250: if (tabScroller == null) {
0251: tabScroller = new ScrollableTabSupport(tabPane
0252: .getTabPlacement());
0253: tabPane.add(tabScroller.viewport);
0254: tabPane.add(tabScroller.scrollForwardButton);
0255: tabPane.add(tabScroller.scrollBackwardButton);
0256: }
0257: }
0258: }
0259:
0260: /**
0261: * Removes any installed subcomponents from the JTabbedPane.
0262: * Invoked by uninstallUI.
0263: *
0264: * @since 1.4
0265: */
0266: protected void uninstallComponents() {
0267: if (scrollableTabLayoutEnabled()) {
0268: tabPane.remove(tabScroller.viewport);
0269: tabPane.remove(tabScroller.scrollForwardButton);
0270: tabPane.remove(tabScroller.scrollBackwardButton);
0271: tabScroller = null;
0272: }
0273: }
0274:
0275: protected void installDefaults() {
0276: LookAndFeel.installColorsAndFont(tabPane,
0277: "TabbedPane.background", "TabbedPane.foreground",
0278: "TabbedPane.font");
0279: highlight = UIManager.getColor("TabbedPane.light");
0280: lightHighlight = UIManager.getColor("TabbedPane.highlight");
0281: shadow = UIManager.getColor("TabbedPane.shadow");
0282: darkShadow = UIManager.getColor("TabbedPane.darkShadow");
0283: focus = UIManager.getColor("TabbedPane.focus");
0284: selectedColor = UIManager.getColor("TabbedPane.selected");
0285:
0286: if (selectedColor == null) {
0287: selectedColor = UIManager
0288: .getColor("TabbedPane.unselectedTabBackground");
0289:
0290: if (selectedColor == null) { // if still null (some l&f)
0291: selectedColor = UIManager.getColor("control");
0292: }
0293:
0294: }
0295:
0296: controlShadow = UIManager.getColor("controlShadow");
0297:
0298: contentBorderInsets = new Insets(1, 1, 1, 1);
0299: tabInsets = new Insets(0, 1, 0, 6);
0300: tabAreaInsets = new Insets(4, 0, 0, 6);
0301: textIconGap = 1;
0302:
0303: // textIconGap = UIManager.getInt("TabbedPane.textIconGap");
0304: // tabInsets = UIManager.getInsets("TabbedPane.tabInsets");
0305:
0306: selectedTabPadInsets = UIManager
0307: .getInsets("TabbedPane.selectedTabPadInsets");
0308: // tabAreaInsets = UIManager.getInsets("TabbedPane.tabAreaInsets");
0309:
0310: // contentBorderInsets = UIManager.getInsets("TabbedPane.contentBorderInsets");
0311: tabRunOverlay = UIManager.getInt("TabbedPane.tabRunOverlay");
0312:
0313: }
0314:
0315: protected void uninstallDefaults() {
0316: highlight = null;
0317: lightHighlight = null;
0318: shadow = null;
0319: darkShadow = null;
0320: focus = null;
0321: tabInsets = null;
0322: selectedTabPadInsets = null;
0323: tabAreaInsets = null;
0324: contentBorderInsets = null;
0325: }
0326:
0327: protected void installListeners() {
0328: if ((propertyChangeListener = createPropertyChangeListener()) != null) {
0329: tabPane.addPropertyChangeListener(propertyChangeListener);
0330: }
0331: if ((tabChangeListener = createChangeListener()) != null) {
0332: tabPane.addChangeListener(tabChangeListener);
0333: }
0334: if ((mouseListener = createMouseListener()) != null) {
0335: if (scrollableTabLayoutEnabled()) {
0336: tabScroller.tabPanel.addMouseListener(mouseListener);
0337:
0338: } else { // WRAP_TAB_LAYOUT
0339: tabPane.addMouseListener(mouseListener);
0340: }
0341: }
0342: if ((focusListener = createFocusListener()) != null) {
0343: tabPane.addFocusListener(focusListener);
0344: }
0345: // PENDING(api) : See comment for ContainerHandler
0346: if ((containerListener = new ContainerHandler()) != null) {
0347: tabPane.addContainerListener(containerListener);
0348: if (tabPane.getTabCount() > 0) {
0349: htmlViews = createHTMLVector();
0350: }
0351: }
0352: }
0353:
0354: protected void uninstallListeners() {
0355: if (mouseListener != null) {
0356: if (scrollableTabLayoutEnabled()) { // SCROLL_TAB_LAYOUT
0357: tabScroller.tabPanel.removeMouseListener(mouseListener);
0358:
0359: } else { // WRAP_TAB_LAYOUT
0360: tabPane.removeMouseListener(mouseListener);
0361: }
0362: mouseListener = null;
0363: }
0364: if (focusListener != null) {
0365: tabPane.removeFocusListener(focusListener);
0366: focusListener = null;
0367: }
0368:
0369: // PENDING(api): See comment for ContainerHandler
0370: if (containerListener != null) {
0371: tabPane.removeContainerListener(containerListener);
0372: containerListener = null;
0373: if (htmlViews != null) {
0374: htmlViews.removeAllElements();
0375: htmlViews = null;
0376: }
0377: }
0378: if (tabChangeListener != null) {
0379: tabPane.removeChangeListener(tabChangeListener);
0380: tabChangeListener = null;
0381: }
0382: if (propertyChangeListener != null) {
0383: tabPane
0384: .removePropertyChangeListener(propertyChangeListener);
0385: propertyChangeListener = null;
0386: }
0387: }
0388:
0389: protected MouseListener createMouseListener() {
0390: return new MouseHandler();
0391: }
0392:
0393: protected FocusListener createFocusListener() {
0394: return new FocusHandler();
0395: }
0396:
0397: protected ChangeListener createChangeListener() {
0398: return new TabSelectionHandler();
0399: }
0400:
0401: protected PropertyChangeListener createPropertyChangeListener() {
0402: return new PropertyChangeHandler();
0403: }
0404:
0405: protected void installKeyboardActions() {
0406: InputMap km = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0407:
0408: SwingUtilities.replaceUIInputMap(tabPane,
0409: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, km);
0410: km = getInputMap(JComponent.WHEN_FOCUSED);
0411: SwingUtilities.replaceUIInputMap(tabPane,
0412: JComponent.WHEN_FOCUSED, km);
0413: ActionMap am = getActionMap();
0414:
0415: SwingUtilities.replaceUIActionMap(tabPane, am);
0416: /*
0417: if (scrollableTabLayoutEnabled()) {
0418: tabScroller.scrollForwardButton.setAction(am.get("scrollTabsForwardAction"));
0419: tabScroller.scrollBackwardButton.setAction(am.get("scrollTabsBackwardAction"));
0420: }
0421: */
0422: }
0423:
0424: InputMap getInputMap(int condition) {
0425: if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
0426: return (InputMap) UIManager
0427: .get("TabbedPane.ancestorInputMap");
0428: } else if (condition == JComponent.WHEN_FOCUSED) {
0429: return (InputMap) UIManager.get("TabbedPane.focusInputMap");
0430: }
0431: return null;
0432: }
0433:
0434: ActionMap getActionMap() {
0435: ActionMap map = (ActionMap) UIManager
0436: .get("TabbedPane.actionMap");
0437:
0438: if (map == null) {
0439: map = createActionMap();
0440: if (map != null) {
0441: UIManager.getLookAndFeelDefaults().put(
0442: "TabbedPane.actionMap", map);
0443: }
0444: }
0445: return map;
0446: }
0447:
0448: ActionMap createActionMap() {
0449: ActionMap map = new ActionMapUIResource();
0450: map.put("navigateNext", new NextAction());
0451: map.put("navigatePrevious", new PreviousAction());
0452: map.put("navigateRight", new RightAction());
0453: map.put("navigateLeft", new LeftAction());
0454: map.put("navigateUp", new UpAction());
0455: map.put("navigateDown", new DownAction());
0456: map.put("navigatePageUp", new PageUpAction());
0457: map.put("navigatePageDown", new PageDownAction());
0458: map.put("requestFocus", new RequestFocusAction());
0459: map.put("requestFocusForVisibleComponent",
0460: new RequestFocusForVisibleAction());
0461: map.put("setSelectedIndex", new SetSelectedIndexAction());
0462: // map.put("scrollTabsForwardAction", new FlatScrollTabsForwardAction());
0463: // map.put("scrollTabsBackwardAction",new FlatScrollTabsBackwardAction());
0464: return map;
0465: }
0466:
0467: protected void uninstallKeyboardActions() {
0468: SwingUtilities.replaceUIActionMap(tabPane, null);
0469: SwingUtilities.replaceUIInputMap(tabPane,
0470: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
0471: SwingUtilities.replaceUIInputMap(tabPane,
0472: JComponent.WHEN_FOCUSED, null);
0473: }
0474:
0475: /**
0476: * Reloads the mnemonics. This should be invoked when a memonic changes,
0477: * when the title of a mnemonic changes, or when tabs are added/removed.
0478: */
0479: private void updateMnemonics() {
0480: resetMnemonics();
0481: for (int counter = tabPane.getTabCount() - 1; counter >= 0; counter--) {
0482: int mnemonic = tabPane.getMnemonicAt(counter);
0483:
0484: if (mnemonic > 0) {
0485: addMnemonic(counter, mnemonic);
0486: }
0487: }
0488: }
0489:
0490: /**
0491: * Resets the mnemonics bindings to an empty state.
0492: */
0493: private void resetMnemonics() {
0494: if (mnemonicToIndexMap != null) {
0495: mnemonicToIndexMap.clear();
0496: mnemonicInputMap.clear();
0497: }
0498: }
0499:
0500: /**
0501: * Adds the specified mnemonic at the specified index.
0502: */
0503: private void addMnemonic(int index, int mnemonic) {
0504: if (mnemonicToIndexMap == null) {
0505: initMnemonics();
0506: }
0507: mnemonicInputMap.put(KeyStroke.getKeyStroke(mnemonic,
0508: Event.ALT_MASK), "setSelectedIndex");
0509: mnemonicToIndexMap.put(new Integer(mnemonic),
0510: new Integer(index));
0511: }
0512:
0513: /**
0514: * Installs the state needed for mnemonics.
0515: */
0516: private void initMnemonics() {
0517: mnemonicToIndexMap = new Hashtable();
0518: mnemonicInputMap = new InputMapUIResource();
0519: mnemonicInputMap
0520: .setParent(SwingUtilities.getUIInputMap(tabPane,
0521: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT));
0522: SwingUtilities.replaceUIInputMap(tabPane,
0523: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
0524: mnemonicInputMap);
0525: }
0526:
0527: // Geometry
0528:
0529: public Dimension getPreferredSize(JComponent c) {
0530: // Default to LayoutManager's preferredLayoutSize
0531: return null;
0532: }
0533:
0534: public Dimension getMinimumSize(JComponent c) {
0535: // Default to LayoutManager's minimumLayoutSize
0536: return null;
0537: }
0538:
0539: public Dimension getMaximumSize(JComponent c) {
0540: // Default to LayoutManager's maximumLayoutSize
0541: return null;
0542: }
0543:
0544: // UI Rendering
0545:
0546: public void paint(Graphics g, JComponent c) {
0547: int tc = tabPane.getTabCount();
0548:
0549: if (tabCount != tc) {
0550: tabCount = tc;
0551: updateMnemonics();
0552: }
0553:
0554: int selectedIndex = tabPane.getSelectedIndex();
0555: int tabPlacement = tabPane.getTabPlacement();
0556:
0557: ensureCurrentLayout();
0558:
0559: // Paint tab area
0560: // If scrollable tabs are enabled, the tab area will be
0561: // painted by the scrollable tab panel instead.
0562: //
0563: if (!scrollableTabLayoutEnabled()) { // WRAP_TAB_LAYOUT
0564: paintTabArea(g, tabPlacement, selectedIndex);
0565: }
0566:
0567: // Paint content border
0568: paintContentBorder(g, tabPlacement, selectedIndex);
0569:
0570: }
0571:
0572: /**
0573: * Paints the tabs in the tab area.
0574: * Invoked by paint().
0575: * The graphics parameter must be a valid <code>Graphics</code>
0576: * object. Tab placement may be either:
0577: * <code>JTabbedPane.TOP</code>, <code>JTabbedPane.BOTTOM</code>,
0578: * <code>JTabbedPane.LEFT</code>, or <code>JTabbedPane.RIGHT</code>.
0579: * The selected index must be a valid tabbed pane tab index (0 to
0580: * tab count - 1, inclusive) or -1 if no tab is currently selected.
0581: * The handling of invalid parameters is unspecified.
0582: *
0583: * @param g the graphics object to use for rendering
0584: * @param tabPlacement the placement for the tabs within the JTabbedPane
0585: * @param selectedIndex the tab index of the selected component
0586: *
0587: * @since 1.4
0588: */
0589: protected void paintTabArea(Graphics g, int tabPlacement,
0590: int selectedIndex) {
0591: int tabCount = tabPane.getTabCount();
0592:
0593: Rectangle iconRect = new Rectangle(), textRect = new Rectangle();
0594: Rectangle clipRect = g.getClipBounds();
0595:
0596: // Paint tabRuns of tabs from back to front
0597: for (int i = runCount - 1; i >= 0; i--) {
0598: int start = tabRuns[i];
0599: int next = tabRuns[(i == runCount - 1) ? 0 : i + 1];
0600: int end = (next != 0 ? next - 1 : tabCount - 1);
0601:
0602: for (int j = start; j <= end; j++) {
0603:
0604: // stupid hack - remove all causes index exception
0605: try {
0606: if (rects[j].intersects(clipRect)) {
0607: paintTab(g, tabPlacement, rects, j, iconRect,
0608: textRect);
0609: }
0610: } catch (ArrayIndexOutOfBoundsException e) {
0611: break;
0612: }
0613:
0614: }
0615:
0616: }
0617:
0618: // Paint selected tab if its in the front run
0619: // since it may overlap other tabs
0620: if (selectedIndex >= 0
0621: && getRunForTab(tabCount, selectedIndex) == 0) {
0622: if (rects[selectedIndex].intersects(clipRect)) {
0623: paintTab(g, tabPlacement, rects, selectedIndex,
0624: iconRect, textRect);
0625: }
0626: }
0627: }
0628:
0629: protected void paintTab(Graphics g, int tabPlacement,
0630: Rectangle[] rects, int tabIndex, Rectangle iconRect,
0631: Rectangle textRect) {
0632:
0633: Rectangle tabRect = rects[tabIndex];
0634: int selectedIndex = tabPane.getSelectedIndex();
0635: boolean isSelected = selectedIndex == tabIndex;
0636: Graphics2D g2 = null;
0637: Polygon cropShape = null;
0638: Shape save = null;
0639: int cropx = 0;
0640: int cropy = 0;
0641:
0642: if (scrollableTabLayoutEnabled()) {
0643: if (g instanceof Graphics2D) {
0644: g2 = (Graphics2D) g;
0645:
0646: // Render visual for cropped tab edge...
0647: Rectangle viewRect = tabScroller.viewport.getViewRect();
0648: int cropline;
0649:
0650: switch (tabPlacement) {
0651: case LEFT:
0652: case RIGHT:
0653: cropline = viewRect.y + viewRect.height;
0654: if ((tabRect.y < cropline)
0655: && (tabRect.y + tabRect.height > cropline)) {
0656: cropShape = createCroppedTabClip(tabPlacement,
0657: tabRect, cropline);
0658: cropx = tabRect.x;
0659: cropy = cropline - 1;
0660: }
0661: break;
0662: case TOP:
0663: case BOTTOM:
0664: default:
0665: cropline = viewRect.x + viewRect.width;
0666:
0667: if ((tabRect.x < cropline)
0668: && (tabRect.x + tabRect.width > cropline)) {
0669: cropShape = createCroppedTabClip(tabPlacement,
0670: tabRect, cropline);
0671: cropx = cropline - 1;
0672: cropy = tabRect.y;
0673: }
0674:
0675: }
0676:
0677: if (cropShape != null) {
0678: save = g2.getClip();
0679: g2.clip(cropShape);
0680: }
0681:
0682: }
0683:
0684: }
0685:
0686: paintTabBackground(g, tabPlacement, tabIndex, tabRect.x,
0687: tabRect.y, tabRect.width, tabRect.height + 1,
0688: isSelected);
0689:
0690: paintTabBorder(g, tabPlacement, tabIndex, tabRect.x, tabRect.y,
0691: tabRect.width, tabRect.height + 1, isSelected);
0692:
0693: String title = tabPane.getTitleAt(tabIndex);
0694: Font font = tabPane.getFont();
0695: FontMetrics metrics = g.getFontMetrics(font);
0696: Icon icon = getIconForTab(tabIndex);
0697:
0698: layoutLabel(tabPlacement, metrics, tabIndex, title, icon,
0699: tabRect, iconRect, textRect, isSelected);
0700:
0701: paintText(g, tabPlacement, font, metrics, tabIndex, title,
0702: textRect, isSelected);
0703:
0704: paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected);
0705:
0706: // paintFocusIndicator(g, tabPlacement, rects, tabIndex,
0707: // iconRect, textRect, isSelected);
0708:
0709: if (cropShape != null) {
0710: paintCroppedTabEdge(g, tabPlacement, tabIndex, isSelected,
0711: cropx, cropy);
0712: g2.setClip(save);
0713: }
0714:
0715: }
0716:
0717: /* This method will create and return a polygon shape for the given tab rectangle
0718: * which has been cropped at the specified cropline with a torn edge visual.
0719: * e.g. A "File" tab which has cropped been cropped just after the "i":
0720: * -------------
0721: * | ..... |
0722: * | . |
0723: * | ... . |
0724: * | . . |
0725: * | . . |
0726: * | . . |
0727: * --------------
0728: *
0729: * The x, y arrays below define the pattern used to create a "torn" edge
0730: * segment which is repeated to fill the edge of the tab.
0731: * For tabs placed on TOP and BOTTOM, this righthand torn edge is created by
0732: * line segments which are defined by coordinates obtained by
0733: * subtracting xCropLen[i] from (tab.x + tab.width) and adding yCroplen[i]
0734: * to (tab.y).
0735: * For tabs placed on LEFT or RIGHT, the bottom torn edge is created by
0736: * subtracting xCropLen[i] from (tab.y + tab.height) and adding yCropLen[i]
0737: * to (tab.x).
0738: */
0739: private int xCropLen[] = { 1, 1, 0, 0, 1, 1, 2, 2 };
0740: private int yCropLen[] = { 0, 3, 3, 6, 6, 9, 9, 12 };
0741: private static final int CROP_SEGMENT = 12;
0742:
0743: private Polygon createCroppedTabClip(int tabPlacement,
0744: Rectangle tabRect, int cropline) {
0745: int rlen = 0;
0746: int start = 0;
0747: int end = 0;
0748: int ostart = 0;
0749:
0750: switch (tabPlacement) {
0751: case LEFT:
0752: case RIGHT:
0753: rlen = tabRect.width;
0754: start = tabRect.x;
0755: end = tabRect.x + tabRect.width;
0756: ostart = tabRect.y;
0757: break;
0758: case TOP:
0759: case BOTTOM:
0760: default:
0761: rlen = tabRect.height;
0762: start = tabRect.y;
0763: end = tabRect.y + tabRect.height + 1;
0764: ostart = tabRect.x;
0765: }
0766:
0767: int rcnt = rlen / CROP_SEGMENT;
0768:
0769: if (rlen % CROP_SEGMENT > 0) {
0770: rcnt++;
0771: }
0772:
0773: int npts = 2 + (rcnt * 8);
0774: int xp[] = new int[npts];
0775: int yp[] = new int[npts];
0776: int pcnt = 0;
0777:
0778: xp[pcnt] = ostart;
0779: yp[pcnt++] = end;
0780: xp[pcnt] = ostart;
0781: yp[pcnt++] = start;
0782: for (int i = 0; i < rcnt; i++) {
0783: for (int j = 0; j < xCropLen.length; j++) {
0784: xp[pcnt] = cropline - xCropLen[j];
0785: yp[pcnt] = start + (i * CROP_SEGMENT) + yCropLen[j];
0786: if (yp[pcnt] >= end) {
0787: yp[pcnt] = end;
0788: pcnt++;
0789: break;
0790: }
0791: pcnt++;
0792: }
0793: }
0794: if (tabPlacement == JTabbedPane.TOP
0795: || tabPlacement == JTabbedPane.BOTTOM) {
0796: return new Polygon(xp, yp, pcnt);
0797:
0798: } else { // LEFT or RIGHT
0799: return new Polygon(yp, xp, pcnt);
0800: }
0801: }
0802:
0803: /* If tabLayoutPolicy == SCROLL_TAB_LAYOUT, this method will paint an edge
0804: * indicating the tab is cropped in the viewport display
0805: */
0806: private void paintCroppedTabEdge(Graphics g, int tabPlacement,
0807: int tabIndex, boolean isSelected, int x, int y) {
0808:
0809: switch (tabPlacement) {
0810:
0811: case LEFT:
0812: case RIGHT:
0813: int xx = x;
0814: g.setColor(shadow);
0815:
0816: while (xx <= x + rects[tabIndex].width) {
0817:
0818: for (int i = 0; i < xCropLen.length; i += 2) {
0819: g.drawLine(xx + yCropLen[i], y - xCropLen[i], xx
0820: + yCropLen[i + 1] - 1, y - xCropLen[i + 1]);
0821: }
0822:
0823: xx += CROP_SEGMENT;
0824:
0825: }
0826:
0827: break;
0828:
0829: case TOP:
0830: case BOTTOM:
0831: default:
0832: int yy = y;
0833: g.setColor(controlShadow);
0834:
0835: while (yy <= y + rects[tabIndex].height) {
0836:
0837: for (int i = 0; i < xCropLen.length; i += 2) {
0838: g
0839: .drawLine(x - xCropLen[i],
0840: yy + yCropLen[i], x
0841: - xCropLen[i + 1], yy
0842: + yCropLen[i + 1] - 1);
0843: }
0844:
0845: yy += CROP_SEGMENT;
0846:
0847: }
0848:
0849: }
0850:
0851: }
0852:
0853: protected void layoutLabel(int tabPlacement, FontMetrics metrics,
0854: int tabIndex, String title, Icon icon, Rectangle tabRect,
0855: Rectangle iconRect, Rectangle textRect, boolean isSelected) {
0856:
0857: textRect.x = textRect.y = iconRect.x = iconRect.y = 0;
0858:
0859: View v = getTextViewForTab(tabIndex);
0860:
0861: if (v != null) {
0862: tabPane.putClientProperty("html", v);
0863: }
0864:
0865: SwingUtilities.layoutCompoundLabel((JComponent) tabPane,
0866: metrics, title, icon, SwingUtilities.CENTER,
0867: SwingUtilities.CENTER, SwingUtilities.CENTER,
0868: SwingUtilities.TRAILING, tabRect, iconRect, textRect,
0869: textIconGap);
0870:
0871: tabPane.putClientProperty("html", null);
0872:
0873: int xNudge = getTabLabelShiftX(tabPlacement, tabIndex,
0874: isSelected);
0875: int yNudge = getTabLabelShiftY(tabPlacement, tabIndex,
0876: isSelected);
0877: iconRect.x += xNudge;
0878: iconRect.y += yNudge;
0879: textRect.x += xNudge;
0880: textRect.y += yNudge;
0881:
0882: }
0883:
0884: protected void paintIcon(Graphics g, int tabPlacement,
0885: int tabIndex, Icon icon, Rectangle iconRect,
0886: boolean isSelected) {
0887: if (icon != null) {
0888:
0889: int y = iconRect.y - 1;
0890:
0891: if (!isSelected)
0892: y += 2;
0893:
0894: icon.paintIcon(tabPane, g, iconRect.x, y);
0895:
0896: }
0897:
0898: }
0899:
0900: protected void paintText(Graphics g, int tabPlacement, Font font,
0901: FontMetrics metrics, int tabIndex, String title,
0902: Rectangle textRect, boolean isSelected) {
0903:
0904: g.setFont(font);
0905:
0906: View v = getTextViewForTab(tabIndex);
0907:
0908: if (v != null) {
0909: v.paint(g, textRect); // html
0910: }
0911:
0912: else { // plain text
0913: int mnemIndex = tabPane
0914: .getDisplayedMnemonicIndexAt(tabIndex);
0915:
0916: if (tabPane.isEnabled() && tabPane.isEnabledAt(tabIndex)) {
0917: g.setColor(tabPane.getForegroundAt(tabIndex));
0918: int y = textRect.y + metrics.getAscent() - 1;
0919:
0920: if (!isSelected)
0921: y += 2;
0922:
0923: BasicGraphicsUtils.drawStringUnderlineCharAt(g, title,
0924: mnemIndex, textRect.x, y);
0925:
0926: }
0927:
0928: else { // tab disabled
0929: g
0930: .setColor(tabPane.getBackgroundAt(tabIndex)
0931: .brighter());
0932: BasicGraphicsUtils.drawStringUnderlineCharAt(g, title,
0933: mnemIndex, textRect.x, textRect.y
0934: + metrics.getAscent() - 1);
0935: g.setColor(tabPane.getBackgroundAt(tabIndex).darker());
0936: BasicGraphicsUtils.drawStringUnderlineCharAt(g, title,
0937: mnemIndex, textRect.x - 1, textRect.y
0938: + metrics.getAscent() - 1);
0939: }
0940:
0941: }
0942:
0943: }
0944:
0945: protected int getTabLabelShiftX(int tabPlacement, int tabIndex,
0946: boolean isSelected) {
0947: Rectangle tabRect = rects[tabIndex];
0948: int nudge = 0;
0949: switch (tabPlacement) {
0950: case LEFT:
0951: nudge = isSelected ? -1 : 1;
0952: break;
0953: case RIGHT:
0954: nudge = isSelected ? 1 : -1;
0955: break;
0956: case BOTTOM:
0957: case TOP:
0958: default:
0959: nudge = tabRect.width % 2;
0960: }
0961: return nudge;
0962: }
0963:
0964: protected int getTabLabelShiftY(int tabPlacement, int tabIndex,
0965: boolean isSelected) {
0966: Rectangle tabRect = rects[tabIndex];
0967: int nudge = 0;
0968: switch (tabPlacement) {
0969: case BOTTOM:
0970: nudge = isSelected ? 1 : -1;
0971: break;
0972: case LEFT:
0973: case RIGHT:
0974: nudge = tabRect.height % 2;
0975: break;
0976: case TOP:
0977: default:
0978: nudge = isSelected ? -1 : 1;
0979: ;
0980: }
0981: return nudge;
0982: }
0983:
0984: protected void paintFocusIndicator(Graphics g, int tabPlacement,
0985: Rectangle[] rects, int tabIndex, Rectangle iconRect,
0986: Rectangle textRect, boolean isSelected) {
0987:
0988: Rectangle tabRect = rects[tabIndex];
0989: if (tabPane.hasFocus() && isSelected) {
0990: int x, y, w, h;
0991: g.setColor(focus);
0992: switch (tabPlacement) {
0993: case LEFT:
0994: x = tabRect.x + 3;
0995: y = tabRect.y + 3;
0996: w = tabRect.width - 5;
0997: h = tabRect.height - 6;
0998: break;
0999: case RIGHT:
1000: x = tabRect.x + 2;
1001: y = tabRect.y + 3;
1002: w = tabRect.width - 5;
1003: h = tabRect.height - 6;
1004: break;
1005: case BOTTOM:
1006: x = tabRect.x + 3;
1007: y = tabRect.y + 2;
1008: w = tabRect.width - 6;
1009: h = tabRect.height - 5;
1010: break;
1011: case TOP:
1012: default:
1013: x = tabRect.x + 3;
1014: y = tabRect.y + 3;
1015: w = tabRect.width - 6;
1016: h = tabRect.height - 5;
1017: }
1018: BasicGraphicsUtils.drawDashedRect(g, x, y, w, h);
1019: }
1020: }
1021:
1022: /**
1023: * this function draws the border around each tab
1024: * note that this function does now draw the background of the tab.
1025: * that is done elsewhere
1026: */
1027: protected void paintTabBorder(Graphics g, int tabPlacement,
1028: int tabIndex, int x, int y, int w, int h, boolean isSelected) {
1029: g.setColor(lightHighlight);
1030:
1031: switch (tabPlacement) {
1032: case LEFT:
1033: g.drawLine(x + 1, y + h - 2, x + 1, y + h - 2); // bottom-left highlight
1034: g.drawLine(x, y + 2, x, y + h - 3); // left highlight
1035: g.drawLine(x + 1, y + 1, x + 1, y + 1); // top-left highlight
1036: g.drawLine(x + 2, y, x + w - 1, y); // top highlight
1037:
1038: g.setColor(shadow);
1039: g.drawLine(x + 2, y + h - 2, x + w - 1, y + h - 2); // bottom shadow
1040:
1041: g.setColor(darkShadow);
1042: g.drawLine(x + 2, y + h - 1, x + w - 1, y + h - 1); // bottom dark shadow
1043: break;
1044: case RIGHT:
1045: g.drawLine(x, y, x + w - 3, y); // top highlight
1046:
1047: g.setColor(shadow);
1048: g.drawLine(x, y + h - 2, x + w - 3, y + h - 2); // bottom shadow
1049: g.drawLine(x + w - 2, y + 2, x + w - 2, y + h - 3); // right shadow
1050:
1051: g.setColor(darkShadow);
1052: g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1); // top-right dark shadow
1053: g.drawLine(x + w - 2, y + h - 2, x + w - 2, y + h - 2); // bottom-right dark shadow
1054: g.drawLine(x + w - 1, y + 2, x + w - 1, y + h - 3); // right dark shadow
1055: g.drawLine(x, y + h - 1, x + w - 3, y + h - 1); // bottom dark shadow
1056: break;
1057: case BOTTOM:
1058: g.setColor(controlShadow);
1059: g.drawLine(x, y, x, y + h - 3); // left highlight
1060: g.drawLine(x + 1, y + h - 2, x + 1, y + h - 2); // bottom-left highlight
1061:
1062: // g.setColor(shadow);
1063: // g.drawLine(x+2, y+h-2, x+w-3, y+h-2); // bottom shadow
1064: // g.drawLine(x+w-2, y, x+w-2, y+h-3); // right shadow
1065:
1066: // g.setColor(darkShadow);
1067:
1068: // if (isSelected)
1069: g.drawLine(x + 2, y + h - 1, x + w - 3, y + h - 1); // bottom dark shadow
1070: // else
1071: // g.drawLine(x+2, y+h, x+w-3, y+h); // bottom dark shadow
1072:
1073: g.drawLine(x + w - 2, y + h - 2, x + w - 2, y + h - 2); // bottom-right dark shadow
1074: g.drawLine(x + w - 1, y, x + w - 1, y + h - 3); // right dark shadow
1075:
1076: if (isSelected) {
1077: Shape _clip = g.getClip();
1078: Rectangle r = _clip.getBounds();
1079: g.setClip(r.x, r.y - 3, r.x + r.width, r.y - 3);
1080: g.setColor(Color.RED);
1081: g.drawLine(x + 1, y - 2, x + w - 2, y - 2);
1082: g.setClip(_clip);
1083: }
1084:
1085: break;
1086: case TOP:
1087: default:
1088: g.drawLine(x, y + 2, x, y + h - 1); // left highlight
1089: g.drawLine(x + 1, y + 1, x + 1, y + 1); // top-left highlight
1090: g.drawLine(x + 2, y, x + w - 3, y); // top highlight
1091:
1092: g.setColor(shadow);
1093: g.drawLine(x + w - 2, y + 2, x + w - 2, y + h - 1); // right shadow
1094:
1095: g.setColor(darkShadow);
1096: g.drawLine(x + w - 1, y + 2, x + w - 1, y + h - 1); // right dark-shadow
1097: g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1); // top-right shadow
1098: }
1099:
1100: }
1101:
1102: protected void paintTabBackground(Graphics g, int tabPlacement,
1103: int tabIndex, int x, int y, int w, int h, boolean isSelected) {
1104:
1105: if (isSelected)
1106: g.setColor(Color.WHITE);
1107: else
1108: g.setColor(selectedColor);
1109:
1110: switch (tabPlacement) {
1111:
1112: case LEFT:
1113: g.fillRect(x + 1, y + 1, w - 2, h - 3);
1114: break;
1115: case RIGHT:
1116: g.fillRect(x, y + 1, w - 2, h - 3);
1117: break;
1118: case BOTTOM:
1119: g.fillRect(x + 1, y, w - 3, h - 1);
1120: break;
1121: case TOP:
1122: default:
1123: g.fillRect(x + 1, y + 1, w - 3, h - 1);
1124:
1125: }
1126:
1127: }
1128:
1129: protected void paintContentBorder(Graphics g, int tabPlacement,
1130: int selectedIndex) {
1131: int width = tabPane.getWidth();
1132: int height = tabPane.getHeight();
1133: Insets insets = tabPane.getInsets();
1134:
1135: int x = insets.left;
1136: int y = insets.top;
1137: int w = width - insets.right - insets.left;
1138: int h = height - insets.top - insets.bottom;
1139:
1140: switch (tabPlacement) {
1141: case LEFT:
1142: x += calculateTabAreaWidth(tabPlacement, runCount,
1143: maxTabWidth);
1144: w -= (x - insets.left);
1145: break;
1146: case RIGHT:
1147: w -= calculateTabAreaWidth(tabPlacement, runCount,
1148: maxTabWidth);
1149: break;
1150: case BOTTOM:
1151: h -= calculateTabAreaHeight(tabPlacement, runCount,
1152: maxTabHeight);
1153: break;
1154: case TOP:
1155: default:
1156: y += calculateTabAreaHeight(tabPlacement, runCount,
1157: maxTabHeight);
1158: h -= (y - insets.top);
1159: }
1160: // Fill region behind content area
1161: if (selectedColor == null) {
1162: g.setColor(tabPane.getBackground());
1163: } else {
1164: g.setColor(selectedColor);
1165: }
1166: g.fillRect(x, y, w, h);
1167:
1168: paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y,
1169: w, h);
1170: paintContentBorderLeftEdge(g, tabPlacement, selectedIndex, x,
1171: y, w, h);
1172: paintContentBorderBottomEdge(g, tabPlacement, selectedIndex, x,
1173: y, w, h);
1174: paintContentBorderRightEdge(g, tabPlacement, selectedIndex, x,
1175: y, w, h);
1176:
1177: }
1178:
1179: protected void paintContentBorderTopEdge(Graphics g,
1180: int tabPlacement, int selectedIndex, int x, int y, int w,
1181: int h) {
1182: g.setColor(controlShadow);
1183: g.drawLine(x, y, x + w - 2, y);
1184: }
1185:
1186: protected void paintContentBorderBottomEdge(Graphics g,
1187: int tabPlacement, int selectedIndex, int x, int y, int w,
1188: int h) {
1189:
1190: g.setColor(controlShadow);
1191: g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
1192:
1193: g.setColor(Color.WHITE);
1194: Rectangle r = getTabBounds(tabPane, selectedIndex);
1195: g.drawLine(r.x + 1, y + h - 1, r.x + r.width - 2, y + h - 1);
1196:
1197: }
1198:
1199: protected void paintContentBorderLeftEdge(Graphics g,
1200: int tabPlacement, int selectedIndex, int x, int y, int w,
1201: int h) {
1202:
1203: g.setColor(controlShadow);
1204: g.drawLine(x, y, x, y + h - 2);
1205: }
1206:
1207: protected void paintContentBorderRightEdge(Graphics g,
1208: int tabPlacement, int selectedIndex, int x, int y, int w,
1209: int h) {
1210:
1211: g.setColor(controlShadow);
1212: g.drawLine(x + w - 1, y, x + w - 1, y + h - 1);
1213:
1214: }
1215:
1216: private boolean isLeftToRight(Component c) {
1217: return c.getComponentOrientation().isLeftToRight();
1218: }
1219:
1220: private void ensureCurrentLayout() {
1221: if (!tabPane.isValid()) {
1222: tabPane.validate();
1223: }
1224: /* If tabPane doesn't have a peer yet, the validate() call will
1225: * silently fail. We handle that by forcing a layout if tabPane
1226: * is still invalid. See bug 4237677.
1227: */
1228: if (!tabPane.isValid()) {
1229: TabbedPaneLayout layout = (TabbedPaneLayout) tabPane
1230: .getLayout();
1231: layout.calculateLayoutInfo();
1232: }
1233: }
1234:
1235: // TabbedPaneUI methods
1236:
1237: /**
1238: * Returns the bounds of the specified tab index. The bounds are
1239: * with respect to the JTabbedPane's coordinate space.
1240: */
1241: public Rectangle getTabBounds(JTabbedPane pane, int i) {
1242: ensureCurrentLayout();
1243: Rectangle tabRect = new Rectangle();
1244: return getTabBounds(i, tabRect);
1245: }
1246:
1247: public int getTabRunCount(JTabbedPane pane) {
1248: ensureCurrentLayout();
1249: return runCount;
1250: }
1251:
1252: /**
1253: * Returns the tab index which intersects the specified point
1254: * in the JTabbedPane's coordinate space.
1255: */
1256: public int tabForCoordinate(JTabbedPane pane, int x, int y) {
1257: ensureCurrentLayout();
1258: Point p = new Point(x, y);
1259:
1260: if (scrollableTabLayoutEnabled()) {
1261: translatePointToTabPanel(x, y, p);
1262: }
1263: int tabCount = tabPane.getTabCount();
1264: for (int i = 0; i < tabCount; i++) {
1265: if (rects[i].contains(p.x, p.y)) {
1266: return i;
1267: }
1268: }
1269: return -1;
1270: }
1271:
1272: /**
1273: * Returns the bounds of the specified tab in the coordinate space
1274: * of the JTabbedPane component. This is required because the tab rects
1275: * are by default defined in the coordinate space of the component where
1276: * they are rendered, which could be the JTabbedPane
1277: * (for WRAP_TAB_LAYOUT) or a ScrollableTabPanel (SCROLL_TAB_LAYOUT).
1278: * This method should be used whenever the tab rectangle must be relative
1279: * to the JTabbedPane itself and the result should be placed in a
1280: * designated Rectangle object (rather than instantiating and returning
1281: * a new Rectangle each time). The tab index parameter must be a valid
1282: * tabbed pane tab index (0 to tab count - 1, inclusive). The destination
1283: * rectangle parameter must be a valid <code>Rectangle</code> instance.
1284: * The handling of invalid parameters is unspecified.
1285: *
1286: * @param tabIndex the index of the tab
1287: * @param dest the rectangle where the result should be placed
1288: * @return the resulting rectangle
1289: *
1290: * @since 1.4
1291: */
1292: protected Rectangle getTabBounds(int tabIndex, Rectangle dest) {
1293: dest.width = rects[tabIndex].width;
1294: dest.height = rects[tabIndex].height;
1295:
1296: if (scrollableTabLayoutEnabled()) { // SCROLL_TAB_LAYOUT
1297: // Need to translate coordinates based on viewport location &
1298: // view position
1299: Point vpp = tabScroller.viewport.getLocation();
1300: Point viewp = tabScroller.viewport.getViewPosition();
1301: dest.x = rects[tabIndex].x + vpp.x - viewp.x;
1302: dest.y = rects[tabIndex].y + vpp.y - viewp.y;
1303:
1304: } else { // WRAP_TAB_LAYOUT
1305: dest.x = rects[tabIndex].x;
1306: dest.y = rects[tabIndex].y;
1307: }
1308: return dest;
1309: }
1310:
1311: /**
1312: * Returns the tab index which intersects the specified point
1313: * in the coordinate space of the component where the
1314: * tabs are actually rendered, which could be the JTabbedPane
1315: * (for WRAP_TAB_LAYOUT) or a ScrollableTabPanel (SCROLL_TAB_LAYOUT).
1316: */
1317: private int getTabAtLocation(int x, int y) {
1318: ensureCurrentLayout();
1319:
1320: int tabCount = tabPane.getTabCount();
1321: for (int i = 0; i < tabCount; i++) {
1322: if (rects[i].contains(x, y)) {
1323: return i;
1324: }
1325: }
1326: return -1;
1327: }
1328:
1329: /**
1330: * Returns the index of the tab closest to the passed in location, note
1331: * that the returned tab may not contain the location x,y.
1332: */
1333: private int getClosestTab(int x, int y) {
1334: int min = 0;
1335: int tabCount = Math.min(rects.length, tabPane.getTabCount());
1336: int max = tabCount;
1337: int tabPlacement = tabPane.getTabPlacement();
1338: boolean useX = (tabPlacement == TOP || tabPlacement == BOTTOM);
1339: int want = (useX) ? x : y;
1340:
1341: while (min != max) {
1342: int current = (max + min) / 2;
1343: int minLoc;
1344: int maxLoc;
1345:
1346: if (useX) {
1347: minLoc = rects[current].x;
1348: maxLoc = minLoc + rects[current].width;
1349: } else {
1350: minLoc = rects[current].y;
1351: maxLoc = minLoc + rects[current].height;
1352: }
1353: if (want < minLoc) {
1354: max = current;
1355: if (min == max) {
1356: return Math.max(0, current - 1);
1357: }
1358: } else if (want >= maxLoc) {
1359: min = current;
1360: if (max - min <= 1) {
1361: return Math.max(current + 1, tabCount - 1);
1362: }
1363: } else {
1364: return current;
1365: }
1366: }
1367: return min;
1368: }
1369:
1370: /**
1371: * Returns a point which is translated from the specified point in the
1372: * JTabbedPane's coordinate space to the coordinate space of the
1373: * ScrollableTabPanel. This is used for SCROLL_TAB_LAYOUT ONLY.
1374: */
1375: private Point translatePointToTabPanel(int srcx, int srcy,
1376: Point dest) {
1377: Point vpp = tabScroller.viewport.getLocation();
1378: Point viewp = tabScroller.viewport.getViewPosition();
1379: dest.x = srcx + vpp.x + viewp.x;
1380: dest.y = srcy + vpp.y + viewp.y;
1381: return dest;
1382: }
1383:
1384: // FlatTabbedPaneUI methods
1385:
1386: protected Component getVisibleComponent() {
1387: return visibleComponent;
1388: }
1389:
1390: protected void setVisibleComponent(Component component) {
1391: if (visibleComponent != null && visibleComponent != component
1392: && visibleComponent.getParent() == tabPane) {
1393: visibleComponent.setVisible(false);
1394: }
1395: if (component != null && !component.isVisible()) {
1396: component.setVisible(true);
1397: }
1398: visibleComponent = component;
1399: }
1400:
1401: protected void assureRectsCreated(int tabCount) {
1402: int rectArrayLen = rects.length;
1403: if (tabCount != rectArrayLen) {
1404: Rectangle[] tempRectArray = new Rectangle[tabCount];
1405: System.arraycopy(rects, 0, tempRectArray, 0, Math.min(
1406: rectArrayLen, tabCount));
1407: rects = tempRectArray;
1408: for (int rectIndex = rectArrayLen; rectIndex < tabCount; rectIndex++) {
1409: rects[rectIndex] = new Rectangle();
1410: }
1411: }
1412:
1413: }
1414:
1415: protected void expandTabRunsArray() {
1416: int rectLen = tabRuns.length;
1417: int[] newArray = new int[rectLen + 10];
1418: System.arraycopy(tabRuns, 0, newArray, 0, runCount);
1419: tabRuns = newArray;
1420: }
1421:
1422: protected int getRunForTab(int tabCount, int tabIndex) {
1423: for (int i = 0; i < runCount; i++) {
1424: int first = tabRuns[i];
1425: int last = lastTabInRun(tabCount, i);
1426: if (tabIndex >= first && tabIndex <= last) {
1427: return i;
1428: }
1429: }
1430: return 0;
1431: }
1432:
1433: protected int lastTabInRun(int tabCount, int run) {
1434: if (runCount == 1) {
1435: return tabCount - 1;
1436: }
1437: int nextRun = (run == runCount - 1 ? 0 : run + 1);
1438: if (tabRuns[nextRun] == 0) {
1439: return tabCount - 1;
1440: }
1441: return tabRuns[nextRun] - 1;
1442: }
1443:
1444: protected int getTabRunOverlay(int tabPlacement) {
1445: return tabRunOverlay;
1446: }
1447:
1448: protected int getTabRunIndent(int tabPlacement, int run) {
1449: return 0;
1450: }
1451:
1452: protected boolean shouldPadTabRun(int tabPlacement, int run) {
1453: return runCount > 1;
1454: }
1455:
1456: protected boolean shouldRotateTabRuns(int tabPlacement) {
1457: return true;
1458: }
1459:
1460: protected Icon getIconForTab(int tabIndex) {
1461: return (!tabPane.isEnabled() || !tabPane.isEnabledAt(tabIndex)) ? tabPane
1462: .getDisabledIconAt(tabIndex)
1463: : tabPane.getIconAt(tabIndex);
1464: }
1465:
1466: /**
1467: * Returns the text View object required to render stylized text (HTML) for
1468: * the specified tab or null if no specialized text rendering is needed
1469: * for this tab. This is provided to support html rendering inside tabs.
1470: *
1471: * @param tabIndex the index of the tab
1472: * @return the text view to render the tab's text or null if no
1473: * specialized rendering is required
1474: *
1475: * @since 1.4
1476: */
1477: protected View getTextViewForTab(int tabIndex) {
1478: if (htmlViews != null) {
1479: return (View) htmlViews.elementAt(tabIndex);
1480: }
1481: return null;
1482: }
1483:
1484: protected int calculateTabHeight(int tabPlacement, int tabIndex,
1485: int fontHeight) {
1486: int height = 0;
1487: View v = getTextViewForTab(tabIndex);
1488: if (v != null) {
1489: // html
1490: height += (int) v.getPreferredSpan(View.Y_AXIS);
1491: } else {
1492: // plain text
1493: height += fontHeight;
1494: }
1495: Icon icon = getIconForTab(tabIndex);
1496: Insets tabInsets = getTabInsets(tabPlacement, tabIndex);
1497:
1498: if (icon != null) {
1499: height = Math.max(height, icon.getIconHeight());
1500: }
1501: height += tabInsets.top + tabInsets.bottom + 2;
1502:
1503: return height;
1504: }
1505:
1506: protected int calculateMaxTabHeight(int tabPlacement) {
1507: FontMetrics metrics = getFontMetrics();
1508: int tabCount = tabPane.getTabCount();
1509: int result = 0;
1510: int fontHeight = metrics.getHeight();
1511: for (int i = 0; i < tabCount; i++) {
1512: result = Math.max(calculateTabHeight(tabPlacement, i,
1513: fontHeight), result);
1514: }
1515: return result;
1516: }
1517:
1518: protected int calculateTabWidth(int tabPlacement, int tabIndex,
1519: FontMetrics metrics) {
1520: Icon icon = getIconForTab(tabIndex);
1521: Insets tabInsets = getTabInsets(tabPlacement, tabIndex);
1522: int width = tabInsets.left + tabInsets.right + 3;
1523:
1524: if (icon != null) {
1525: width += icon.getIconWidth() + textIconGap;
1526: }
1527: View v = getTextViewForTab(tabIndex);
1528: if (v != null) {
1529: // html
1530: width += (int) v.getPreferredSpan(View.X_AXIS);
1531: } else {
1532: // plain text
1533: String title = tabPane.getTitleAt(tabIndex);
1534: width += SwingUtilities.computeStringWidth(metrics, title);
1535: }
1536:
1537: return width;
1538: }
1539:
1540: protected int calculateMaxTabWidth(int tabPlacement) {
1541: FontMetrics metrics = getFontMetrics();
1542: int tabCount = tabPane.getTabCount();
1543: int result = 0;
1544: for (int i = 0; i < tabCount; i++) {
1545: result = Math.max(calculateTabWidth(tabPlacement, i,
1546: metrics), result);
1547: }
1548: return result;
1549: }
1550:
1551: protected int calculateTabAreaHeight(int tabPlacement,
1552: int horizRunCount, int maxTabHeight) {
1553: Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
1554: int tabRunOverlay = getTabRunOverlay(tabPlacement);
1555: return (horizRunCount > 0 ? horizRunCount
1556: * (maxTabHeight - tabRunOverlay) + tabRunOverlay
1557: + tabAreaInsets.top + tabAreaInsets.bottom : 0);
1558: }
1559:
1560: protected int calculateTabAreaWidth(int tabPlacement,
1561: int vertRunCount, int maxTabWidth) {
1562: Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
1563: int tabRunOverlay = getTabRunOverlay(tabPlacement);
1564: return (vertRunCount > 0 ? vertRunCount
1565: * (maxTabWidth - tabRunOverlay) + tabRunOverlay
1566: + tabAreaInsets.left + tabAreaInsets.right : 0);
1567: }
1568:
1569: protected Insets getTabInsets(int tabPlacement, int tabIndex) {
1570: return tabInsets;
1571: }
1572:
1573: protected Insets getSelectedTabPadInsets(int tabPlacement) {
1574: rotateInsets(selectedTabPadInsets, currentPadInsets,
1575: tabPlacement);
1576: return currentPadInsets;
1577: }
1578:
1579: protected Insets getTabAreaInsets(int tabPlacement) {
1580: rotateInsets(tabAreaInsets, currentTabAreaInsets, tabPlacement);
1581: return currentTabAreaInsets;
1582: }
1583:
1584: protected Insets getContentBorderInsets(int tabPlacement) {
1585: return contentBorderInsets;
1586: }
1587:
1588: protected FontMetrics getFontMetrics() {
1589: Font font = tabPane.getFont();
1590: return tabPane.getFontMetrics(font);// Toolkit.getDefaultToolkit().getFontMetrics(font);
1591: }
1592:
1593: // Tab Navigation methods
1594:
1595: protected void navigateSelectedTab(int direction) {
1596: int tabPlacement = tabPane.getTabPlacement();
1597: int current = tabPane.getSelectedIndex();
1598: int tabCount = tabPane.getTabCount();
1599: boolean leftToRight = isLeftToRight(tabPane);
1600:
1601: // If we have no tabs then don't navigate.
1602: if (tabCount <= 0) {
1603: return;
1604: }
1605:
1606: int offset;
1607: switch (tabPlacement) {
1608: case NEXT:
1609: selectNextTab(current);
1610: break;
1611: case PREVIOUS:
1612: selectPreviousTab(current);
1613: break;
1614: case LEFT:
1615: case RIGHT:
1616: switch (direction) {
1617: case NORTH:
1618: selectPreviousTabInRun(current);
1619: break;
1620: case SOUTH:
1621: selectNextTabInRun(current);
1622: break;
1623: case WEST:
1624: offset = getTabRunOffset(tabPlacement, tabCount,
1625: current, false);
1626: selectAdjacentRunTab(tabPlacement, current, offset);
1627: break;
1628: case EAST:
1629: offset = getTabRunOffset(tabPlacement, tabCount,
1630: current, true);
1631: selectAdjacentRunTab(tabPlacement, current, offset);
1632: break;
1633: default:
1634: }
1635: break;
1636: case BOTTOM:
1637: case TOP:
1638: default:
1639: switch (direction) {
1640: case NORTH:
1641: offset = getTabRunOffset(tabPlacement, tabCount,
1642: current, false);
1643: selectAdjacentRunTab(tabPlacement, current, offset);
1644: break;
1645: case SOUTH:
1646: offset = getTabRunOffset(tabPlacement, tabCount,
1647: current, true);
1648: selectAdjacentRunTab(tabPlacement, current, offset);
1649: break;
1650: case EAST:
1651: if (leftToRight) {
1652: selectNextTabInRun(current);
1653: } else {
1654: selectPreviousTabInRun(current);
1655: }
1656: break;
1657: case WEST:
1658: if (leftToRight) {
1659: selectPreviousTabInRun(current);
1660: } else {
1661: selectNextTabInRun(current);
1662: }
1663: break;
1664: default:
1665: }
1666: }
1667: }
1668:
1669: protected void selectNextTabInRun(int current) {
1670: int tabCount = tabPane.getTabCount();
1671: int tabIndex = getNextTabIndexInRun(tabCount, current);
1672:
1673: while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
1674: tabIndex = getNextTabIndexInRun(tabCount, tabIndex);
1675: }
1676: tabPane.setSelectedIndex(tabIndex);
1677: }
1678:
1679: protected void selectPreviousTabInRun(int current) {
1680: int tabCount = tabPane.getTabCount();
1681: int tabIndex = getPreviousTabIndexInRun(tabCount, current);
1682:
1683: while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
1684: tabIndex = getPreviousTabIndexInRun(tabCount, tabIndex);
1685: }
1686: tabPane.setSelectedIndex(tabIndex);
1687: }
1688:
1689: protected void selectNextTab(int current) {
1690: int tabIndex = getNextTabIndex(current);
1691:
1692: while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
1693: tabIndex = getNextTabIndex(tabIndex);
1694: }
1695: tabPane.setSelectedIndex(tabIndex);
1696: }
1697:
1698: protected void selectPreviousTab(int current) {
1699: int tabIndex = getPreviousTabIndex(current);
1700:
1701: while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
1702: tabIndex = getPreviousTabIndex(tabIndex);
1703: }
1704: tabPane.setSelectedIndex(tabIndex);
1705: }
1706:
1707: protected void selectAdjacentRunTab(int tabPlacement, int tabIndex,
1708: int offset) {
1709: if (runCount < 2) {
1710: return;
1711: }
1712: int newIndex;
1713: Rectangle r = rects[tabIndex];
1714: switch (tabPlacement) {
1715: case LEFT:
1716: case RIGHT:
1717: newIndex = getTabAtLocation(r.x + r.width / 2 + offset, r.y
1718: + r.height / 2);
1719: break;
1720: case BOTTOM:
1721: case TOP:
1722: default:
1723: newIndex = getTabAtLocation(r.x + r.width / 2, r.y
1724: + r.height / 2 + offset);
1725: }
1726: if (newIndex != -1) {
1727: while (!tabPane.isEnabledAt(newIndex)
1728: && newIndex != tabIndex) {
1729: newIndex = getNextTabIndex(newIndex);
1730: }
1731: tabPane.setSelectedIndex(newIndex);
1732: }
1733: }
1734:
1735: protected int getTabRunOffset(int tabPlacement, int tabCount,
1736: int tabIndex, boolean forward) {
1737: int run = getRunForTab(tabCount, tabIndex);
1738: int offset;
1739: switch (tabPlacement) {
1740: case LEFT: {
1741: if (run == 0) {
1742: offset = (forward ? -(calculateTabAreaWidth(
1743: tabPlacement, runCount, maxTabWidth) - maxTabWidth)
1744: : -maxTabWidth);
1745:
1746: } else if (run == runCount - 1) {
1747: offset = (forward ? maxTabWidth
1748: : calculateTabAreaWidth(tabPlacement, runCount,
1749: maxTabWidth)
1750: - maxTabWidth);
1751: } else {
1752: offset = (forward ? maxTabWidth : -maxTabWidth);
1753: }
1754: break;
1755: }
1756: case RIGHT: {
1757: if (run == 0) {
1758: offset = (forward ? maxTabWidth
1759: : calculateTabAreaWidth(tabPlacement, runCount,
1760: maxTabWidth)
1761: - maxTabWidth);
1762: } else if (run == runCount - 1) {
1763: offset = (forward ? -(calculateTabAreaWidth(
1764: tabPlacement, runCount, maxTabWidth) - maxTabWidth)
1765: : -maxTabWidth);
1766: } else {
1767: offset = (forward ? maxTabWidth : -maxTabWidth);
1768: }
1769: break;
1770: }
1771: case BOTTOM: {
1772: if (run == 0) {
1773: offset = (forward ? maxTabHeight
1774: : calculateTabAreaHeight(tabPlacement,
1775: runCount, maxTabHeight)
1776: - maxTabHeight);
1777: } else if (run == runCount - 1) {
1778: offset = (forward ? -(calculateTabAreaHeight(
1779: tabPlacement, runCount, maxTabHeight) - maxTabHeight)
1780: : -maxTabHeight);
1781: } else {
1782: offset = (forward ? maxTabHeight : -maxTabHeight);
1783: }
1784: break;
1785: }
1786: case TOP:
1787: default: {
1788: if (run == 0) {
1789: offset = (forward ? -(calculateTabAreaHeight(
1790: tabPlacement, runCount, maxTabHeight) - maxTabHeight)
1791: : -maxTabHeight);
1792: } else if (run == runCount - 1) {
1793: offset = (forward ? maxTabHeight
1794: : calculateTabAreaHeight(tabPlacement,
1795: runCount, maxTabHeight)
1796: - maxTabHeight);
1797: } else {
1798: offset = (forward ? maxTabHeight : -maxTabHeight);
1799: }
1800: }
1801: }
1802: return offset;
1803: }
1804:
1805: protected int getPreviousTabIndex(int base) {
1806: int tabIndex = (base - 1 >= 0 ? base - 1 : tabPane
1807: .getTabCount() - 1);
1808: return (tabIndex >= 0 ? tabIndex : 0);
1809: }
1810:
1811: protected int getNextTabIndex(int base) {
1812: return (base + 1) % tabPane.getTabCount();
1813: }
1814:
1815: protected int getNextTabIndexInRun(int tabCount, int base) {
1816: if (runCount < 2) {
1817: return getNextTabIndex(base);
1818: }
1819: int currentRun = getRunForTab(tabCount, base);
1820: int next = getNextTabIndex(base);
1821: if (next == tabRuns[getNextTabRun(currentRun)]) {
1822: return tabRuns[currentRun];
1823: }
1824: return next;
1825: }
1826:
1827: protected int getPreviousTabIndexInRun(int tabCount, int base) {
1828: if (runCount < 2) {
1829: return getPreviousTabIndex(base);
1830: }
1831: int currentRun = getRunForTab(tabCount, base);
1832: if (base == tabRuns[currentRun]) {
1833: int previous = tabRuns[getNextTabRun(currentRun)] - 1;
1834: return (previous != -1 ? previous : tabCount - 1);
1835: }
1836: return getPreviousTabIndex(base);
1837: }
1838:
1839: protected int getPreviousTabRun(int baseRun) {
1840: int runIndex = (baseRun - 1 >= 0 ? baseRun - 1 : runCount - 1);
1841: return (runIndex >= 0 ? runIndex : 0);
1842: }
1843:
1844: protected int getNextTabRun(int baseRun) {
1845: return (baseRun + 1) % runCount;
1846: }
1847:
1848: protected static void rotateInsets(Insets topInsets,
1849: Insets targetInsets, int targetPlacement) {
1850:
1851: switch (targetPlacement) {
1852: case LEFT:
1853: targetInsets.top = topInsets.left;
1854: targetInsets.left = topInsets.top;
1855: targetInsets.bottom = topInsets.right;
1856: targetInsets.right = topInsets.bottom;
1857: break;
1858: case BOTTOM:
1859: targetInsets.top = topInsets.bottom;
1860: targetInsets.left = topInsets.left;
1861: targetInsets.bottom = topInsets.top;
1862: targetInsets.right = topInsets.right;
1863: break;
1864: case RIGHT:
1865: targetInsets.top = topInsets.left;
1866: targetInsets.left = topInsets.bottom;
1867: targetInsets.bottom = topInsets.right;
1868: targetInsets.right = topInsets.top;
1869: break;
1870: case TOP:
1871: default:
1872: targetInsets.top = topInsets.top;
1873: targetInsets.left = topInsets.left;
1874: targetInsets.bottom = topInsets.bottom;
1875: targetInsets.right = topInsets.right;
1876: }
1877: }
1878:
1879: // REMIND(aim,7/29/98): This method should be made
1880: // protected in the next release where
1881: // API changes are allowed
1882: //
1883: boolean requestFocusForVisibleComponent() {
1884: Component visibleComponent = getVisibleComponent();
1885:
1886: if (visibleComponent.isFocusable()) {
1887: visibleComponent.requestFocus();
1888: return true;
1889: }
1890:
1891: else if (visibleComponent instanceof JComponent) {
1892: JComponent jComponent = (JComponent) visibleComponent;
1893:
1894: //************* CHECK THIS **********************************
1895: if (jComponent.getFocusTraversalPolicy()
1896: .getDefaultComponent(jComponent)
1897: .requestFocusInWindow()) {
1898: return true;
1899: }
1900:
1901: // if (((JComponent)visibleComponent).requestDefaultFocus()) {
1902: // return true;
1903: // }
1904: }
1905:
1906: return false;
1907:
1908: }
1909:
1910: private static class RightAction extends AbstractAction {
1911: public void actionPerformed(ActionEvent e) {
1912: JTabbedPane pane = (JTabbedPane) e.getSource();
1913: FlatTabbedPaneUI ui = (FlatTabbedPaneUI) pane.getUI();
1914: ui.navigateSelectedTab(EAST);
1915: }
1916: };
1917:
1918: private static class LeftAction extends AbstractAction {
1919: public void actionPerformed(ActionEvent e) {
1920: JTabbedPane pane = (JTabbedPane) e.getSource();
1921: FlatTabbedPaneUI ui = (FlatTabbedPaneUI) pane.getUI();
1922: ui.navigateSelectedTab(WEST);
1923: }
1924: };
1925:
1926: private static class UpAction extends AbstractAction {
1927: public void actionPerformed(ActionEvent e) {
1928: JTabbedPane pane = (JTabbedPane) e.getSource();
1929: FlatTabbedPaneUI ui = (FlatTabbedPaneUI) pane.getUI();
1930: ui.navigateSelectedTab(NORTH);
1931: }
1932: };
1933:
1934: private static class DownAction extends AbstractAction {
1935: public void actionPerformed(ActionEvent e) {
1936: JTabbedPane pane = (JTabbedPane) e.getSource();
1937: FlatTabbedPaneUI ui = (FlatTabbedPaneUI) pane.getUI();
1938: ui.navigateSelectedTab(SOUTH);
1939: }
1940: };
1941:
1942: private static class NextAction extends AbstractAction {
1943: public void actionPerformed(ActionEvent e) {
1944: JTabbedPane pane = (JTabbedPane) e.getSource();
1945: FlatTabbedPaneUI ui = (FlatTabbedPaneUI) pane.getUI();
1946: ui.navigateSelectedTab(NEXT);
1947: }
1948: };
1949:
1950: private static class PreviousAction extends AbstractAction {
1951: public void actionPerformed(ActionEvent e) {
1952: JTabbedPane pane = (JTabbedPane) e.getSource();
1953: FlatTabbedPaneUI ui = (FlatTabbedPaneUI) pane.getUI();
1954: ui.navigateSelectedTab(PREVIOUS);
1955: }
1956: };
1957:
1958: private static class PageUpAction extends AbstractAction {
1959: public void actionPerformed(ActionEvent e) {
1960: JTabbedPane pane = (JTabbedPane) e.getSource();
1961: FlatTabbedPaneUI ui = (FlatTabbedPaneUI) pane.getUI();
1962: int tabPlacement = pane.getTabPlacement();
1963: if (tabPlacement == TOP || tabPlacement == BOTTOM) {
1964: ui.navigateSelectedTab(WEST);
1965: } else {
1966: ui.navigateSelectedTab(NORTH);
1967: }
1968: }
1969: };
1970:
1971: private static class PageDownAction extends AbstractAction {
1972: public void actionPerformed(ActionEvent e) {
1973: JTabbedPane pane = (JTabbedPane) e.getSource();
1974: FlatTabbedPaneUI ui = (FlatTabbedPaneUI) pane.getUI();
1975: int tabPlacement = pane.getTabPlacement();
1976: if (tabPlacement == TOP || tabPlacement == BOTTOM) {
1977: ui.navigateSelectedTab(EAST);
1978: } else {
1979: ui.navigateSelectedTab(SOUTH);
1980: }
1981: }
1982: };
1983:
1984: private static class RequestFocusAction extends AbstractAction {
1985: public void actionPerformed(ActionEvent e) {
1986: JTabbedPane pane = (JTabbedPane) e.getSource();
1987: pane.requestFocus();
1988: }
1989: };
1990:
1991: private static class RequestFocusForVisibleAction extends
1992: AbstractAction {
1993: public void actionPerformed(ActionEvent e) {
1994: JTabbedPane pane = (JTabbedPane) e.getSource();
1995: FlatTabbedPaneUI ui = (FlatTabbedPaneUI) pane.getUI();
1996: ui.requestFocusForVisibleComponent();
1997: }
1998: };
1999:
2000: /**
2001: * Selects a tab in the JTabbedPane based on the String of the
2002: * action command. The tab selected is based on the first tab that
2003: * has a mnemonic matching the first character of the action command.
2004: */
2005: private static class SetSelectedIndexAction extends AbstractAction {
2006: public void actionPerformed(ActionEvent e) {
2007: JTabbedPane pane = (JTabbedPane) e.getSource();
2008:
2009: if (pane != null
2010: && (pane.getUI() instanceof FlatTabbedPaneUI)) {
2011: FlatTabbedPaneUI ui = (FlatTabbedPaneUI) pane.getUI();
2012: String command = e.getActionCommand();
2013:
2014: if (command != null && command.length() > 0) {
2015: int mnemonic = (int) e.getActionCommand().charAt(0);
2016: if (mnemonic >= 'a' && mnemonic <= 'z') {
2017: mnemonic -= ('a' - 'A');
2018: }
2019: Integer index = (Integer) ui.mnemonicToIndexMap
2020: .get(new Integer(mnemonic));
2021: if (index != null
2022: && pane.isEnabledAt(index.intValue())) {
2023: pane.setSelectedIndex(index.intValue());
2024: }
2025: }
2026: }
2027: }
2028: };
2029:
2030: /*
2031: private static class FlatScrollTabsForwardAction extends AbstractAction {
2032: public void actionPerformed(ActionEvent e) {
2033: JTabbedPane pane = null;
2034: Object src = e.getSource();
2035: if (src instanceof JTabbedPane) {
2036: pane = (JTabbedPane)src;
2037: } else if (src instanceof ScrollableTabButton) {
2038: pane = (JTabbedPane)((ScrollableTabButton)src).getParent();
2039: } else {
2040: return; // shouldn't happen
2041: }
2042:
2043: //System.out.println("pane: "+pane.getClass().getName());
2044: //System.out.println("ui: "+(pane.getUI()).getClass().getName());
2045:
2046: FlatTabbedPaneUI ui = (FlatTabbedPaneUI)pane.getUI();
2047:
2048: if (ui.scrollableTabLayoutEnabled()) {
2049: ui.tabScroller.scrollForward(pane.getTabPlacement());
2050: }
2051: }
2052: }
2053:
2054: private static class FlatScrollTabsBackwardAction extends AbstractAction {
2055: public void actionPerformed(ActionEvent e) {
2056: JTabbedPane pane = null;
2057: Object src = e.getSource();
2058: if (src instanceof JTabbedPane) {
2059: pane = (JTabbedPane)src;
2060: } else if (src instanceof ScrollableTabButton) {
2061: pane = (JTabbedPane)((ScrollableTabButton)src).getParent();
2062: } else {
2063: return; // shouldn't happen
2064: }
2065: FlatTabbedPaneUI ui = (FlatTabbedPaneUI)pane.getUI();
2066:
2067:
2068:
2069: if (ui.scrollableTabLayoutEnabled()) {
2070: ui.tabScroller.scrollBackward(pane.getTabPlacement());
2071: }
2072: }
2073: }
2074: */
2075: /**
2076: * This inner class is marked "public" due to a compiler bug.
2077: * This class should be treated as a "protected" inner class.
2078: * Instantiate it only within subclasses of FlatTabbedPaneUI.
2079: */
2080: public class TabbedPaneLayout implements LayoutManager {
2081:
2082: public void addLayoutComponent(String name, Component comp) {
2083: }
2084:
2085: public void removeLayoutComponent(Component comp) {
2086: }
2087:
2088: public Dimension preferredLayoutSize(Container parent) {
2089: return calculateSize(false);
2090: }
2091:
2092: public Dimension minimumLayoutSize(Container parent) {
2093: return calculateSize(true);
2094: }
2095:
2096: protected Dimension calculateSize(boolean minimum) {
2097: int tabPlacement = tabPane.getTabPlacement();
2098: Insets insets = tabPane.getInsets();
2099: Insets contentInsets = getContentBorderInsets(tabPlacement);
2100: Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
2101:
2102: Dimension zeroSize = new Dimension(0, 0);
2103: int height = contentInsets.top + contentInsets.bottom;
2104: int width = contentInsets.left + contentInsets.right;
2105: int cWidth = 0;
2106: int cHeight = 0;
2107:
2108: // Determine minimum size required to display largest
2109: // child in each dimension
2110: //
2111: for (int i = 0; i < tabPane.getTabCount(); i++) {
2112: Component component = tabPane.getComponentAt(i);
2113: if (component != null) {
2114: Dimension size = zeroSize;
2115: size = minimum ? component.getMinimumSize()
2116: : component.getPreferredSize();
2117:
2118: if (size != null) {
2119: cHeight = Math.max(size.height, cHeight);
2120: cWidth = Math.max(size.width, cWidth);
2121: }
2122: }
2123: }
2124: // Add content border insets to minimum size
2125: width += cWidth;
2126: height += cHeight;
2127: int tabExtent = 0;
2128:
2129: // Calculate how much space the tabs will need, based on the
2130: // minimum size required to display largest child + content border
2131: //
2132: switch (tabPlacement) {
2133: case LEFT:
2134: case RIGHT:
2135: height = Math.max(height,
2136: calculateMaxTabHeight(tabPlacement)
2137: + tabAreaInsets.top
2138: + tabAreaInsets.bottom);
2139: tabExtent = preferredTabAreaWidth(tabPlacement, height);
2140: width += tabExtent;
2141: break;
2142: case TOP:
2143: case BOTTOM:
2144: default:
2145: width = Math.max(width,
2146: calculateMaxTabWidth(tabPlacement)
2147: + tabAreaInsets.left
2148: + tabAreaInsets.right);
2149: tabExtent = preferredTabAreaHeight(tabPlacement, width);
2150: height += tabExtent;
2151: }
2152: return new Dimension(width + insets.left + insets.right,
2153: height + insets.bottom + insets.top);
2154:
2155: }
2156:
2157: protected int preferredTabAreaHeight(int tabPlacement, int width) {
2158: FontMetrics metrics = getFontMetrics();
2159: int tabCount = tabPane.getTabCount();
2160: int total = 0;
2161: if (tabCount > 0) {
2162: int rows = 1;
2163: int x = 0;
2164:
2165: int maxTabHeight = calculateMaxTabHeight(tabPlacement);
2166:
2167: for (int i = 0; i < tabCount; i++) {
2168: int tabWidth = calculateTabWidth(tabPlacement, i,
2169: metrics);
2170:
2171: if (x != 0 && x + tabWidth > width) {
2172: rows++;
2173: x = 0;
2174: }
2175: x += tabWidth;
2176: }
2177: total = calculateTabAreaHeight(tabPlacement, rows,
2178: maxTabHeight);
2179: }
2180: return total;
2181: }
2182:
2183: protected int preferredTabAreaWidth(int tabPlacement, int height) {
2184: FontMetrics metrics = getFontMetrics();
2185: int tabCount = tabPane.getTabCount();
2186: int total = 0;
2187: if (tabCount > 0) {
2188: int columns = 1;
2189: int y = 0;
2190: int fontHeight = metrics.getHeight();
2191:
2192: maxTabWidth = calculateMaxTabWidth(tabPlacement);
2193:
2194: for (int i = 0; i < tabCount; i++) {
2195: int tabHeight = calculateTabHeight(tabPlacement, i,
2196: fontHeight);
2197:
2198: if (y != 0 && y + tabHeight > height) {
2199: columns++;
2200: y = 0;
2201: }
2202: y += tabHeight;
2203: }
2204: total = calculateTabAreaWidth(tabPlacement, columns,
2205: maxTabWidth);
2206: }
2207: return total;
2208: }
2209:
2210: public void layoutContainer(Container parent) {
2211: int tabPlacement = tabPane.getTabPlacement();
2212: Insets insets = tabPane.getInsets();
2213: int selectedIndex = tabPane.getSelectedIndex();
2214: Component visibleComponent = getVisibleComponent();
2215:
2216: calculateLayoutInfo();
2217:
2218: if (selectedIndex < 0) {
2219: if (visibleComponent != null) {
2220: // The last tab was removed, so remove the component
2221: setVisibleComponent(null);
2222: }
2223: } else {
2224: int cx, cy, cw, ch;
2225: int totalTabWidth = 0;
2226: int totalTabHeight = 0;
2227: Insets contentInsets = getContentBorderInsets(tabPlacement);
2228:
2229: Component selectedComponent = tabPane
2230: .getComponentAt(selectedIndex);
2231: boolean shouldChangeFocus = false;
2232:
2233: // In order to allow programs to use a single component
2234: // as the display for multiple tabs, we will not change
2235: // the visible compnent if the currently selected tab
2236: // has a null component. This is a bit dicey, as we don't
2237: // explicitly state we support this in the spec, but since
2238: // programs are now depending on this, we're making it work.
2239: //
2240: if (selectedComponent != null) {
2241: if (selectedComponent != visibleComponent
2242: && visibleComponent != null) {
2243:
2244: if (SwingUtilities
2245: .findFocusOwner(visibleComponent) != null) {
2246: shouldChangeFocus = true;
2247: }
2248: }
2249: setVisibleComponent(selectedComponent);
2250: }
2251:
2252: Rectangle bounds = tabPane.getBounds();
2253: int numChildren = tabPane.getComponentCount();
2254:
2255: if (numChildren > 0) {
2256:
2257: switch (tabPlacement) {
2258: case LEFT:
2259: totalTabWidth = calculateTabAreaWidth(
2260: tabPlacement, runCount, maxTabWidth);
2261: cx = insets.left + totalTabWidth
2262: + contentInsets.left;
2263: cy = insets.top + contentInsets.top;
2264: break;
2265: case RIGHT:
2266: totalTabWidth = calculateTabAreaWidth(
2267: tabPlacement, runCount, maxTabWidth);
2268: cx = insets.left + contentInsets.left;
2269: cy = insets.top + contentInsets.top;
2270: break;
2271: case BOTTOM:
2272: totalTabHeight = calculateTabAreaHeight(
2273: tabPlacement, runCount, maxTabHeight);
2274: cx = insets.left + contentInsets.left;
2275: cy = insets.top + contentInsets.top;
2276: break;
2277: case TOP:
2278: default:
2279: totalTabHeight = calculateTabAreaHeight(
2280: tabPlacement, runCount, maxTabHeight);
2281: cx = insets.left + contentInsets.left;
2282: cy = insets.top + totalTabHeight
2283: + contentInsets.top;
2284: }
2285:
2286: cw = bounds.width - totalTabWidth - insets.left
2287: - insets.right - contentInsets.left
2288: - contentInsets.right;
2289: ch = bounds.height - totalTabHeight - insets.top
2290: - insets.bottom - contentInsets.top
2291: - contentInsets.bottom;
2292:
2293: for (int i = 0; i < numChildren; i++) {
2294: Component child = tabPane.getComponent(i);
2295: child.setBounds(cx, cy, cw, ch);
2296: }
2297: }
2298:
2299: if (shouldChangeFocus) {
2300: if (!requestFocusForVisibleComponent()) {
2301: tabPane.requestFocus();
2302: }
2303: }
2304: }
2305: }
2306:
2307: public void calculateLayoutInfo() {
2308: int tabCount = tabPane.getTabCount();
2309: assureRectsCreated(tabCount);
2310: calculateTabRects(tabPane.getTabPlacement(), tabCount);
2311: }
2312:
2313: protected void calculateTabRects(int tabPlacement, int tabCount) {
2314: FontMetrics metrics = getFontMetrics();
2315: Dimension size = tabPane.getSize();
2316: Insets insets = tabPane.getInsets();
2317: Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
2318: int fontHeight = metrics.getHeight();
2319: int selectedIndex = tabPane.getSelectedIndex();
2320: int tabRunOverlay;
2321: int i, j;
2322: int x, y;
2323: int returnAt;
2324: boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
2325: boolean leftToRight = isLeftToRight(tabPane);
2326:
2327: //
2328: // Calculate bounds within which a tab run must fit
2329: //
2330: switch (tabPlacement) {
2331: case LEFT:
2332: maxTabWidth = calculateMaxTabWidth(tabPlacement);
2333: x = insets.left + tabAreaInsets.left;
2334: y = insets.top + tabAreaInsets.top;
2335: returnAt = size.height
2336: - (insets.bottom + tabAreaInsets.bottom);
2337: break;
2338: case RIGHT:
2339: maxTabWidth = calculateMaxTabWidth(tabPlacement);
2340: x = size.width - insets.right - tabAreaInsets.right
2341: - maxTabWidth;
2342: y = insets.top + tabAreaInsets.top;
2343: returnAt = size.height
2344: - (insets.bottom + tabAreaInsets.bottom);
2345: break;
2346: case BOTTOM:
2347: maxTabHeight = calculateMaxTabHeight(tabPlacement);
2348: x = insets.left + tabAreaInsets.left;
2349: y = size.height - insets.bottom - tabAreaInsets.bottom
2350: - maxTabHeight;
2351: returnAt = size.width
2352: - (insets.right + tabAreaInsets.right);
2353: break;
2354: case TOP:
2355: default:
2356: maxTabHeight = calculateMaxTabHeight(tabPlacement);
2357: x = insets.left + tabAreaInsets.left;
2358: y = insets.top + tabAreaInsets.top;
2359: returnAt = size.width
2360: - (insets.right + tabAreaInsets.right);
2361: break;
2362: }
2363:
2364: tabRunOverlay = getTabRunOverlay(tabPlacement);
2365:
2366: runCount = 0;
2367: selectedRun = -1;
2368:
2369: if (tabCount == 0) {
2370: return;
2371: }
2372:
2373: // Run through tabs and partition them into runs
2374: Rectangle rect;
2375: for (i = 0; i < tabCount; i++) {
2376: rect = rects[i];
2377:
2378: if (!verticalTabRuns) {
2379: // Tabs on TOP or BOTTOM....
2380: if (i > 0) {
2381: rect.x = rects[i - 1].x + rects[i - 1].width;
2382: } else {
2383: tabRuns[0] = 0;
2384: runCount = 1;
2385: maxTabWidth = 0;
2386: rect.x = x;
2387: }
2388: rect.width = calculateTabWidth(tabPlacement, i,
2389: metrics);
2390: maxTabWidth = Math.max(maxTabWidth, rect.width);
2391:
2392: // Never move a TAB down a run if it is in the first column.
2393: // Even if there isn't enough room, moving it to a fresh
2394: // line won't help.
2395: if (rect.x != 2 + insets.left
2396: && rect.x + rect.width > returnAt) {
2397: if (runCount > tabRuns.length - 1) {
2398: expandTabRunsArray();
2399: }
2400: tabRuns[runCount] = i;
2401: runCount++;
2402: rect.x = x;
2403: }
2404: // Initialize y position in case there's just one run
2405: rect.y = y;
2406: rect.height = maxTabHeight/* - 2*/;
2407:
2408: } else {
2409: // Tabs on LEFT or RIGHT...
2410: if (i > 0) {
2411: rect.y = rects[i - 1].y + rects[i - 1].height;
2412: } else {
2413: tabRuns[0] = 0;
2414: runCount = 1;
2415: maxTabHeight = 0;
2416: rect.y = y;
2417: }
2418: rect.height = calculateTabHeight(tabPlacement, i,
2419: fontHeight);
2420: maxTabHeight = Math.max(maxTabHeight, rect.height);
2421:
2422: // Never move a TAB over a run if it is in the first run.
2423: // Even if there isn't enough room, moving it to a fresh
2424: // column won't help.
2425: if (rect.y != 2 + insets.top
2426: && rect.y + rect.height > returnAt) {
2427: if (runCount > tabRuns.length - 1) {
2428: expandTabRunsArray();
2429: }
2430: tabRuns[runCount] = i;
2431: runCount++;
2432: rect.y = y;
2433: }
2434: // Initialize x position in case there's just one column
2435: rect.x = x;
2436: rect.width = maxTabWidth/* - 2*/;
2437:
2438: }
2439: if (i == selectedIndex) {
2440: selectedRun = runCount - 1;
2441: }
2442: }
2443:
2444: if (runCount > 1) {
2445: // Re-distribute tabs in case last run has leftover space
2446: normalizeTabRuns(tabPlacement, tabCount,
2447: verticalTabRuns ? y : x, returnAt);
2448:
2449: selectedRun = getRunForTab(tabCount, selectedIndex);
2450:
2451: // Rotate run array so that selected run is first
2452: if (shouldRotateTabRuns(tabPlacement)) {
2453: rotateTabRuns(tabPlacement, selectedRun);
2454: }
2455: }
2456:
2457: // Step through runs from back to front to calculate
2458: // tab y locations and to pad runs appropriately
2459: for (i = runCount - 1; i >= 0; i--) {
2460: int start = tabRuns[i];
2461: int next = tabRuns[i == (runCount - 1) ? 0 : i + 1];
2462: int end = (next != 0 ? next - 1 : tabCount - 1);
2463: if (!verticalTabRuns) {
2464: for (j = start; j <= end; j++) {
2465: rect = rects[j];
2466: rect.y = y;
2467: rect.x += getTabRunIndent(tabPlacement, i);
2468: }
2469: if (shouldPadTabRun(tabPlacement, i)) {
2470: padTabRun(tabPlacement, start, end, returnAt);
2471: }
2472: if (tabPlacement == BOTTOM) {
2473: y -= (maxTabHeight - tabRunOverlay);
2474: } else {
2475: y += (maxTabHeight - tabRunOverlay);
2476: }
2477: } else {
2478: for (j = start; j <= end; j++) {
2479: rect = rects[j];
2480: rect.x = x;
2481: rect.y += getTabRunIndent(tabPlacement, i);
2482: }
2483: if (shouldPadTabRun(tabPlacement, i)) {
2484: padTabRun(tabPlacement, start, end, returnAt);
2485: }
2486: if (tabPlacement == RIGHT) {
2487: x -= (maxTabWidth - tabRunOverlay);
2488: } else {
2489: x += (maxTabWidth - tabRunOverlay);
2490: }
2491: }
2492: }
2493:
2494: // Pad the selected tab so that it appears raised in front
2495: padSelectedTab(tabPlacement, selectedIndex);
2496:
2497: // if right to left and tab placement on the top or
2498: // the bottom, flip x positions and adjust by widths
2499: if (!leftToRight && !verticalTabRuns) {
2500: int rightMargin = size.width
2501: - (insets.right + tabAreaInsets.right);
2502: for (i = 0; i < tabCount; i++) {
2503: rects[i].x = rightMargin - rects[i].x
2504: - rects[i].width;
2505: }
2506: }
2507: }
2508:
2509: /*
2510: * Rotates the run-index array so that the selected run is run[0]
2511: */
2512: protected void rotateTabRuns(int tabPlacement, int selectedRun) {
2513: for (int i = 0; i < selectedRun; i++) {
2514: int save = tabRuns[0];
2515: for (int j = 1; j < runCount; j++) {
2516: tabRuns[j - 1] = tabRuns[j];
2517: }
2518: tabRuns[runCount - 1] = save;
2519: }
2520: }
2521:
2522: protected void normalizeTabRuns(int tabPlacement, int tabCount,
2523: int start, int max) {
2524: boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
2525: int run = runCount - 1;
2526: boolean keepAdjusting = true;
2527: double weight = 1.25;
2528:
2529: // At this point the tab runs are packed to fit as many
2530: // tabs as possible, which can leave the last run with a lot
2531: // of extra space (resulting in very fat tabs on the last run).
2532: // So we'll attempt to distribute this extra space more evenly
2533: // across the runs in order to make the runs look more consistent.
2534: //
2535: // Starting with the last run, determine whether the last tab in
2536: // the previous run would fit (generously) in this run; if so,
2537: // move tab to current run and shift tabs accordingly. Cycle
2538: // through remaining runs using the same algorithm.
2539: //
2540: while (keepAdjusting) {
2541: int last = lastTabInRun(tabCount, run);
2542: int prevLast = lastTabInRun(tabCount, run - 1);
2543: int end;
2544: int prevLastLen;
2545:
2546: if (!verticalTabRuns) {
2547: end = rects[last].x + rects[last].width;
2548: prevLastLen = (int) (maxTabWidth * weight);
2549: } else {
2550: end = rects[last].y + rects[last].height;
2551: prevLastLen = (int) (maxTabHeight * weight * 2);
2552: }
2553:
2554: // Check if the run has enough extra space to fit the last tab
2555: // from the previous row...
2556: if (max - end > prevLastLen) {
2557:
2558: // Insert tab from previous row and shift rest over
2559: tabRuns[run] = prevLast;
2560: if (!verticalTabRuns) {
2561: rects[prevLast].x = start;
2562: } else {
2563: rects[prevLast].y = start;
2564: }
2565: for (int i = prevLast + 1; i <= last; i++) {
2566: if (!verticalTabRuns) {
2567: rects[i].x = rects[i - 1].x
2568: + rects[i - 1].width;
2569: } else {
2570: rects[i].y = rects[i - 1].y
2571: + rects[i - 1].height;
2572: }
2573: }
2574:
2575: } else if (run == runCount - 1) {
2576: // no more room left in last run, so we're done!
2577: keepAdjusting = false;
2578: }
2579: if (run - 1 > 0) {
2580: // check previous run next...
2581: run -= 1;
2582: } else {
2583: // check last run again...but require a higher ratio
2584: // of extraspace-to-tabsize because we don't want to
2585: // end up with too many tabs on the last run!
2586: run = runCount - 1;
2587: weight += .25;
2588: }
2589: }
2590: }
2591:
2592: protected void padTabRun(int tabPlacement, int start, int end,
2593: int max) {
2594: Rectangle lastRect = rects[end];
2595: if (tabPlacement == TOP || tabPlacement == BOTTOM) {
2596: int runWidth = (lastRect.x + lastRect.width)
2597: - rects[start].x;
2598: int deltaWidth = max - (lastRect.x + lastRect.width);
2599: float factor = (float) deltaWidth / (float) runWidth;
2600:
2601: for (int j = start; j <= end; j++) {
2602: Rectangle pastRect = rects[j];
2603: if (j > start) {
2604: pastRect.x = rects[j - 1].x
2605: + rects[j - 1].width;
2606: }
2607: pastRect.width += Math.round((float) pastRect.width
2608: * factor);
2609: }
2610: lastRect.width = max - lastRect.x;
2611: } else {
2612: int runHeight = (lastRect.y + lastRect.height)
2613: - rects[start].y;
2614: int deltaHeight = max - (lastRect.y + lastRect.height);
2615: float factor = (float) deltaHeight / (float) runHeight;
2616:
2617: for (int j = start; j <= end; j++) {
2618: Rectangle pastRect = rects[j];
2619: if (j > start) {
2620: pastRect.y = rects[j - 1].y
2621: + rects[j - 1].height;
2622: }
2623: pastRect.height += Math
2624: .round((float) pastRect.height * factor);
2625: }
2626: lastRect.height = max - lastRect.y;
2627: }
2628: }
2629:
2630: protected void padSelectedTab(int tabPlacement,
2631: int selectedIndex) {
2632:
2633: if (selectedIndex >= 0) {
2634: Rectangle selRect = rects[selectedIndex];
2635: Insets padInsets = getSelectedTabPadInsets(tabPlacement);
2636: selRect.x -= padInsets.left;
2637: selRect.width += (padInsets.left + padInsets.right);
2638: selRect.y -= padInsets.top;
2639: selRect.height += (padInsets.top + padInsets.bottom);
2640: }
2641: }
2642: }
2643:
2644: private class TabbedPaneScrollLayout extends TabbedPaneLayout {
2645:
2646: protected int preferredTabAreaHeight(int tabPlacement, int width) {
2647: return calculateMaxTabHeight(tabPlacement);
2648: }
2649:
2650: protected int preferredTabAreaWidth(int tabPlacement, int height) {
2651: return calculateMaxTabWidth(tabPlacement);
2652: }
2653:
2654: public void layoutContainer(Container parent) {
2655: int tabPlacement = tabPane.getTabPlacement();
2656: int tabCount = tabPane.getTabCount();
2657: Insets insets = tabPane.getInsets();
2658: int selectedIndex = tabPane.getSelectedIndex();
2659: Component visibleComponent = getVisibleComponent();
2660:
2661: calculateLayoutInfo();
2662:
2663: if (selectedIndex < 0) {
2664: if (visibleComponent != null) {
2665: // The last tab was removed, so remove the component
2666: setVisibleComponent(null);
2667: }
2668: } else {
2669: Component selectedComponent = tabPane
2670: .getComponentAt(selectedIndex);
2671: boolean shouldChangeFocus = false;
2672:
2673: // In order to allow programs to use a single component
2674: // as the display for multiple tabs, we will not change
2675: // the visible compnent if the currently selected tab
2676: // has a null component. This is a bit dicey, as we don't
2677: // explicitly state we support this in the spec, but since
2678: // programs are now depending on this, we're making it work.
2679: //
2680: if (selectedComponent != null) {
2681: if (selectedComponent != visibleComponent
2682: && visibleComponent != null) {
2683: if (SwingUtilities
2684: .findFocusOwner(visibleComponent) != null) {
2685: shouldChangeFocus = true;
2686: }
2687: }
2688: setVisibleComponent(selectedComponent);
2689: }
2690: int tx, ty, tw, th; // tab area bounds
2691: int cx, cy, cw, ch; // content area bounds
2692: Insets contentInsets = getContentBorderInsets(tabPlacement);
2693: Rectangle bounds = tabPane.getBounds();
2694: int numChildren = tabPane.getComponentCount();
2695:
2696: if (numChildren > 0) {
2697: switch (tabPlacement) {
2698: case LEFT:
2699: // calculate tab area bounds
2700: tw = calculateTabAreaWidth(tabPlacement,
2701: runCount, maxTabWidth);
2702: th = bounds.height - insets.top - insets.bottom;
2703: tx = insets.left;
2704: ty = insets.top;
2705:
2706: // calculate content area bounds
2707: cx = tx + tw + contentInsets.left;
2708: cy = ty + contentInsets.top;
2709: cw = bounds.width - insets.left - insets.right
2710: - tw - contentInsets.left
2711: - contentInsets.right;
2712: ch = bounds.height - insets.top - insets.bottom
2713: - contentInsets.top
2714: - contentInsets.bottom;
2715: break;
2716: case RIGHT:
2717: // calculate tab area bounds
2718: tw = calculateTabAreaWidth(tabPlacement,
2719: runCount, maxTabWidth);
2720: th = bounds.height - insets.top - insets.bottom;
2721: tx = bounds.width - insets.right - tw;
2722: ty = insets.top;
2723:
2724: // calculate content area bounds
2725: cx = insets.left + contentInsets.left;
2726: cy = insets.top + contentInsets.top;
2727: cw = bounds.width - insets.left - insets.right
2728: - tw - contentInsets.left
2729: - contentInsets.right;
2730: ch = bounds.height - insets.top - insets.bottom
2731: - contentInsets.top
2732: - contentInsets.bottom;
2733: break;
2734: case BOTTOM:
2735: // calculate tab area bounds
2736: tw = bounds.width - insets.left - insets.right;
2737: th = calculateTabAreaHeight(tabPlacement,
2738: runCount, maxTabHeight);
2739: tx = insets.left;
2740: ty = bounds.height - insets.bottom - th;
2741:
2742: // calculate content area bounds
2743: cx = insets.left + contentInsets.left;
2744: cy = insets.top + contentInsets.top;
2745: cw = bounds.width - insets.left - insets.right
2746: - contentInsets.left
2747: - contentInsets.right;
2748: ch = bounds.height - insets.top - insets.bottom
2749: - th - contentInsets.top
2750: - contentInsets.bottom;
2751: break;
2752: case TOP:
2753: default:
2754: // calculate tab area bounds
2755: tw = bounds.width - insets.left - insets.right;
2756: th = calculateTabAreaHeight(tabPlacement,
2757: runCount, maxTabHeight);
2758: tx = insets.left;
2759: ty = insets.top;
2760:
2761: // calculate content area bounds
2762: cx = tx + contentInsets.left;
2763: cy = ty + th + contentInsets.top;
2764: cw = bounds.width - insets.left - insets.right
2765: - contentInsets.left
2766: - contentInsets.right;
2767: ch = bounds.height - insets.top - insets.bottom
2768: - th - contentInsets.top
2769: - contentInsets.bottom;
2770: }
2771:
2772: for (int i = 0; i < numChildren; i++) {
2773: Component child = tabPane.getComponent(i);
2774:
2775: if (child instanceof ScrollableTabViewport) {
2776: JViewport viewport = (JViewport) child;
2777: Rectangle viewRect = viewport.getViewRect();
2778: int vw = tw;
2779: int vh = th;
2780: switch (tabPlacement) {
2781: case LEFT:
2782: case RIGHT:
2783: int totalTabHeight = rects[tabCount - 1].y
2784: + rects[tabCount - 1].height;
2785: if (totalTabHeight > th) {
2786: // Allow space for scrollbuttons
2787: vh = Math.max(th - 36, 36);
2788: if (totalTabHeight - viewRect.y <= vh) {
2789: // Scrolled to the end, so ensure the viewport size is
2790: // such that the scroll offset aligns with a tab
2791: vh = totalTabHeight
2792: - viewRect.y;
2793: }
2794: }
2795: break;
2796: case BOTTOM:
2797: case TOP:
2798: default:
2799: int totalTabWidth = rects[tabCount - 1].x
2800: + rects[tabCount - 1].width;
2801: if (totalTabWidth > tw) {
2802: // Need to allow space for scrollbuttons
2803: vw = Math.max(tw - 36, 36);
2804: ;
2805: if (totalTabWidth - viewRect.x <= vw) {
2806: // Scrolled to the end, so ensure the viewport size is
2807: // such that the scroll offset aligns with a tab
2808: vw = totalTabWidth - viewRect.x;
2809: }
2810: }
2811: }
2812: child.setBounds(tx, ty, vw, vh);
2813:
2814: } else if (child instanceof ScrollableTabButton) {
2815: ScrollableTabButton scrollbutton = (ScrollableTabButton) child;
2816: Dimension bsize = scrollbutton
2817: .getPreferredSize();
2818: int bx = 0;
2819: int by = 0;
2820: int bw = bsize.width;
2821: int bh = bsize.height;
2822: boolean visible = false;
2823:
2824: switch (tabPlacement) {
2825: case LEFT:
2826: case RIGHT:
2827: int totalTabHeight = rects[tabCount - 1].y
2828: + rects[tabCount - 1].height;
2829: if (totalTabHeight > th) {
2830: int dir = scrollbutton
2831: .scrollsForward() ? SOUTH
2832: : NORTH;
2833: scrollbutton.setDirection(dir);
2834: visible = true;
2835: bx = (tabPlacement == LEFT ? tx
2836: + tw - bsize.width : tx);
2837: by = dir == SOUTH ? bounds.height
2838: - insets.bottom
2839: - bsize.height
2840: : bounds.height
2841: - insets.bottom - 2
2842: * bsize.height;
2843: }
2844: break;
2845:
2846: case BOTTOM:
2847: case TOP:
2848: default:
2849: int totalTabWidth = rects[tabCount - 1].x
2850: + rects[tabCount - 1].width;
2851:
2852: if (totalTabWidth > tw) {
2853: int dir = scrollbutton
2854: .scrollsForward() ? EAST
2855: : WEST;
2856: scrollbutton.setDirection(dir);
2857: visible = true;
2858: bx = dir == EAST ? bounds.width
2859: - insets.left - bsize.width
2860: : bounds.width
2861: - insets.left - 2
2862: * bsize.width;
2863: by = (tabPlacement == TOP ? ty + th
2864: - bsize.height : ty);
2865: }
2866: }
2867: child.setVisible(visible);
2868: if (visible) {
2869: child.setBounds(bx, by, bw, bh);
2870: }
2871:
2872: } else {
2873: // All content children...
2874: child.setBounds(cx, cy, cw, ch);
2875: }
2876: }
2877: if (shouldChangeFocus) {
2878: if (!requestFocusForVisibleComponent()) {
2879: tabPane.requestFocus();
2880: }
2881: }
2882: }
2883: }
2884: }
2885:
2886: protected void calculateTabRects(int tabPlacement, int tabCount) {
2887: FontMetrics metrics = getFontMetrics();
2888: Dimension size = tabPane.getSize();
2889: Insets insets = tabPane.getInsets();
2890: Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
2891: int fontHeight = metrics.getHeight();
2892: int selectedIndex = tabPane.getSelectedIndex();
2893: int i, j;
2894: boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
2895: boolean leftToRight = isLeftToRight(tabPane);
2896: int x = tabAreaInsets.left;
2897: int y = tabAreaInsets.top;
2898: int totalWidth = 0;
2899: int totalHeight = 0;
2900:
2901: //
2902: // Calculate bounds within which a tab run must fit
2903: //
2904: switch (tabPlacement) {
2905: case LEFT:
2906: case RIGHT:
2907: maxTabWidth = calculateMaxTabWidth(tabPlacement);
2908: break;
2909: case BOTTOM:
2910: case TOP:
2911: default:
2912: maxTabHeight = calculateMaxTabHeight(tabPlacement);
2913: }
2914:
2915: runCount = 0;
2916: selectedRun = -1;
2917:
2918: if (tabCount == 0) {
2919: return;
2920: }
2921:
2922: selectedRun = 0;
2923: runCount = 1;
2924:
2925: // Run through tabs and lay them out in a single run
2926: Rectangle rect;
2927: for (i = 0; i < tabCount; i++) {
2928: rect = rects[i];
2929:
2930: if (!verticalTabRuns) {
2931: // Tabs on TOP or BOTTOM....
2932: if (i > 0) {
2933: rect.x = rects[i - 1].x + rects[i - 1].width;
2934: } else {
2935: tabRuns[0] = 0;
2936: maxTabWidth = 0;
2937: totalHeight += maxTabHeight;
2938: rect.x = x;
2939: }
2940: rect.width = calculateTabWidth(tabPlacement, i,
2941: metrics);
2942: totalWidth = rect.x + rect.width;
2943: maxTabWidth = Math.max(maxTabWidth, rect.width);
2944:
2945: rect.y = y;
2946: rect.height = maxTabHeight/* - 2*/;
2947:
2948: } else {
2949: // Tabs on LEFT or RIGHT...
2950: if (i > 0) {
2951: rect.y = rects[i - 1].y + rects[i - 1].height;
2952: } else {
2953: tabRuns[0] = 0;
2954: maxTabHeight = 0;
2955: totalWidth = maxTabWidth;
2956: rect.y = y;
2957: }
2958: rect.height = calculateTabHeight(tabPlacement, i,
2959: fontHeight);
2960: totalHeight = rect.y + rect.height;
2961: maxTabHeight = Math.max(maxTabHeight, rect.height);
2962:
2963: rect.x = x;
2964: rect.width = maxTabWidth/* - 2*/;
2965:
2966: }
2967: }
2968:
2969: // for right to left and tab placement on the top or
2970: // the bottom, flip x positions and adjust by widths
2971: if (!leftToRight && !verticalTabRuns) {
2972: int rightMargin = size.width
2973: - (insets.right + tabAreaInsets.right);
2974: for (i = 0; i < tabCount; i++) {
2975: rects[i].x = rightMargin - rects[i].x
2976: - rects[i].width;
2977: }
2978: }
2979: //tabPanel.setSize(totalWidth, totalHeight);
2980: tabScroller.tabPanel.setPreferredSize(new Dimension(
2981: totalWidth, totalHeight));
2982: }
2983: }
2984:
2985: private class ScrollableTabSupport implements ChangeListener,
2986: ActionListener {
2987: public ScrollableTabViewport viewport;
2988: public ScrollableTabPanel tabPanel;
2989: public ScrollableTabButton scrollForwardButton;
2990: public ScrollableTabButton scrollBackwardButton;
2991: public int leadingTabIndex;
2992:
2993: private Point tabViewPosition = new Point(0, 0);
2994:
2995: ScrollableTabSupport(int tabPlacement) {
2996: viewport = new ScrollableTabViewport();
2997: tabPanel = new ScrollableTabPanel();
2998: viewport.setView(tabPanel);
2999: viewport.addChangeListener(this );
3000:
3001: if (tabPlacement == TOP || tabPlacement == BOTTOM) {
3002: scrollForwardButton = new ScrollableTabButton(EAST);
3003: scrollBackwardButton = new ScrollableTabButton(WEST);
3004:
3005: } else { // tabPlacement = LEFT || RIGHT
3006: scrollForwardButton = new ScrollableTabButton(SOUTH);
3007: scrollBackwardButton = new ScrollableTabButton(NORTH);
3008: }
3009:
3010: scrollForwardButton.addActionListener(this );
3011: scrollBackwardButton.addActionListener(this );
3012:
3013: }
3014:
3015: public void actionPerformed(ActionEvent e) {
3016: Object object = e.getSource();
3017: if (object == scrollForwardButton) {
3018: scrollForward(tabPane.getTabPlacement());
3019: } else if (object == scrollBackwardButton) {
3020: scrollBackward(tabPane.getTabPlacement());
3021: }
3022: }
3023:
3024: public void scrollForward(int tabPlacement) {
3025: Dimension viewSize = viewport.getViewSize();
3026: Rectangle viewRect = viewport.getViewRect();
3027:
3028: if (tabPlacement == TOP || tabPlacement == BOTTOM) {
3029: if (viewRect.width >= viewSize.width - viewRect.x) {
3030: return; // no room left to scroll
3031: }
3032: } else { // tabPlacement == LEFT || tabPlacement == RIGHT
3033: if (viewRect.height >= viewSize.height - viewRect.y) {
3034: return;
3035: }
3036: }
3037: setLeadingTabIndex(tabPlacement, leadingTabIndex + 1);
3038: }
3039:
3040: public void scrollBackward(int tabPlacement) {
3041: if (leadingTabIndex == 0) {
3042: return; // no room left to scroll
3043: }
3044: setLeadingTabIndex(tabPlacement, leadingTabIndex - 1);
3045: }
3046:
3047: public void setLeadingTabIndex(int tabPlacement, int index) {
3048: leadingTabIndex = index;
3049: Dimension viewSize = viewport.getViewSize();
3050: Rectangle viewRect = viewport.getViewRect();
3051:
3052: switch (tabPlacement) {
3053: case TOP:
3054: case BOTTOM:
3055: tabViewPosition.x = leadingTabIndex == 0 ? 0
3056: : rects[leadingTabIndex].x;
3057:
3058: if ((viewSize.width - tabViewPosition.x) < viewRect.width) {
3059: // We've scrolled to the end, so adjust the viewport size
3060: // to ensure the view position remains aligned on a tab boundary
3061: Dimension extentSize = new Dimension(viewSize.width
3062: - tabViewPosition.x, viewRect.height);
3063: viewport.setExtentSize(extentSize);
3064: }
3065: break;
3066: case LEFT:
3067: case RIGHT:
3068: tabViewPosition.y = leadingTabIndex == 0 ? 0
3069: : rects[leadingTabIndex].y;
3070:
3071: if ((viewSize.height - tabViewPosition.y) < viewRect.height) {
3072: // We've scrolled to the end, so adjust the viewport size
3073: // to ensure the view position remains aligned on a tab boundary
3074: Dimension extentSize = new Dimension(
3075: viewRect.width, viewSize.height
3076: - tabViewPosition.y);
3077: viewport.setExtentSize(extentSize);
3078: }
3079: }
3080: viewport.setViewPosition(tabViewPosition);
3081: }
3082:
3083: public void stateChanged(ChangeEvent e) {
3084: JViewport viewport = (JViewport) e.getSource();
3085: int tabPlacement = tabPane.getTabPlacement();
3086: int tabCount = tabPane.getTabCount();
3087: Rectangle vpRect = viewport.getBounds();
3088: Dimension viewSize = viewport.getViewSize();
3089: Rectangle viewRect = viewport.getViewRect();
3090:
3091: leadingTabIndex = getClosestTab(viewRect.x, viewRect.y);
3092:
3093: // If the tab isn't right aligned, adjust it.
3094: if (leadingTabIndex + 1 < tabCount) {
3095: switch (tabPlacement) {
3096: case TOP:
3097: case BOTTOM:
3098: if (rects[leadingTabIndex].x < viewRect.x) {
3099: leadingTabIndex++;
3100: }
3101: break;
3102: case LEFT:
3103: case RIGHT:
3104: if (rects[leadingTabIndex].y < viewRect.y) {
3105: leadingTabIndex++;
3106: }
3107: break;
3108: }
3109: }
3110: Insets contentInsets = getContentBorderInsets(tabPlacement);
3111: switch (tabPlacement) {
3112: case LEFT:
3113: tabPane.repaint(vpRect.x + vpRect.width, vpRect.y,
3114: contentInsets.left, vpRect.height);
3115: scrollBackwardButton.setEnabled(viewRect.y > 0);
3116: scrollForwardButton
3117: .setEnabled(leadingTabIndex < tabCount - 1
3118: && viewSize.height - viewRect.y > viewRect.height);
3119: break;
3120: case RIGHT:
3121: tabPane.repaint(vpRect.x - contentInsets.right,
3122: vpRect.y, contentInsets.right, vpRect.height);
3123: scrollBackwardButton.setEnabled(viewRect.y > 0);
3124: scrollForwardButton
3125: .setEnabled(leadingTabIndex < tabCount - 1
3126: && viewSize.height - viewRect.y > viewRect.height);
3127: break;
3128: case BOTTOM:
3129: tabPane.repaint(vpRect.x, vpRect.y
3130: - contentInsets.bottom, vpRect.width,
3131: contentInsets.bottom);
3132: scrollBackwardButton.setEnabled(viewRect.x > 0);
3133: scrollForwardButton
3134: .setEnabled(leadingTabIndex < tabCount - 1
3135: && viewSize.width - viewRect.x > viewRect.width);
3136: break;
3137: case TOP:
3138: default:
3139: tabPane.repaint(vpRect.x, vpRect.y + vpRect.height,
3140: vpRect.width, contentInsets.top);
3141: scrollBackwardButton.setEnabled(viewRect.x > 0);
3142: scrollForwardButton
3143: .setEnabled(leadingTabIndex < tabCount - 1
3144: && viewSize.width - viewRect.x > viewRect.width);
3145: }
3146: }
3147:
3148: public String toString() {
3149: return new String("viewport.viewSize="
3150: + viewport.getViewSize() + "\n"
3151: + "viewport.viewRectangle="
3152: + viewport.getViewRect() + "\n"
3153: + "leadingTabIndex=" + leadingTabIndex + "\n"
3154: + "tabViewPosition=" + tabViewPosition);
3155: }
3156:
3157: }
3158:
3159: private class ScrollableTabViewport extends JViewport implements
3160: UIResource {
3161: public ScrollableTabViewport() {
3162: super ();
3163: setScrollMode(SIMPLE_SCROLL_MODE);
3164: }
3165: } // class ScrollableTabViewport
3166:
3167: private class ScrollableTabPanel extends JPanel implements
3168: UIResource {
3169: public ScrollableTabPanel() {
3170: setLayout(null);
3171: }
3172:
3173: public void paintComponent(Graphics g) {
3174: super .paintComponent(g);
3175: FlatTabbedPaneUI.this .paintTabArea(g, tabPane
3176: .getTabPlacement(), tabPane.getSelectedIndex());
3177:
3178: }
3179: } // class ScrollableTabPanel
3180:
3181: private class ScrollableTabButton extends BasicArrowButton
3182: implements UIResource, SwingConstants, MouseListener {
3183: private boolean mouseOver = false;
3184:
3185: public ScrollableTabButton(int direction) {
3186: super (direction);
3187: addMouseListener(this );
3188: }
3189:
3190: public boolean scrollsForward() {
3191: return direction == EAST || direction == SOUTH;
3192: }
3193:
3194: public void mouseEntered(MouseEvent e) {
3195: mouseOver = true;
3196: repaint();
3197: }
3198:
3199: public void mouseExited(MouseEvent e) {
3200: mouseOver = false;
3201: repaint();
3202: }
3203:
3204: public void mouseClicked(MouseEvent e) {
3205: }
3206:
3207: public void mousePressed(MouseEvent e) {
3208: }
3209:
3210: public void mouseReleased(MouseEvent e) {
3211: }
3212:
3213: public boolean isMouseOver() {
3214: return mouseOver;
3215: }
3216:
3217: public void setMouseOver(boolean _mouseOver) {
3218: mouseOver = _mouseOver;
3219: }
3220:
3221: public void paint(Graphics g) {
3222: Color origColor;
3223: boolean isPressed, isEnabled;
3224: int w, h, size;
3225:
3226: w = getSize().width;
3227: h = getSize().height;
3228: origColor = g.getColor();
3229: isPressed = getModel().isPressed();
3230: isEnabled = isEnabled();
3231:
3232: g.setColor(getBackground());
3233: g.fillRect(0, 0, w, h);
3234:
3235: if (mouseOver && isEnabled) {
3236: g.setColor(Color.DARK_GRAY);
3237: g.drawRect(1, 1, w - 2, h - 2);
3238: }
3239:
3240: // If there's no room to draw arrow, bail
3241: if (h < 5 || w < 5) {
3242: g.setColor(origColor);
3243: return;
3244: }
3245:
3246: g.translate(1, 1);
3247:
3248: // Draw the arrow
3249: size = Math.min((h - 4) / 3, (w - 4) / 3);
3250: size = Math.max(size, 2);
3251:
3252: paintTriangle(g, (w - size) / 2, (h - size) / 2, size,
3253: direction, isEnabled);
3254:
3255: // Reset the Graphics back to it's original settings
3256: g.translate(-1, -1);
3257: g.setColor(origColor);
3258:
3259: }
3260:
3261: public void paintTriangle(Graphics g, int x, int y, int size,
3262: int direction, boolean isEnabled) {
3263:
3264: Color oldColor = g.getColor();
3265: int mid, i, j;
3266:
3267: j = 0;
3268: size = Math.max(size, 2);
3269: mid = (size / 2) - 1;
3270:
3271: g.translate(x, y);
3272:
3273: if (isEnabled)
3274: g.setColor(darkShadow);
3275: else
3276: g.setColor(shadow);
3277:
3278: switch (direction) {
3279: case NORTH:
3280: for (i = 0; i < size; i++) {
3281: g.drawLine(mid - i, i, mid + i, i);
3282: }
3283: break;
3284: case SOUTH:
3285: j = 0;
3286: for (i = size - 1; i >= 0; i--) {
3287: g.drawLine(mid - i, j, mid + i, j);
3288: j++;
3289: }
3290: break;
3291: case WEST:
3292: for (i = 0; i < size; i++) {
3293: g.drawLine(i, mid - i, i, mid + i);
3294: }
3295: break;
3296: case EAST:
3297: j = 0;
3298: for (i = size - 1; i >= 0; i--) {
3299: g.drawLine(j, mid - i, j, mid + i);
3300: j++;
3301: }
3302: break;
3303: }
3304: g.translate(-x, -y);
3305: g.setColor(oldColor);
3306: }
3307:
3308: } // class ScrollableTabButton
3309:
3310: // Controller: event listeners
3311:
3312: /**
3313: * This inner class is marked "public" due to a compiler bug.
3314: * This class should be treated as a "protected" inner class.
3315: * Instantiate it only within subclasses of FlatTabbedPaneUI.
3316: */
3317: public class PropertyChangeHandler implements
3318: PropertyChangeListener {
3319: public void propertyChange(PropertyChangeEvent e) {
3320: JTabbedPane pane = (JTabbedPane) e.getSource();
3321: String name = e.getPropertyName();
3322:
3323: if ("mnemonicAt".equals(name)) {
3324: updateMnemonics();
3325: pane.repaint();
3326: } else if ("displayedMnemonicIndexAt".equals(name)) {
3327: pane.repaint();
3328: } else if (name.equals("indexForTitle")) {
3329: int index = ((Integer) e.getNewValue()).intValue();
3330: String title = tabPane.getTitleAt(index);
3331: if (BasicHTML.isHTMLString(title)) {
3332: if (htmlViews == null) { // Initialize vector
3333: htmlViews = createHTMLVector();
3334: } else { // Vector already exists
3335: View v = BasicHTML.createHTMLView(tabPane,
3336: title);
3337: htmlViews.setElementAt(v, index);
3338: }
3339: } else {
3340: if (htmlViews != null
3341: && htmlViews.elementAt(index) != null) {
3342: htmlViews.setElementAt(null, index);
3343: }
3344: }
3345: updateMnemonics();
3346: } else if (name.equals("tabLayoutPolicy")) {
3347: FlatTabbedPaneUI.this .uninstallUI(pane);
3348: FlatTabbedPaneUI.this .installUI(pane);
3349: }
3350: }
3351: }
3352:
3353: /**
3354: * This inner class is marked "public" due to a compiler bug.
3355: * This class should be treated as a "protected" inner class.
3356: * Instantiate it only within subclasses of FlatTabbedPaneUI.
3357: */
3358: public class TabSelectionHandler implements ChangeListener {
3359: public void stateChanged(ChangeEvent e) {
3360: JTabbedPane tabPane = (JTabbedPane) e.getSource();
3361: tabPane.revalidate();
3362: tabPane.repaint();
3363:
3364: if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
3365: int index = tabPane.getSelectedIndex();
3366: if (index < rects.length && index != -1) {
3367: tabScroller.tabPanel
3368: .scrollRectToVisible(rects[index]);
3369: }
3370: }
3371: }
3372: }
3373:
3374: /**
3375: * This inner class is marked "public" due to a compiler bug.
3376: * This class should be treated as a "protected" inner class.
3377: * Instantiate it only within subclasses of FlatTabbedPaneUI.
3378: */
3379: public class MouseHandler extends MouseAdapter {
3380: public void mousePressed(MouseEvent e) {
3381: if (!tabPane.isEnabled()) {
3382: return;
3383: }
3384: int tabIndex = getTabAtLocation(e.getX(), e.getY());
3385: if (tabIndex >= 0 && tabPane.isEnabledAt(tabIndex)) {
3386: if (tabIndex == tabPane.getSelectedIndex()) {
3387: if (tabPane.isRequestFocusEnabled()) {
3388: tabPane.requestFocus();
3389: tabPane
3390: .repaint(getTabBounds(tabPane, tabIndex));
3391: }
3392: } else {
3393: tabPane.setSelectedIndex(tabIndex);
3394: }
3395: }
3396: }
3397: }
3398:
3399: /**
3400: * This inner class is marked "public" due to a compiler bug.
3401: * This class should be treated as a "protected" inner class.
3402: * Instantiate it only within subclasses of FlatTabbedPaneUI.
3403: */
3404: public class FocusHandler extends FocusAdapter {
3405: public void focusGained(FocusEvent e) {
3406: JTabbedPane tabPane = (JTabbedPane) e.getSource();
3407: int tabCount = tabPane.getTabCount();
3408: int selectedIndex = tabPane.getSelectedIndex();
3409: if (selectedIndex != -1 && tabCount > 0
3410: && tabCount == rects.length) {
3411: tabPane.repaint(getTabBounds(tabPane, selectedIndex));
3412: }
3413: }
3414:
3415: public void focusLost(FocusEvent e) {
3416: JTabbedPane tabPane = (JTabbedPane) e.getSource();
3417: int tabCount = tabPane.getTabCount();
3418: int selectedIndex = tabPane.getSelectedIndex();
3419: if (selectedIndex != -1 && tabCount > 0
3420: && tabCount == rects.length) {
3421: tabPane.repaint(getTabBounds(tabPane, selectedIndex));
3422: }
3423: }
3424: }
3425:
3426: /* GES 2/3/99:
3427: The container listener code was added to support HTML
3428: rendering of tab titles.
3429:
3430: Ideally, we would be able to listen for property changes
3431: when a tab is added or its text modified. At the moment
3432: there are no such events because the Beans spec doesn't
3433: allow 'indexed' property changes (i.e. tab 2's text changed
3434: from A to B).
3435:
3436: In order to get around this, we listen for tabs to be added
3437: or removed by listening for the container events. we then
3438: queue up a runnable (so the component has a chance to complete
3439: the add) which checks the tab title of the new component to see
3440: if it requires HTML rendering.
3441:
3442: The Views (one per tab title requiring HTML rendering) are
3443: stored in the htmlViews Vector, which is only allocated after
3444: the first time we run into an HTML tab. Note that this vector
3445: is kept in step with the number of pages, and nulls are added
3446: for those pages whose tab title do not require HTML rendering.
3447:
3448: This makes it easy for the paint and layout code to tell
3449: whether to invoke the HTML engine without having to check
3450: the string during time-sensitive operations.
3451:
3452: When we have added a way to listen for tab additions and
3453: changes to tab text, this code should be removed and
3454: replaced by something which uses that. */
3455:
3456: private class ContainerHandler implements ContainerListener {
3457: public void componentAdded(ContainerEvent e) {
3458: JTabbedPane tp = (JTabbedPane) e.getContainer();
3459: Component child = e.getChild();
3460: if (child instanceof UIResource) {
3461: return;
3462: }
3463: int index = tp.indexOfComponent(child);
3464: String title = tp.getTitleAt(index);
3465: boolean isHTML = BasicHTML.isHTMLString(title);
3466: if (isHTML) {
3467: if (htmlViews == null) { // Initialize vector
3468: htmlViews = createHTMLVector();
3469: } else { // Vector already exists
3470: View v = BasicHTML.createHTMLView(tp, title);
3471: htmlViews.insertElementAt(v, index);
3472: }
3473: } else { // Not HTML
3474: if (htmlViews != null) { // Add placeholder
3475: htmlViews.insertElementAt(null, index);
3476: } // nada!
3477: }
3478: }
3479:
3480: public void componentRemoved(ContainerEvent e) {
3481: JTabbedPane tp = (JTabbedPane) e.getContainer();
3482: Component child = e.getChild();
3483: if (child instanceof UIResource) {
3484: return;
3485: }
3486:
3487: // NOTE 4/15/2002 (joutwate):
3488: // This fix is implemented using client properties since there is
3489: // currently no IndexPropertyChangeEvent. Once
3490: // IndexPropertyChangeEvents have been added this code should be
3491: // modified to use it.
3492: Integer indexObj = (Integer) tp
3493: .getClientProperty("__index_to_remove__");
3494: if (indexObj != null) {
3495: int index = indexObj.intValue();
3496: if (htmlViews != null && htmlViews.size() >= index) {
3497: htmlViews.removeElementAt(index);
3498: }
3499: }
3500: }
3501: }
3502:
3503: private Vector createHTMLVector() {
3504: Vector htmlViews = new Vector();
3505: int count = tabPane.getTabCount();
3506: if (count > 0) {
3507: for (int i = 0; i < count; i++) {
3508: String title = tabPane.getTitleAt(i);
3509: if (BasicHTML.isHTMLString(title)) {
3510: htmlViews.addElement(BasicHTML.createHTMLView(
3511: tabPane, title));
3512: } else {
3513: htmlViews.addElement(null);
3514: }
3515: }
3516: }
3517: return htmlViews;
3518: }
3519:
3520: }
|