0001: /*
0002: * Copyright (c) 2001-2007 JGoodies Karsten Lentzsch. All Rights Reserved.
0003: *
0004: * Redistribution and use in source and binary forms, with or without
0005: * modification, are permitted provided that the following conditions are met:
0006: *
0007: * o Redistributions of source code must retain the above copyright notice,
0008: * this list of conditions and the following disclaimer.
0009: *
0010: * o Redistributions in binary form must reproduce the above copyright notice,
0011: * this list of conditions and the following disclaimer in the documentation
0012: * and/or other materials provided with the distribution.
0013: *
0014: * o Neither the name of JGoodies Karsten Lentzsch nor the names of
0015: * its contributors may be used to endorse or promote products derived
0016: * from this software without specific prior written permission.
0017: *
0018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
0020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
0021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
0022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
0025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
0026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
0027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
0028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0029: */
0030:
0031: package com.jgoodies.looks.plastic;
0032:
0033: import java.awt.Color;
0034: import java.awt.Component;
0035: import java.awt.Container;
0036: import java.awt.Dimension;
0037: import java.awt.Font;
0038: import java.awt.FontMetrics;
0039: import java.awt.Graphics;
0040: import java.awt.Graphics2D;
0041: import java.awt.Insets;
0042: import java.awt.LayoutManager;
0043: import java.awt.Point;
0044: import java.awt.Polygon;
0045: import java.awt.Rectangle;
0046: import java.awt.Shape;
0047: import java.awt.event.ActionEvent;
0048: import java.awt.event.ActionListener;
0049: import java.awt.event.MouseEvent;
0050: import java.beans.PropertyChangeEvent;
0051: import java.beans.PropertyChangeListener;
0052:
0053: import javax.swing.AbstractAction;
0054: import javax.swing.Action;
0055: import javax.swing.ActionMap;
0056: import javax.swing.Icon;
0057: import javax.swing.JButton;
0058: import javax.swing.JComponent;
0059: import javax.swing.JPanel;
0060: import javax.swing.JTabbedPane;
0061: import javax.swing.JViewport;
0062: import javax.swing.SwingConstants;
0063: import javax.swing.SwingUtilities;
0064: import javax.swing.UIManager;
0065: import javax.swing.event.ChangeEvent;
0066: import javax.swing.event.ChangeListener;
0067: import javax.swing.plaf.ComponentUI;
0068: import javax.swing.plaf.UIResource;
0069: import javax.swing.plaf.basic.BasicTabbedPaneUI;
0070: import javax.swing.plaf.metal.MetalTabbedPaneUI;
0071: import javax.swing.text.View;
0072:
0073: import com.jgoodies.looks.LookUtils;
0074: import com.jgoodies.looks.Options;
0075:
0076: /**
0077: * The JGoodies Plastic Look&Feel implementation of
0078: * <code>TabbedPaneUI</code>. It differs from its superclass
0079: * <code>MetalTabbedPaneUI</code> in that it paints new tab shapes,
0080: * provides two options, and supports ClearLook.
0081: * <p>
0082: * You can enable or disable icons in tabs globally via
0083: * com.jgoodies.looks.Options.setTabIconsEnabled(boolean).
0084: * <p>
0085: * To disable the content border set
0086: * <pre>
0087: * JTabbedPane tabbedPane = new JTabbedPane();
0088: * tabbedPane.putClientProperty(Option.NO_CONTENT_BORDER_KEY, Boolean.TRUE);
0089: * </pre>
0090: * To paint embedded tabs use
0091: * <pre>
0092: * JTabbedPane tabbedPane = new JTabbedPane();
0093: * tabbedPane.putClientProperty(Option.EMBEDDED_TABS_KEY, Boolean.TRUE);
0094: * </pre>
0095: * <p>
0096: * There's a special mode that helps you detect content borders in
0097: * heavily wrapped component hierarchies - such as the NetBeans IDE.
0098: * In this marked mode the content border is painted as a Magenta line.
0099: * You can enable this mode by setting the System property
0100: * <tt>markContentBorders</tt> to <tt>true</tt>; in a command line:
0101: * <pre>
0102: * java -DmarkContentBorders=true
0103: * </pre>
0104: *
0105: * @author Karsten Lentzsch
0106: * @author Torge Husfeldt
0107: * @author Andrej Golovnin
0108: * @version $Revision: 1.6 $
0109: *
0110: * @see Options
0111: */
0112: public final class PlasticTabbedPaneUI extends MetalTabbedPaneUI {
0113:
0114: // State ******************************************************************
0115:
0116: /**
0117: * Describes if tabs are painted with or without icons.
0118: */
0119: private static boolean isTabIconsEnabled = Options
0120: .isTabIconsEnabled();
0121:
0122: /**
0123: * Describes if we paint no content border or not; is false by default.
0124: * You can disable the content border by setting the client property
0125: * Options.NO_CONTENT_BORDER_KEY to Boolean.TRUE;
0126: */
0127: private Boolean noContentBorder;
0128:
0129: /**
0130: * Describes if we paint tabs in an embedded style that is with
0131: * less decoration; this is false by default.
0132: * You can enable the embedded tabs style by setting the client property
0133: * Options.EMBEDDED_TABS_KEY to Boolean.TRUE.
0134: */
0135: private Boolean embeddedTabs;
0136:
0137: /**
0138: * Holds the renderer that is used to render the tabs.
0139: */
0140: private AbstractRenderer renderer;
0141:
0142: /** For use when tabLayoutPolicy == SCROLL_TAB_LAYOUT. */
0143: private ScrollableTabSupport tabScroller;
0144:
0145: /**
0146: * Creates the <code>PlasticTabbedPaneUI</code>.
0147: *
0148: * @see javax.swing.plaf.ComponentUI#createUI(JComponent)
0149: */
0150: public static ComponentUI createUI(JComponent tabPane) {
0151: return new PlasticTabbedPaneUI();
0152: }
0153:
0154: /**
0155: * Installs the UI.
0156: *
0157: * @see javax.swing.plaf.ComponentUI#installUI(JComponent)
0158: */
0159: public void installUI(JComponent c) {
0160: super .installUI(c);
0161: embeddedTabs = (Boolean) c
0162: .getClientProperty(Options.EMBEDDED_TABS_KEY);
0163: noContentBorder = (Boolean) c
0164: .getClientProperty(Options.NO_CONTENT_BORDER_KEY);
0165: renderer = createRenderer(tabPane);
0166: }
0167:
0168: /**
0169: * Uninstalls the UI.
0170: * @see javax.swing.plaf.ComponentUI#uninstallUI(JComponent)
0171: */
0172: public void uninstallUI(JComponent c) {
0173: renderer = null;
0174: super .uninstallUI(c);
0175: }
0176:
0177: /**
0178: * Creates and installs any required subcomponents for the JTabbedPane.
0179: * Invoked by installUI.
0180: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#installComponents()
0181: */
0182: protected void installComponents() {
0183: if (scrollableTabLayoutEnabled()) {
0184: if (tabScroller == null) {
0185: tabScroller = new ScrollableTabSupport(tabPane
0186: .getTabPlacement());
0187: tabPane.add(tabScroller.viewport);
0188: }
0189: }
0190: }
0191:
0192: /**
0193: * Removes any installed subcomponents from the JTabbedPane.
0194: * Invoked by uninstallUI.
0195: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#uninstallComponents()
0196: */
0197: protected void uninstallComponents() {
0198: if (scrollableTabLayoutEnabled()) {
0199: tabPane.remove(tabScroller.viewport);
0200: tabPane.remove(tabScroller.scrollForwardButton);
0201: tabPane.remove(tabScroller.scrollBackwardButton);
0202: tabScroller = null;
0203: }
0204: }
0205:
0206: protected void installListeners() {
0207: super .installListeners();
0208: // if the layout policy is the SCROLL_TAB_LAYOUT, the super class
0209: // will install the mouse listener on tabPane instead of
0210: // tabScroller#tabPanel and there is no way to prevent this.
0211: // That's why the mouse listener must be removed from tabPane and
0212: // added to tabScroller#tabPanel when the scroll tab layout is enabled.
0213: // This applies only to JDK 1.4!!!
0214: if ((mouseListener != null) && (LookUtils.IS_JAVA_1_4)) {
0215: if (scrollableTabLayoutEnabled()) {
0216: tabPane.removeMouseListener(mouseListener);
0217: tabScroller.tabPanel.addMouseListener(mouseListener);
0218: }
0219: }
0220: }
0221:
0222: protected void uninstallListeners() {
0223: if ((mouseListener != null) && (LookUtils.IS_JAVA_1_4)) {
0224: if (scrollableTabLayoutEnabled()) { // SCROLL_TAB_LAYOUT
0225: tabScroller.tabPanel.removeMouseListener(mouseListener);
0226: } else { // WRAP_TAB_LAYOUT
0227: tabPane.removeMouseListener(mouseListener);
0228: }
0229: mouseListener = null;
0230: }
0231: super .uninstallListeners();
0232: }
0233:
0234: protected void installKeyboardActions() {
0235: super .installKeyboardActions();
0236: // if the layout policy is the SCROLL_TAB_LAYOUT, then replace
0237: // the forward and backward actions, installed in the action map
0238: // in the supper class, by our own.
0239: if (scrollableTabLayoutEnabled()) {
0240: Action forwardAction = new ScrollTabsForwardAction();
0241: Action backwardAction = new ScrollTabsBackwardAction();
0242: ActionMap am = SwingUtilities.getUIActionMap(tabPane);
0243: am.put("scrollTabsForwardAction", forwardAction);
0244: am.put("scrollTabsBackwardAction", backwardAction);
0245: tabScroller.scrollForwardButton.setAction(forwardAction);
0246: tabScroller.scrollBackwardButton.setAction(backwardAction);
0247: }
0248: }
0249:
0250: /**
0251: * Checks and answers if content border will be painted.
0252: * This is controlled by the component's client property
0253: * Options.NO_CONTENT_BORDER or Options.EMBEDDED.
0254: */
0255: private boolean hasNoContentBorder() {
0256: return Boolean.TRUE.equals(noContentBorder);
0257: }
0258:
0259: /**
0260: * Checks and answers if tabs are painted with minimal decoration.
0261: */
0262: private boolean hasEmbeddedTabs() {
0263: return Boolean.TRUE.equals(embeddedTabs);
0264: }
0265:
0266: /**
0267: * Creates the renderer used to lay out and paint the tabs.
0268: * @param tabbedPane the UIs component
0269: * @return AbstractRenderer the renderer that will be used to paint
0270: */
0271: private AbstractRenderer createRenderer(JTabbedPane tabbedPane) {
0272: return hasEmbeddedTabs() ? AbstractRenderer
0273: .createEmbeddedRenderer(tabbedPane) : AbstractRenderer
0274: .createRenderer(tabPane);
0275: }
0276:
0277: /**
0278: * Creates and answer a handler that listens to property changes.
0279: * Unlike the superclass BasicTabbedPane, the PlasticTabbedPaneUI
0280: * uses an extended Handler.
0281: */
0282: protected PropertyChangeListener createPropertyChangeListener() {
0283: return new MyPropertyChangeHandler();
0284: }
0285:
0286: protected ChangeListener createChangeListener() {
0287: return new TabSelectionHandler();
0288: }
0289:
0290: /*
0291: * Private helper method for the next three methods.
0292: */
0293: private void doLayout() {
0294: tabPane.revalidate();
0295: tabPane.repaint();
0296: }
0297:
0298: /**
0299: * Updates the renderer and layout. This message is sent by
0300: * my PropertyChangeHandler whenever the tab placement changes.
0301: */
0302: private void tabPlacementChanged() {
0303: renderer = createRenderer(tabPane);
0304: if (scrollableTabLayoutEnabled()) {
0305: tabScroller.createButtons();
0306: }
0307: doLayout();
0308: }
0309:
0310: /**
0311: * Updates the embedded tabs property. This message is sent by
0312: * my PropertyChangeHandler whenever the embedded tabs property changes.
0313: */
0314: private void embeddedTabsPropertyChanged(Boolean newValue) {
0315: embeddedTabs = newValue;
0316: renderer = createRenderer(tabPane);
0317: doLayout();
0318: }
0319:
0320: /**
0321: * Updates the no content border property. This message is sent
0322: * by my PropertyChangeHandler whenever the noContentBorder
0323: * property changes.
0324: */
0325: private void noContentBorderPropertyChanged(Boolean newValue) {
0326: noContentBorder = newValue;
0327: tabPane.repaint();
0328: }
0329:
0330: public void paint(Graphics g, JComponent c) {
0331: int selectedIndex = tabPane.getSelectedIndex();
0332: int tabPlacement = tabPane.getTabPlacement();
0333:
0334: ensureCurrentLayout();
0335:
0336: // Paint tab area
0337: // If scrollable tabs are enabled, the tab area will be
0338: // painted by the scrollable tab panel instead.
0339: //
0340: if (!scrollableTabLayoutEnabled()) { // WRAP_TAB_LAYOUT
0341: paintTabArea(g, tabPlacement, selectedIndex);
0342: }
0343:
0344: // Paint content border
0345: paintContentBorder(g, tabPlacement, selectedIndex);
0346: }
0347:
0348: protected void paintTab(Graphics g, int tabPlacement,
0349: Rectangle[] rects, int tabIndex, Rectangle iconRect,
0350: Rectangle textRect) {
0351: Rectangle tabRect = rects[tabIndex];
0352: int selectedIndex = tabPane.getSelectedIndex();
0353: boolean isSelected = selectedIndex == tabIndex;
0354: Graphics2D g2 = null;
0355: Polygon cropShape = null;
0356: Shape save = null;
0357: int cropx = 0;
0358: int cropy = 0;
0359:
0360: if (scrollableTabLayoutEnabled()) {
0361: if (g instanceof Graphics2D) {
0362: g2 = (Graphics2D) g;
0363:
0364: // Render visual for cropped tab edge...
0365: Rectangle viewRect = tabScroller.viewport.getViewRect();
0366: int cropline;
0367: switch (tabPlacement) {
0368: case LEFT:
0369: case RIGHT:
0370: cropline = viewRect.y + viewRect.height;
0371: if ((tabRect.y < cropline)
0372: && (tabRect.y + tabRect.height > cropline)) {
0373: cropShape = createCroppedTabClip(tabPlacement,
0374: tabRect, cropline);
0375: cropx = tabRect.x;
0376: cropy = cropline - 1;
0377: }
0378: break;
0379: case TOP:
0380: case BOTTOM:
0381: default:
0382: cropline = viewRect.x + viewRect.width;
0383: if ((tabRect.x < cropline)
0384: && (tabRect.x + tabRect.width > cropline)) {
0385: cropShape = createCroppedTabClip(tabPlacement,
0386: tabRect, cropline);
0387: cropx = cropline - 1;
0388: cropy = tabRect.y;
0389: }
0390: }
0391: if (cropShape != null) {
0392: save = g.getClip();
0393: g2.clip(cropShape);
0394: }
0395: }
0396: }
0397:
0398: paintTabBackground(g, tabPlacement, tabIndex, tabRect.x,
0399: tabRect.y, tabRect.width, tabRect.height, isSelected);
0400:
0401: paintTabBorder(g, tabPlacement, tabIndex, tabRect.x, tabRect.y,
0402: tabRect.width, tabRect.height, isSelected);
0403:
0404: String title = tabPane.getTitleAt(tabIndex);
0405: Font font = tabPane.getFont();
0406: FontMetrics metrics = g.getFontMetrics(font);
0407: Icon icon = getIconForTab(tabIndex);
0408:
0409: layoutLabel(tabPlacement, metrics, tabIndex, title, icon,
0410: tabRect, iconRect, textRect, isSelected);
0411:
0412: paintText(g, tabPlacement, font, metrics, tabIndex, title,
0413: textRect, isSelected);
0414:
0415: paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected);
0416:
0417: paintFocusIndicator(g, tabPlacement, rects, tabIndex, iconRect,
0418: textRect, isSelected);
0419:
0420: if (cropShape != null) {
0421: paintCroppedTabEdge(g, tabPlacement, tabIndex, isSelected,
0422: cropx, cropy);
0423: g.setClip(save);
0424: }
0425: }
0426:
0427: /*
0428: * This method will create and return a polygon shape for the given tab
0429: * rectangle which has been cropped at the specified cropline with a torn
0430: * edge visual. e.g. A "File" tab which has cropped been cropped just after
0431: * the "i":
0432: * -------------
0433: * | ..... |
0434: * | . |
0435: * | ... . |
0436: * | . . |
0437: * | . . |
0438: * | . . |
0439: * --------------
0440: *
0441: * The x, y arrays below define the pattern used to create a "torn" edge
0442: * segment which is repeated to fill the edge of the tab. For tabs placed on
0443: * TOP and BOTTOM, this righthand torn edge is created by line segments
0444: * which are defined by coordinates obtained by subtracting xCropLen[i] from
0445: * (tab.x + tab.width) and adding yCroplen[i] to (tab.y). For tabs placed on
0446: * LEFT or RIGHT, the bottom torn edge is created by subtracting xCropLen[i]
0447: * from (tab.y + tab.height) and adding yCropLen[i] to (tab.x).
0448: */
0449: private int[] xCropLen = { 1, 1, 0, 0, 1, 1, 2, 2 };
0450:
0451: private int[] yCropLen = { 0, 3, 3, 6, 6, 9, 9, 12 };
0452:
0453: private static final int CROP_SEGMENT = 12;
0454:
0455: private Polygon createCroppedTabClip(int tabPlacement,
0456: Rectangle tabRect, int cropline) {
0457: int rlen = 0;
0458: int start = 0;
0459: int end = 0;
0460: int ostart = 0;
0461:
0462: switch (tabPlacement) {
0463: case LEFT:
0464: case RIGHT:
0465: rlen = tabRect.width;
0466: start = tabRect.x;
0467: end = tabRect.x + tabRect.width;
0468: ostart = tabRect.y;
0469: break;
0470: case TOP:
0471: case BOTTOM:
0472: default:
0473: rlen = tabRect.height;
0474: start = tabRect.y;
0475: end = tabRect.y + tabRect.height;
0476: ostart = tabRect.x;
0477: }
0478: int rcnt = rlen / CROP_SEGMENT;
0479: if (rlen % CROP_SEGMENT > 0) {
0480: rcnt++;
0481: }
0482: int npts = 2 + (rcnt * 8);
0483: int[] xp = new int[npts];
0484: int[] yp = new int[npts];
0485: int pcnt = 0;
0486:
0487: xp[pcnt] = ostart;
0488: yp[pcnt++] = end;
0489: xp[pcnt] = ostart;
0490: yp[pcnt++] = start;
0491: for (int i = 0; i < rcnt; i++) {
0492: for (int j = 0; j < xCropLen.length; j++) {
0493: xp[pcnt] = cropline - xCropLen[j];
0494: yp[pcnt] = start + (i * CROP_SEGMENT) + yCropLen[j];
0495: if (yp[pcnt] >= end) {
0496: yp[pcnt] = end;
0497: pcnt++;
0498: break;
0499: }
0500: pcnt++;
0501: }
0502: }
0503: if (tabPlacement == SwingConstants.TOP
0504: || tabPlacement == SwingConstants.BOTTOM) {
0505: return new Polygon(xp, yp, pcnt);
0506:
0507: }
0508: //LEFT or RIGHT
0509: return new Polygon(yp, xp, pcnt);
0510: }
0511:
0512: /* If tabLayoutPolicy == SCROLL_TAB_LAYOUT, this method will paint an edge
0513: * indicating the tab is cropped in the viewport display
0514: */
0515: private void paintCroppedTabEdge(Graphics g, int tabPlacement,
0516: int tabIndex, boolean isSelected, int x, int y) {
0517: switch (tabPlacement) {
0518: case LEFT:
0519: case RIGHT:
0520: int xx = x;
0521: g.setColor(shadow);
0522: while (xx <= x + rects[tabIndex].width) {
0523: for (int i = 0; i < xCropLen.length; i += 2) {
0524: g.drawLine(xx + yCropLen[i], y - xCropLen[i], xx
0525: + yCropLen[i + 1] - 1, y - xCropLen[i + 1]);
0526: }
0527: xx += CROP_SEGMENT;
0528: }
0529: break;
0530: case TOP:
0531: case BOTTOM:
0532: default:
0533: int yy = y;
0534: g.setColor(shadow);
0535: while (yy <= y + rects[tabIndex].height) {
0536: for (int i = 0; i < xCropLen.length; i += 2) {
0537: g
0538: .drawLine(x - xCropLen[i],
0539: yy + yCropLen[i], x
0540: - xCropLen[i + 1], yy
0541: + yCropLen[i + 1] - 1);
0542: }
0543: yy += CROP_SEGMENT;
0544: }
0545: }
0546: }
0547:
0548: private void ensureCurrentLayout() {
0549: if (!tabPane.isValid()) {
0550: tabPane.validate();
0551: }
0552: /* If tabPane doesn't have a peer yet, the validate() call will
0553: * silently fail. We handle that by forcing a layout if tabPane
0554: * is still invalid. See bug 4237677.
0555: */
0556: if (!tabPane.isValid()) {
0557: TabbedPaneLayout layout = (TabbedPaneLayout) tabPane
0558: .getLayout();
0559: layout.calculateLayoutInfo();
0560: }
0561: }
0562:
0563: /**
0564: * Returns the tab index which intersects the specified point
0565: * in the JTabbedPane's coordinate space.
0566: */
0567: public int tabForCoordinate(JTabbedPane pane, int x, int y) {
0568: ensureCurrentLayout();
0569: Point p = new Point(x, y);
0570:
0571: if (scrollableTabLayoutEnabled()) {
0572: translatePointToTabPanel(x, y, p);
0573: Rectangle viewRect = tabScroller.viewport.getViewRect();
0574: if (!viewRect.contains(p)) {
0575: return -1;
0576: }
0577: }
0578: int tabCount = tabPane.getTabCount();
0579: for (int i = 0; i < tabCount; i++) {
0580: if (rects[i].contains(p.x, p.y)) {
0581: return i;
0582: }
0583: }
0584: return -1;
0585: }
0586:
0587: protected Rectangle getTabBounds(int tabIndex, Rectangle dest) {
0588: dest.width = rects[tabIndex].width;
0589: dest.height = rects[tabIndex].height;
0590: if (scrollableTabLayoutEnabled()) { // SCROLL_TAB_LAYOUT
0591: // Need to translate coordinates based on viewport location &
0592: // view position
0593: Point vpp = tabScroller.viewport.getLocation();
0594: Point viewp = tabScroller.viewport.getViewPosition();
0595: dest.x = rects[tabIndex].x + vpp.x - viewp.x;
0596: dest.y = rects[tabIndex].y + vpp.y - viewp.y;
0597: } else { // WRAP_TAB_LAYOUT
0598: dest.x = rects[tabIndex].x;
0599: dest.y = rects[tabIndex].y;
0600: }
0601: return dest;
0602: }
0603:
0604: /**
0605: * Returns the index of the tab closest to the passed in location, note
0606: * that the returned tab may not contain the location x,y.
0607: */
0608: private int getClosestTab(int x, int y) {
0609: int min = 0;
0610: int tabCount = Math.min(rects.length, tabPane.getTabCount());
0611: int max = tabCount;
0612: int tabPlacement = tabPane.getTabPlacement();
0613: boolean useX = (tabPlacement == TOP || tabPlacement == BOTTOM);
0614: int want = (useX) ? x : y;
0615:
0616: while (min != max) {
0617: int current = (max + min) / 2;
0618: int minLoc;
0619: int maxLoc;
0620:
0621: if (useX) {
0622: minLoc = rects[current].x;
0623: maxLoc = minLoc + rects[current].width;
0624: } else {
0625: minLoc = rects[current].y;
0626: maxLoc = minLoc + rects[current].height;
0627: }
0628: if (want < minLoc) {
0629: max = current;
0630: if (min == max) {
0631: return Math.max(0, current - 1);
0632: }
0633: } else if (want >= maxLoc) {
0634: min = current;
0635: if (max - min <= 1) {
0636: return Math.max(current + 1, tabCount - 1);
0637: }
0638: } else {
0639: return current;
0640: }
0641: }
0642: return min;
0643: }
0644:
0645: /**
0646: * Returns a point which is translated from the specified point in the
0647: * JTabbedPane's coordinate space to the coordinate space of the
0648: * ScrollableTabPanel. This is used for SCROLL_TAB_LAYOUT ONLY.
0649: */
0650: private Point translatePointToTabPanel(int srcx, int srcy,
0651: Point dest) {
0652: Point vpp = tabScroller.viewport.getLocation();
0653: Point viewp = tabScroller.viewport.getViewPosition();
0654: dest.x = srcx - vpp.x + viewp.x;
0655: dest.y = srcy - vpp.y + viewp.y;
0656: return dest;
0657: }
0658:
0659: protected void paintTabArea(Graphics g, int tabPlacement,
0660: int selectedIndex) {
0661: int tabCount = tabPane.getTabCount();
0662:
0663: Rectangle iconRect = new Rectangle(), textRect = new Rectangle();
0664: Rectangle clipRect = g.getClipBounds();
0665:
0666: // Paint tabRuns of tabs from back to front
0667: for (int i = runCount - 1; i >= 0; i--) {
0668: int start = tabRuns[i];
0669: int next = tabRuns[(i == runCount - 1) ? 0 : i + 1];
0670: int end = (next != 0 ? next - 1 : tabCount - 1);
0671: for (int j = end; j >= start; j--) {
0672: if (j != selectedIndex && rects[j].intersects(clipRect)) {
0673: paintTab(g, tabPlacement, rects, j, iconRect,
0674: textRect);
0675: }
0676: }
0677: }
0678:
0679: // Paint selected tab if its in the front run
0680: // since it may overlap other tabs
0681: if (selectedIndex >= 0
0682: && rects[selectedIndex].intersects(clipRect)) {
0683: paintTab(g, tabPlacement, rects, selectedIndex, iconRect,
0684: textRect);
0685: }
0686: }
0687:
0688: /*
0689: * Copied here from super(super)class to avoid labels being centered on
0690: * vertical tab runs if they consist of icon and text
0691: */
0692: protected void layoutLabel(int tabPlacement, FontMetrics metrics,
0693: int tabIndex, String title, Icon icon, Rectangle tabRect,
0694: Rectangle iconRect, Rectangle textRect, boolean isSelected) {
0695: textRect.x = textRect.y = iconRect.x = iconRect.y = 0;
0696: //fix of issue #4
0697: View v = getTextViewForTab(tabIndex);
0698: if (v != null) {
0699: tabPane.putClientProperty("html", v);
0700: }
0701:
0702: Rectangle calcRectangle = new Rectangle(tabRect);
0703: if (isSelected) {
0704: Insets calcInsets = getSelectedTabPadInsets(tabPlacement);
0705: calcRectangle.x += calcInsets.left;
0706: calcRectangle.y += calcInsets.top;
0707: calcRectangle.width -= calcInsets.left + calcInsets.right;
0708: calcRectangle.height -= calcInsets.bottom + calcInsets.top;
0709: }
0710: int xNudge = getTabLabelShiftX(tabPlacement, tabIndex,
0711: isSelected);
0712: int yNudge = getTabLabelShiftY(tabPlacement, tabIndex,
0713: isSelected);
0714: if ((tabPlacement == RIGHT || tabPlacement == LEFT)
0715: && icon != null && title != null && !title.equals("")) {
0716: SwingUtilities.layoutCompoundLabel(tabPane, metrics, title,
0717: icon, SwingConstants.CENTER, SwingConstants.LEFT,
0718: SwingConstants.CENTER, SwingConstants.TRAILING,
0719: calcRectangle, iconRect, textRect, textIconGap);
0720: xNudge += 4;
0721: } else {
0722: SwingUtilities.layoutCompoundLabel(tabPane, metrics, title,
0723: icon, SwingConstants.CENTER, SwingConstants.CENTER,
0724: SwingConstants.CENTER, SwingConstants.TRAILING,
0725: calcRectangle, iconRect, textRect, textIconGap);
0726: iconRect.y += calcRectangle.height % 2;
0727: }
0728:
0729: //fix of issue #4
0730: tabPane.putClientProperty("html", null);
0731:
0732: iconRect.x += xNudge;
0733: iconRect.y += yNudge;
0734: textRect.x += xNudge;
0735: textRect.y += yNudge;
0736: }
0737:
0738: /**
0739: * Answers the icon for the tab with the specified index.
0740: * In case, we have globally switched of the use tab icons,
0741: * we answer <code>null</code> if and only if we have a title.
0742: */
0743: protected Icon getIconForTab(int tabIndex) {
0744: String title = tabPane.getTitleAt(tabIndex);
0745: boolean hasTitle = (title != null) && (title.length() > 0);
0746: return !isTabIconsEnabled && hasTitle ? null : super
0747: .getIconForTab(tabIndex);
0748: }
0749:
0750: /**
0751: * Creates the layout manager used to set the tab's bounds.
0752: */
0753: protected LayoutManager createLayoutManager() {
0754: if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
0755: return new TabbedPaneScrollLayout();
0756: }
0757: /* WRAP_TAB_LAYOUT */
0758: return new TabbedPaneLayout();
0759: }
0760:
0761: /* In an attempt to preserve backward compatibility for programs
0762: * which have extended BasicTabbedPaneUI to do their own layout, the
0763: * UI uses the installed layoutManager (and not tabLayoutPolicy) to
0764: * determine if scrollTabLayout is enabled.
0765: */
0766: private boolean scrollableTabLayoutEnabled() {
0767: return tabPane.getLayout() instanceof TabbedPaneScrollLayout;
0768: }
0769:
0770: protected boolean isTabInFirstRun(int tabIndex) {
0771: return getRunForTab(tabPane.getTabCount(), tabIndex) == 0;
0772: }
0773:
0774: protected void paintContentBorder(Graphics g, int tabPlacement,
0775: int selectedIndex) {
0776: int width = tabPane.getWidth();
0777: int height = tabPane.getHeight();
0778: Insets insets = tabPane.getInsets();
0779:
0780: int x = insets.left;
0781: int y = insets.top;
0782: int w = width - insets.right - insets.left;
0783: int h = height - insets.top - insets.bottom;
0784:
0785: switch (tabPlacement) {
0786: case LEFT:
0787: x += calculateTabAreaWidth(tabPlacement, runCount,
0788: maxTabWidth);
0789: w -= (x - insets.left);
0790: break;
0791: case RIGHT:
0792: w -= calculateTabAreaWidth(tabPlacement, runCount,
0793: maxTabWidth);
0794: break;
0795: case BOTTOM:
0796: h -= calculateTabAreaHeight(tabPlacement, runCount,
0797: maxTabHeight);
0798: break;
0799: case TOP:
0800: default:
0801: y += calculateTabAreaHeight(tabPlacement, runCount,
0802: maxTabHeight);
0803: h -= (y - insets.top);
0804: }
0805: // Fill region behind content area
0806: g.setColor(selectColor == null ? tabPane.getBackground()
0807: : selectColor);
0808: g.fillRect(x, y, w, h);
0809:
0810: Rectangle selRect;
0811: selRect = (selectedIndex < 0) ? null : getTabBounds(
0812: selectedIndex, calcRect);
0813: boolean drawBroken = selectedIndex >= 0
0814: && isTabInFirstRun(selectedIndex);
0815: boolean isContentBorderPainted = !hasNoContentBorder();
0816: // It sounds a bit odd to call paintContentBorder with
0817: // a parameter isContentBorderPainted set to false.
0818: // But in this case the part of the border touching the tab
0819: // area will still be painted so best let the renderer decide.
0820: renderer.paintContentBorderTopEdge(g, x, y, w, h, drawBroken,
0821: selRect, isContentBorderPainted);
0822: renderer.paintContentBorderLeftEdge(g, x, y, w, h, drawBroken,
0823: selRect, isContentBorderPainted);
0824: renderer.paintContentBorderBottomEdge(g, x, y, w, h,
0825: drawBroken, selRect, isContentBorderPainted);
0826: renderer.paintContentBorderRightEdge(g, x, y, w, h, drawBroken,
0827: selRect, isContentBorderPainted);
0828: }
0829:
0830: //
0831: // Here comes a number of methods that are just delegated to the
0832: // appropriate renderer
0833: //
0834: /**
0835: * Returns the insets (i.e. the width) of the content Border.
0836: */
0837: protected Insets getContentBorderInsets(int tabPlacement) {
0838: return renderer.getContentBorderInsets(super
0839: .getContentBorderInsets(tabPlacement));
0840: }
0841:
0842: /**
0843: * Returns the amount by which the Tab Area is inset.
0844: */
0845: protected Insets getTabAreaInsets(int tabPlacement) {
0846: return renderer.getTabAreaInsets(super
0847: .getTabAreaInsets(tabPlacement));
0848: }
0849:
0850: /**
0851: * Returns the amount by which the label should be shifted horizontally.
0852: */
0853: protected int getTabLabelShiftX(int tabPlacement, int tabIndex,
0854: boolean isSelected) {
0855: return renderer.getTabLabelShiftX(tabIndex, isSelected);
0856: }
0857:
0858: /**
0859: * Returns the amount by which the label should be shifted vertically.
0860: */
0861: protected int getTabLabelShiftY(int tabPlacement, int tabIndex,
0862: boolean isSelected) {
0863: return renderer.getTabLabelShiftY(tabIndex, isSelected);
0864: }
0865:
0866: /**
0867: * Returns the amount (in pixels) by which two runs should overlap.
0868: */
0869: protected int getTabRunOverlay(int tabPlacement) {
0870: return renderer.getTabRunOverlay(tabRunOverlay);
0871: }
0872:
0873: /**
0874: * This boolean controls wheather the given run should be padded to
0875: * use up as much space as the others (with more tabs in them).
0876: */
0877: protected boolean shouldPadTabRun(int tabPlacement, int run) {
0878: return renderer.shouldPadTabRun(run, super .shouldPadTabRun(
0879: tabPlacement, run));
0880: }
0881:
0882: /**
0883: * Returns the amount by which the run number <code>run</code>
0884: * should be indented. Add six pixels for every run to make
0885: * diagonal lines align.
0886: */
0887: protected int getTabRunIndent(int tabPlacement, int run) {
0888: return renderer.getTabRunIndent(run);
0889: }
0890:
0891: /**
0892: * Returns the insets for this tab.
0893: */
0894: protected Insets getTabInsets(int tabPlacement, int tabIndex) {
0895: return renderer.getTabInsets(tabIndex, tabInsets);
0896: }
0897:
0898: /**
0899: * Returns the insets for selected tab.
0900: */
0901: protected Insets getSelectedTabPadInsets(int tabPlacement) {
0902: return renderer.getSelectedTabPadInsets();
0903: }
0904:
0905: /**
0906: * Draws the rectancle around the Tab label which indicates keyboard focus.
0907: */
0908: protected void paintFocusIndicator(Graphics g, int tabPlacement,
0909: Rectangle[] rectangles, int tabIndex, Rectangle iconRect,
0910: Rectangle textRect, boolean isSelected) {
0911: renderer.paintFocusIndicator(g, rectangles, tabIndex, iconRect,
0912: textRect, isSelected);
0913: }
0914:
0915: /**
0916: * Fills the background of the given tab to make sure overlap of
0917: * tabs is handled correctly.
0918: * Note: that tab backgrounds seem to be painted somewhere else, too.
0919: */
0920: protected void paintTabBackground(Graphics g, int tabPlacement,
0921: int tabIndex, int x, int y, int w, int h, boolean isSelected) {
0922: renderer
0923: .paintTabBackground(g, tabIndex, x, y, w, h, isSelected);
0924: }
0925:
0926: /**
0927: * Paints the border for one tab. Gets the bounds of the tab as parameters.
0928: * Note that the result is not clipped so you can paint outside that
0929: * rectangle. Tabs painted later on have a chance to overwrite though.
0930: */
0931: protected void paintTabBorder(Graphics g, int tabPlacement,
0932: int tabIndex, int x, int y, int w, int h, boolean isSelected) {
0933: renderer.paintTabBorder(g, tabIndex, x, y, w, h, isSelected);
0934: }
0935:
0936: /**
0937: * Answers wheather tab runs should be rotated. If true, the layout mechanism
0938: * will move the run containing the selected tab so that it touches
0939: * the content pane.
0940: */
0941: protected boolean shouldRotateTabRuns(int tabPlacement) {
0942: return false;
0943: }
0944:
0945: private class TabSelectionHandler implements ChangeListener {
0946:
0947: private Rectangle rect = new Rectangle();
0948:
0949: public void stateChanged(ChangeEvent e) {
0950: JTabbedPane tabPane = (JTabbedPane) e.getSource();
0951: tabPane.revalidate();
0952: tabPane.repaint();
0953:
0954: if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
0955: int index = tabPane.getSelectedIndex();
0956: if (index < rects.length && index != -1) {
0957: rect.setBounds(rects[index]);
0958: Point viewPosition = tabScroller.viewport
0959: .getViewPosition();
0960: if (rect.x < viewPosition.x) {
0961: rect.x -= renderer.getTabsOverlay();
0962: } else {
0963: rect.x += renderer.getTabsOverlay();
0964: }
0965: tabScroller.tabPanel.scrollRectToVisible(rect);
0966: }
0967: }
0968: }
0969: }
0970:
0971: /**
0972: * Catches and handles property change events. In addition to the super
0973: * class behavior we listen to changes of the ancestor, tab placement,
0974: * and JGoodies options for content border, and embedded tabs.
0975: */
0976: private class MyPropertyChangeHandler extends
0977: BasicTabbedPaneUI.PropertyChangeHandler {
0978: public void propertyChange(PropertyChangeEvent e) {
0979: String pName = e.getPropertyName();
0980:
0981: if (null == pName) {
0982: return;
0983: }
0984:
0985: super .propertyChange(e);
0986:
0987: if (pName.equals("tabPlacement")) {
0988: tabPlacementChanged();
0989: return;
0990: }
0991: if (pName.equals(Options.EMBEDDED_TABS_KEY)) {
0992: embeddedTabsPropertyChanged((Boolean) e.getNewValue());
0993: return;
0994: }
0995: if (pName.equals(Options.NO_CONTENT_BORDER_KEY)) {
0996: noContentBorderPropertyChanged((Boolean) e
0997: .getNewValue());
0998: return;
0999: }
1000: }
1001: }
1002:
1003: /**
1004: * Does all the layout work. The result is stored in the container
1005: * class's instance variables. Mainly the rects[] vector.
1006: */
1007: private class TabbedPaneLayout extends
1008: BasicTabbedPaneUI.TabbedPaneLayout implements LayoutManager {
1009:
1010: protected void calculateTabRects(int tabPlacement, int tabCount) {
1011: FontMetrics metrics = getFontMetrics();
1012: Dimension size = tabPane.getSize();
1013: Insets insets = tabPane.getInsets();
1014: Insets theTabAreaInsets = getTabAreaInsets(tabPlacement);
1015: int fontHeight = metrics.getHeight();
1016: int selectedIndex = tabPane.getSelectedIndex();
1017: int theTabRunOverlay;
1018: int i, j;
1019: int x, y;
1020: int returnAt;
1021: boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
1022: boolean leftToRight = PlasticUtils.isLeftToRight(tabPane);
1023:
1024: //
1025: // Calculate bounds within which a tab run must fit
1026: //
1027: switch (tabPlacement) {
1028: case LEFT:
1029: maxTabWidth = calculateMaxTabWidth(tabPlacement);
1030: x = insets.left + theTabAreaInsets.left;
1031: y = insets.top + theTabAreaInsets.top;
1032: returnAt = size.height
1033: - (insets.bottom + theTabAreaInsets.bottom);
1034: break;
1035: case RIGHT:
1036: maxTabWidth = calculateMaxTabWidth(tabPlacement);
1037: x = size.width - insets.right - theTabAreaInsets.right
1038: - maxTabWidth;
1039: y = insets.top + theTabAreaInsets.top;
1040: returnAt = size.height
1041: - (insets.bottom + theTabAreaInsets.bottom);
1042: break;
1043: case BOTTOM:
1044: maxTabHeight = calculateMaxTabHeight(tabPlacement);
1045: x = insets.left + theTabAreaInsets.left;
1046: y = size.height - insets.bottom
1047: - theTabAreaInsets.bottom - maxTabHeight;
1048: returnAt = size.width
1049: - (insets.right + theTabAreaInsets.right);
1050: break;
1051: case TOP:
1052: default:
1053: maxTabHeight = calculateMaxTabHeight(tabPlacement);
1054: x = insets.left + theTabAreaInsets.left;
1055: y = insets.top + theTabAreaInsets.top;
1056: returnAt = size.width
1057: - (insets.right + theTabAreaInsets.right);
1058: break;
1059: }
1060:
1061: theTabRunOverlay = getTabRunOverlay(tabPlacement);
1062:
1063: runCount = 0;
1064: selectedRun = -1;
1065: //keeps track of where we are in the current run.
1066: //this helps not to rely on fragile positioning
1067: //informaion to find out wheter the active Tab
1068: //is the first in run
1069: int tabInRun = -1;
1070: // make a copy of returnAt for the current run and modify
1071: // that so returnAt may still be used later on
1072: int runReturnAt = returnAt;
1073:
1074: if (tabCount == 0) {
1075: return;
1076: }
1077:
1078: // Run through tabs and partition them into runs
1079: Rectangle rect;
1080: for (i = 0; i < tabCount; i++) {
1081: rect = rects[i];
1082: tabInRun++;
1083:
1084: if (!verticalTabRuns) {
1085: // Tabs on TOP or BOTTOM....
1086: if (i > 0) {
1087: rect.x = rects[i - 1].x + rects[i - 1].width;
1088: } else {
1089: tabRuns[0] = 0;
1090: runCount = 1;
1091: maxTabWidth = 0;
1092: rect.x = x;
1093: // tabInRun = 0;
1094: }
1095: rect.width = calculateTabWidth(tabPlacement, i,
1096: metrics);
1097: maxTabWidth = Math.max(maxTabWidth, rect.width);
1098:
1099: // Never move a TAB down a run if it is the first in run.
1100: // Even if there isn't enough room, moving it to a fresh
1101: // line won't help.
1102: // if (rect.x != 2 + insets.left && rect.x + rect.width > returnAt) {
1103: // Never rely on phisical position information to determine
1104: // logical position (if you can avoid it)
1105: if (tabInRun != 0
1106: && rect.x + rect.width > runReturnAt) {
1107: if (runCount > tabRuns.length - 1) {
1108: expandTabRunsArray();
1109: }
1110: // just created a new run, adjust some counters
1111: tabInRun = 0;
1112: tabRuns[runCount] = i;
1113: runCount++;
1114: rect.x = x;
1115: runReturnAt = runReturnAt
1116: - 2
1117: * getTabRunIndent(tabPlacement,
1118: runCount);
1119: }
1120: // Initialize y position in case there's just one run
1121: rect.y = y;
1122: rect.height = maxTabHeight /* - 2*/;
1123:
1124: } else {
1125: // Tabs on LEFT or RIGHT...
1126: if (i > 0) {
1127: rect.y = rects[i - 1].y + rects[i - 1].height;
1128: } else {
1129: tabRuns[0] = 0;
1130: runCount = 1;
1131: maxTabHeight = 0;
1132: rect.y = y;
1133: // tabInRun = 0;
1134: }
1135: rect.height = calculateTabHeight(tabPlacement, i,
1136: fontHeight);
1137: maxTabHeight = Math.max(maxTabHeight, rect.height);
1138:
1139: // Never move a TAB over a run if it is the first in run.
1140: // Even if there isn't enough room, moving it to a fresh
1141: // run won't help.
1142: // if (rect.y != 2 + insets.top && rect.y + rect.height > returnAt) {
1143: if (tabInRun != 0
1144: && rect.y + rect.height > runReturnAt) {
1145: if (runCount > tabRuns.length - 1) {
1146: expandTabRunsArray();
1147: }
1148: tabRuns[runCount] = i;
1149: runCount++;
1150: rect.y = y;
1151: tabInRun = 0;
1152: runReturnAt -= 2 * getTabRunIndent(
1153: tabPlacement, runCount);
1154: }
1155: // Initialize x position in case there's just one column
1156: rect.x = x;
1157: rect.width = maxTabWidth /* - 2*/;
1158:
1159: }
1160: if (i == selectedIndex) {
1161: selectedRun = runCount - 1;
1162: }
1163: }
1164:
1165: if (runCount > 1) {
1166: // Re-distribute tabs in case last run has leftover space
1167: //last line flush left is OK
1168: // normalizeTabRuns(tabPlacement, tabCount, verticalTabRuns? y : x, returnAt);
1169: //don't need to recalculate selectedRun if not changed
1170: // selectedRun = getRunForTab(tabCount, selectedIndex);
1171:
1172: // Rotate run array so that selected run is first
1173: if (shouldRotateTabRuns(tabPlacement)) {
1174: rotateTabRuns(tabPlacement, selectedRun);
1175: }
1176: }
1177:
1178: // Step through runs from back to front to calculate
1179: // tab y locations and to pad runs appropriately
1180: for (i = runCount - 1; i >= 0; i--) {
1181: int start = tabRuns[i];
1182: int next = tabRuns[i == (runCount - 1) ? 0 : i + 1];
1183: int end = (next != 0 ? next - 1 : tabCount - 1);
1184: int indent = getTabRunIndent(tabPlacement, i);
1185: if (!verticalTabRuns) {
1186: for (j = start; j <= end; j++) {
1187: rect = rects[j];
1188: rect.y = y;
1189: rect.x += indent;
1190: // try to make tabRunIndent symmetric
1191: // rect.width -= 2* indent + 20;
1192: }
1193: if (shouldPadTabRun(tabPlacement, i)) {
1194: padTabRun(tabPlacement, start, end, returnAt
1195: - 2 * indent);
1196: }
1197: if (tabPlacement == BOTTOM) {
1198: y -= (maxTabHeight - theTabRunOverlay);
1199: } else {
1200: y += (maxTabHeight - theTabRunOverlay);
1201: }
1202: } else {
1203: for (j = start; j <= end; j++) {
1204: rect = rects[j];
1205: rect.x = x;
1206: rect.y += indent;
1207: }
1208: if (shouldPadTabRun(tabPlacement, i)) {
1209: padTabRun(tabPlacement, start, end, returnAt
1210: - 2 * indent);
1211: }
1212: if (tabPlacement == RIGHT) {
1213: x -= (maxTabWidth - theTabRunOverlay);
1214: } else {
1215: x += (maxTabWidth - theTabRunOverlay);
1216: }
1217: }
1218: }
1219:
1220: // Pad the selected tab so that it appears raised in front
1221: padSelectedTab(tabPlacement, selectedIndex);
1222:
1223: // if right to left and tab placement on the top or
1224: // the bottom, flip x positions and adjust by widths
1225: if (!leftToRight && !verticalTabRuns) {
1226: int rightMargin = size.width
1227: - (insets.right + theTabAreaInsets.right);
1228: for (i = 0; i < tabCount; i++) {
1229: rects[i].x = rightMargin - rects[i].x
1230: - rects[i].width
1231: + renderer.getTabsOverlay();
1232: }
1233: }
1234: }
1235:
1236: /**
1237: * Overridden to insure the same behavior in JDK 6.0 as in JDK 5.0.
1238: */
1239: protected void padSelectedTab(int tabPlacement,
1240: int selectedIndex) {
1241: if (selectedIndex >= 0) {
1242: Rectangle selRect = rects[selectedIndex];
1243: Insets padInsets = getSelectedTabPadInsets(tabPlacement);
1244: selRect.x -= padInsets.left;
1245: selRect.width += (padInsets.left + padInsets.right);
1246: selRect.y -= padInsets.top;
1247: selRect.height += (padInsets.top + padInsets.bottom);
1248: }
1249: }
1250:
1251: }
1252:
1253: private boolean requestFocusForVisibleComponent() {
1254: Component visibleComponent = getVisibleComponent();
1255: if (visibleComponent.isFocusable()) {
1256: visibleComponent.requestFocus();
1257: return true;
1258: }
1259: if (visibleComponent instanceof JComponent) {
1260: if (((JComponent) visibleComponent).requestDefaultFocus()) {
1261: return true;
1262: }
1263: }
1264: return false;
1265: }
1266:
1267: private static class ScrollTabsForwardAction extends AbstractAction {
1268:
1269: public void actionPerformed(ActionEvent e) {
1270: JTabbedPane pane = null;
1271: Object src = e.getSource();
1272: if (src instanceof JTabbedPane) {
1273: pane = (JTabbedPane) src;
1274: } else if (src instanceof PlasticArrowButton) {
1275: pane = (JTabbedPane) ((PlasticArrowButton) src)
1276: .getParent();
1277: } else {
1278: return; // shouldn't happen
1279: }
1280: PlasticTabbedPaneUI ui = (PlasticTabbedPaneUI) pane.getUI();
1281:
1282: if (ui.scrollableTabLayoutEnabled()) {
1283: ui.tabScroller.scrollForward(pane.getTabPlacement());
1284: }
1285: }
1286: }
1287:
1288: private static class ScrollTabsBackwardAction extends
1289: AbstractAction {
1290:
1291: public void actionPerformed(ActionEvent e) {
1292: JTabbedPane pane = null;
1293: Object src = e.getSource();
1294: if (src instanceof JTabbedPane) {
1295: pane = (JTabbedPane) src;
1296: } else if (src instanceof PlasticArrowButton) {
1297: pane = (JTabbedPane) ((PlasticArrowButton) src)
1298: .getParent();
1299: } else {
1300: return; // shouldn't happen
1301: }
1302: PlasticTabbedPaneUI ui = (PlasticTabbedPaneUI) pane.getUI();
1303:
1304: if (ui.scrollableTabLayoutEnabled()) {
1305: ui.tabScroller.scrollBackward(pane.getTabPlacement());
1306: }
1307: }
1308: }
1309:
1310: private class TabbedPaneScrollLayout extends TabbedPaneLayout {
1311:
1312: protected int preferredTabAreaHeight(int tabPlacement, int width) {
1313: return calculateMaxTabHeight(tabPlacement);
1314: }
1315:
1316: protected int preferredTabAreaWidth(int tabPlacement, int height) {
1317: return calculateMaxTabWidth(tabPlacement);
1318: }
1319:
1320: public void layoutContainer(Container parent) {
1321: int tabPlacement = tabPane.getTabPlacement();
1322: int tabCount = tabPane.getTabCount();
1323: Insets insets = tabPane.getInsets();
1324: int selectedIndex = tabPane.getSelectedIndex();
1325: Component visibleComponent = getVisibleComponent();
1326:
1327: calculateLayoutInfo();
1328:
1329: if (selectedIndex < 0) {
1330: if (visibleComponent != null) {
1331: // The last tab was removed, so remove the component
1332: setVisibleComponent(null);
1333: }
1334: } else {
1335: Component selectedComponent = tabPane
1336: .getComponentAt(selectedIndex);
1337: boolean shouldChangeFocus = false;
1338:
1339: // In order to allow programs to use a single component
1340: // as the display for multiple tabs, we will not change
1341: // the visible compnent if the currently selected tab
1342: // has a null component. This is a bit dicey, as we don't
1343: // explicitly state we support this in the spec, but since
1344: // programs are now depending on this, we're making it work.
1345: //
1346: if (selectedComponent != null) {
1347: if (selectedComponent != visibleComponent
1348: && visibleComponent != null) {
1349: if (SwingUtilities
1350: .findFocusOwner(visibleComponent) != null) {
1351: shouldChangeFocus = true;
1352: }
1353: }
1354: setVisibleComponent(selectedComponent);
1355: }
1356: int tx, ty, tw, th; // tab area bounds
1357: int cx, cy, cw, ch; // content area bounds
1358: Insets contentInsets = getContentBorderInsets(tabPlacement);
1359: Rectangle bounds = tabPane.getBounds();
1360: int numChildren = tabPane.getComponentCount();
1361:
1362: if (numChildren > 0) {
1363: switch (tabPlacement) {
1364: case LEFT:
1365: // calculate tab area bounds
1366: tw = calculateTabAreaWidth(tabPlacement,
1367: runCount, maxTabWidth);
1368: th = bounds.height - insets.top - insets.bottom;
1369: tx = insets.left;
1370: ty = insets.top;
1371:
1372: // calculate content area bounds
1373: cx = tx + tw + contentInsets.left;
1374: cy = ty + contentInsets.top;
1375: cw = bounds.width - insets.left - insets.right
1376: - tw - contentInsets.left
1377: - contentInsets.right;
1378: ch = bounds.height - insets.top - insets.bottom
1379: - contentInsets.top
1380: - contentInsets.bottom;
1381: break;
1382: case RIGHT:
1383: // calculate tab area bounds
1384: tw = calculateTabAreaWidth(tabPlacement,
1385: runCount, maxTabWidth);
1386: th = bounds.height - insets.top - insets.bottom;
1387: tx = bounds.width - insets.right - tw;
1388: ty = insets.top;
1389:
1390: // calculate content area bounds
1391: cx = insets.left + contentInsets.left;
1392: cy = insets.top + contentInsets.top;
1393: cw = bounds.width - insets.left - insets.right
1394: - tw - contentInsets.left
1395: - contentInsets.right;
1396: ch = bounds.height - insets.top - insets.bottom
1397: - contentInsets.top
1398: - contentInsets.bottom;
1399: break;
1400: case BOTTOM:
1401: // calculate tab area bounds
1402: tw = bounds.width - insets.left - insets.right;
1403: th = calculateTabAreaHeight(tabPlacement,
1404: runCount, maxTabHeight);
1405: tx = insets.left;
1406: ty = bounds.height - insets.bottom - th;
1407:
1408: // calculate content area bounds
1409: cx = insets.left + contentInsets.left;
1410: cy = insets.top + contentInsets.top;
1411: cw = bounds.width - insets.left - insets.right
1412: - contentInsets.left
1413: - contentInsets.right;
1414: ch = bounds.height - insets.top - insets.bottom
1415: - th - contentInsets.top
1416: - contentInsets.bottom;
1417: break;
1418: case TOP:
1419: default:
1420: // calculate tab area bounds
1421: tw = bounds.width - insets.left - insets.right;
1422: th = calculateTabAreaHeight(tabPlacement,
1423: runCount, maxTabHeight);
1424: tx = insets.left;
1425: ty = insets.top;
1426:
1427: // calculate content area bounds
1428: cx = tx + contentInsets.left;
1429: cy = ty + th + contentInsets.top;
1430: cw = bounds.width - insets.left - insets.right
1431: - contentInsets.left
1432: - contentInsets.right;
1433: ch = bounds.height - insets.top - insets.bottom
1434: - th - contentInsets.top
1435: - contentInsets.bottom;
1436: }
1437:
1438: for (int i = 0; i < numChildren; i++) {
1439: Component child = tabPane.getComponent(i);
1440:
1441: if (tabScroller != null
1442: && child == tabScroller.viewport) {
1443: JViewport viewport = (JViewport) child;
1444: Rectangle viewRect = viewport.getViewRect();
1445: int vw = tw;
1446: int vh = th;
1447: Dimension butSize = tabScroller.scrollForwardButton
1448: .getPreferredSize();
1449: switch (tabPlacement) {
1450: case LEFT:
1451: case RIGHT:
1452: int totalTabHeight = rects[tabCount - 1].y
1453: + rects[tabCount - 1].height;
1454: if (totalTabHeight > th) {
1455: // Allow space for scrollbuttons
1456: vh = (th > 2 * butSize.height) ? th
1457: - 2 * butSize.height : 0;
1458: if (totalTabHeight - viewRect.y <= vh) {
1459: // Scrolled to the end, so ensure the
1460: // viewport size is
1461: // such that the scroll offset aligns
1462: // with a tab
1463: vh = totalTabHeight
1464: - viewRect.y;
1465: }
1466: }
1467: break;
1468: case BOTTOM:
1469: case TOP:
1470: default:
1471: int totalTabWidth = rects[tabCount - 1].x
1472: + rects[tabCount - 1].width
1473: + renderer.getTabsOverlay();
1474: if (totalTabWidth > tw) {
1475: // Need to allow space for scrollbuttons
1476: vw = (tw > 2 * butSize.width) ? tw
1477: - 2 * butSize.width : 0;
1478: if (totalTabWidth - viewRect.x <= vw) {
1479: // Scrolled to the end, so ensure the
1480: // viewport size is
1481: // such that the scroll offset aligns
1482: // with a tab
1483: vw = totalTabWidth - viewRect.x;
1484: }
1485: }
1486: }
1487: child.setBounds(tx, ty, vw, vh);
1488:
1489: } else if (tabScroller != null
1490: && (child == tabScroller.scrollForwardButton || child == tabScroller.scrollBackwardButton)) {
1491: Component scrollbutton = child;
1492: Dimension bsize = scrollbutton
1493: .getPreferredSize();
1494: int bx = 0;
1495: int by = 0;
1496: int bw = bsize.width;
1497: int bh = bsize.height;
1498: boolean visible = false;
1499:
1500: switch (tabPlacement) {
1501: case LEFT:
1502: case RIGHT:
1503: int totalTabHeight = rects[tabCount - 1].y
1504: + rects[tabCount - 1].height
1505: + renderer.getTabsOverlay();
1506: if (totalTabHeight > th) {
1507: visible = true;
1508: bx = (tabPlacement == LEFT ? tx
1509: + tw - bsize.width : tx);
1510: by = (child == tabScroller.scrollForwardButton) ? bounds.height
1511: - insets.bottom
1512: - bsize.height
1513: : bounds.height
1514: - insets.bottom - 2
1515: * bsize.height;
1516: }
1517: break;
1518:
1519: case BOTTOM:
1520: case TOP:
1521: default:
1522: int totalTabWidth = rects[tabCount - 1].x
1523: + rects[tabCount - 1].width
1524: + renderer.getTabsOverlay();
1525:
1526: if (totalTabWidth > tw) {
1527: visible = true;
1528: bx = (child == tabScroller.scrollForwardButton) ? bounds.width
1529: - insets.left - bsize.width
1530: : bounds.width
1531: - insets.left - 2
1532: * bsize.width;
1533: by = (tabPlacement == TOP ? ty + th
1534: - bsize.height : ty);
1535: }
1536: }
1537: child.setVisible(visible);
1538: if (visible) {
1539: child.setBounds(bx, by, bw, bh);
1540: }
1541:
1542: } else {
1543: // All content children...
1544: child.setBounds(cx, cy, cw, ch);
1545: }
1546: }
1547: if (shouldChangeFocus) {
1548: if (!requestFocusForVisibleComponent()) {
1549: tabPane.requestFocus();
1550: }
1551: }
1552: }
1553: }
1554: }
1555:
1556: protected void calculateTabRects(int tabPlacement, int tabCount) {
1557: FontMetrics metrics = getFontMetrics();
1558: Dimension size = tabPane.getSize();
1559: Insets insets = tabPane.getInsets();
1560: Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
1561: int fontHeight = metrics.getHeight();
1562: int selectedIndex = tabPane.getSelectedIndex();
1563: boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
1564: boolean leftToRight = PlasticUtils.isLeftToRight(tabPane);
1565: int x = tabAreaInsets.left;
1566: int y = tabAreaInsets.top;
1567: int totalWidth = 0;
1568: int totalHeight = 0;
1569:
1570: //
1571: // Calculate bounds within which a tab run must fit
1572: //
1573: switch (tabPlacement) {
1574: case LEFT:
1575: case RIGHT:
1576: maxTabWidth = calculateMaxTabWidth(tabPlacement);
1577: break;
1578: case BOTTOM:
1579: case TOP:
1580: default:
1581: maxTabHeight = calculateMaxTabHeight(tabPlacement);
1582: }
1583:
1584: runCount = 0;
1585: selectedRun = -1;
1586:
1587: if (tabCount == 0) {
1588: return;
1589: }
1590:
1591: selectedRun = 0;
1592: runCount = 1;
1593:
1594: // Run through tabs and lay them out in a single run
1595: Rectangle rect;
1596: for (int i = 0; i < tabCount; i++) {
1597: rect = rects[i];
1598:
1599: if (!verticalTabRuns) {
1600: // Tabs on TOP or BOTTOM....
1601: if (i > 0) {
1602: rect.x = rects[i - 1].x + rects[i - 1].width;
1603: } else {
1604: tabRuns[0] = 0;
1605: maxTabWidth = 0;
1606: totalHeight += maxTabHeight;
1607: rect.x = x;
1608: }
1609: rect.width = calculateTabWidth(tabPlacement, i,
1610: metrics);
1611: totalWidth = rect.x + rect.width
1612: + renderer.getTabsOverlay();
1613: maxTabWidth = Math.max(maxTabWidth, rect.width);
1614:
1615: rect.y = y;
1616: rect.height = maxTabHeight/* - 2*/;
1617:
1618: } else {
1619: // Tabs on LEFT or RIGHT...
1620: if (i > 0) {
1621: rect.y = rects[i - 1].y + rects[i - 1].height;
1622: } else {
1623: tabRuns[0] = 0;
1624: maxTabHeight = 0;
1625: totalWidth = maxTabWidth;
1626: rect.y = y;
1627: }
1628: rect.height = calculateTabHeight(tabPlacement, i,
1629: fontHeight);
1630: totalHeight = rect.y + rect.height;
1631: maxTabHeight = Math.max(maxTabHeight, rect.height);
1632:
1633: rect.x = x;
1634: rect.width = maxTabWidth/* - 2*/;
1635:
1636: }
1637: }
1638:
1639: // Pad the selected tab so that it appears raised in front
1640: padSelectedTab(tabPlacement, selectedIndex);
1641:
1642: // if right to left and tab placement on the top or
1643: // the bottom, flip x positions and adjust by widths
1644: if (!leftToRight && !verticalTabRuns) {
1645: int rightMargin = size.width
1646: - (insets.right + tabAreaInsets.right);
1647: for (int i = 0; i < tabCount; i++) {
1648: rects[i].x = rightMargin - rects[i].x
1649: - rects[i].width;
1650: }
1651: }
1652: tabScroller.tabPanel.setPreferredSize(new Dimension(
1653: totalWidth, totalHeight));
1654: }
1655: }
1656:
1657: private class ScrollableTabSupport implements ActionListener,
1658: ChangeListener {
1659:
1660: public ScrollableTabViewport viewport;
1661: public ScrollableTabPanel tabPanel;
1662: public JButton scrollForwardButton;
1663: public JButton scrollBackwardButton;
1664: public int leadingTabIndex;
1665: private Point tabViewPosition = new Point(0, 0);
1666:
1667: ScrollableTabSupport(int tabPlacement) {
1668: viewport = new ScrollableTabViewport();
1669: tabPanel = new ScrollableTabPanel();
1670: viewport.setView(tabPanel);
1671: viewport.addChangeListener(this );
1672: createButtons();
1673: }
1674:
1675: /**
1676: * Recreates the scroll buttons and adds them to the TabbedPane.
1677: */
1678: void createButtons() {
1679: if (scrollForwardButton != null) {
1680: tabPane.remove(scrollForwardButton);
1681: scrollForwardButton.removeActionListener(this );
1682: tabPane.remove(scrollBackwardButton);
1683: scrollBackwardButton.removeActionListener(this );
1684: }
1685: int tabPlacement = tabPane.getTabPlacement();
1686: int width = UIManager.getInt("ScrollBar.width");
1687: if (tabPlacement == TOP || tabPlacement == BOTTOM) {
1688: scrollForwardButton = new ArrowButton(EAST, width);
1689: scrollBackwardButton = new ArrowButton(WEST, width);
1690: } else { // tabPlacement = LEFT || RIGHT
1691: scrollForwardButton = new ArrowButton(SOUTH, width);
1692: scrollBackwardButton = new ArrowButton(NORTH, width);
1693: }
1694: scrollForwardButton.addActionListener(this );
1695: scrollBackwardButton.addActionListener(this );
1696: tabPane.add(scrollForwardButton);
1697: tabPane.add(scrollBackwardButton);
1698: }
1699:
1700: public void scrollForward(int tabPlacement) {
1701: Dimension viewSize = viewport.getViewSize();
1702: Rectangle viewRect = viewport.getViewRect();
1703:
1704: if (tabPlacement == TOP || tabPlacement == BOTTOM) {
1705: if (viewRect.width >= viewSize.width - viewRect.x) {
1706: return; // no room left to scroll
1707: }
1708: } else { // tabPlacement == LEFT || tabPlacement == RIGHT
1709: if (viewRect.height >= viewSize.height - viewRect.y) {
1710: return;
1711: }
1712: }
1713: setLeadingTabIndex(tabPlacement, leadingTabIndex + 1);
1714: }
1715:
1716: public void scrollBackward(int tabPlacement) {
1717: if (leadingTabIndex == 0) {
1718: return; // no room left to scroll
1719: }
1720: setLeadingTabIndex(tabPlacement, leadingTabIndex - 1);
1721: }
1722:
1723: public void setLeadingTabIndex(int tabPlacement, int index) {
1724: leadingTabIndex = index;
1725: Dimension viewSize = viewport.getViewSize();
1726: Rectangle viewRect = viewport.getViewRect();
1727:
1728: switch (tabPlacement) {
1729: case TOP:
1730: case BOTTOM:
1731: tabViewPosition.x = leadingTabIndex == 0 ? 0
1732: : rects[leadingTabIndex].x
1733: - renderer.getTabsOverlay();
1734:
1735: if ((viewSize.width - tabViewPosition.x) < viewRect.width) {
1736: // We've scrolled to the end, so adjust the viewport size
1737: // to ensure the view position remains aligned on a tab
1738: // boundary
1739: Dimension extentSize = new Dimension(viewSize.width
1740: - tabViewPosition.x, viewRect.height);
1741: viewport.setExtentSize(extentSize);
1742: }
1743: break;
1744: case LEFT:
1745: case RIGHT:
1746: tabViewPosition.y = leadingTabIndex == 0 ? 0
1747: : rects[leadingTabIndex].y;
1748:
1749: if ((viewSize.height - tabViewPosition.y) < viewRect.height) {
1750: // We've scrolled to the end, so adjust the viewport size
1751: // to ensure the view position remains aligned on a tab
1752: // boundary
1753: Dimension extentSize = new Dimension(
1754: viewRect.width, viewSize.height
1755: - tabViewPosition.y);
1756: viewport.setExtentSize(extentSize);
1757: }
1758: }
1759: viewport.setViewPosition(tabViewPosition);
1760: }
1761:
1762: public void stateChanged(ChangeEvent e) {
1763: JViewport viewport = (JViewport) e.getSource();
1764: int tabPlacement = tabPane.getTabPlacement();
1765: int tabCount = tabPane.getTabCount();
1766: Rectangle vpRect = viewport.getBounds();
1767: Dimension viewSize = viewport.getViewSize();
1768: Rectangle viewRect = viewport.getViewRect();
1769:
1770: leadingTabIndex = getClosestTab(viewRect.x, viewRect.y);
1771:
1772: // If the tab isn't right aligned, adjust it.
1773: if (leadingTabIndex + 1 < tabCount) {
1774: switch (tabPlacement) {
1775: case TOP:
1776: case BOTTOM:
1777: if (rects[leadingTabIndex].x < viewRect.x) {
1778: leadingTabIndex++;
1779: }
1780: break;
1781: case LEFT:
1782: case RIGHT:
1783: if (rects[leadingTabIndex].y < viewRect.y) {
1784: leadingTabIndex++;
1785: }
1786: break;
1787: }
1788: }
1789: Insets contentInsets = getContentBorderInsets(tabPlacement);
1790: switch (tabPlacement) {
1791: case LEFT:
1792: tabPane.repaint(vpRect.x + vpRect.width, vpRect.y,
1793: contentInsets.left, vpRect.height);
1794: scrollBackwardButton.setEnabled(viewRect.y > 0
1795: && leadingTabIndex > 0);
1796: scrollForwardButton
1797: .setEnabled(leadingTabIndex < tabCount - 1
1798: && viewSize.height - viewRect.y > viewRect.height);
1799: break;
1800: case RIGHT:
1801: tabPane.repaint(vpRect.x - contentInsets.right,
1802: vpRect.y, contentInsets.right, vpRect.height);
1803: scrollBackwardButton.setEnabled(viewRect.y > 0
1804: && leadingTabIndex > 0);
1805: scrollForwardButton
1806: .setEnabled(leadingTabIndex < tabCount - 1
1807: && viewSize.height - viewRect.y > viewRect.height);
1808: break;
1809: case BOTTOM:
1810: tabPane.repaint(vpRect.x, vpRect.y
1811: - contentInsets.bottom, vpRect.width,
1812: contentInsets.bottom);
1813: scrollBackwardButton.setEnabled(viewRect.x > 0
1814: && leadingTabIndex > 0);
1815: scrollForwardButton
1816: .setEnabled(leadingTabIndex < tabCount - 1
1817: && viewSize.width - viewRect.x > viewRect.width);
1818: break;
1819: case TOP:
1820: default:
1821: tabPane.repaint(vpRect.x, vpRect.y + vpRect.height,
1822: vpRect.width, contentInsets.top);
1823: scrollBackwardButton.setEnabled(viewRect.x > 0
1824: && leadingTabIndex > 0);
1825: scrollForwardButton
1826: .setEnabled(leadingTabIndex < tabCount - 1
1827: && viewSize.width - viewRect.x > viewRect.width);
1828: }
1829: }
1830:
1831: /**
1832: * ActionListener for the scroll buttons.
1833: */
1834: public void actionPerformed(ActionEvent e) {
1835: ActionMap map = tabPane.getActionMap();
1836:
1837: if (map != null) {
1838: String actionKey;
1839:
1840: if (e.getSource() == scrollForwardButton) {
1841: actionKey = "scrollTabsForwardAction";
1842: } else {
1843: actionKey = "scrollTabsBackwardAction";
1844: }
1845: Action action = map.get(actionKey);
1846:
1847: if (action != null && action.isEnabled()) {
1848: action.actionPerformed(new ActionEvent(tabPane,
1849: ActionEvent.ACTION_PERFORMED, null, e
1850: .getWhen(), e.getModifiers()));
1851: }
1852: }
1853: }
1854:
1855: }
1856:
1857: private class ScrollableTabViewport extends JViewport implements
1858: UIResource {
1859:
1860: public ScrollableTabViewport() {
1861: super ();
1862: setName("TabbedPane.scrollableViewport");
1863: setScrollMode(SIMPLE_SCROLL_MODE);
1864: setOpaque(tabPane.isOpaque());
1865: Color bgColor = UIManager
1866: .getColor("TabbedPane.tabAreaBackground");
1867: if (bgColor == null) {
1868: bgColor = tabPane.getBackground();
1869: }
1870: setBackground(bgColor);
1871: }
1872: }
1873:
1874: private class ScrollableTabPanel extends JPanel implements
1875: UIResource {
1876:
1877: public ScrollableTabPanel() {
1878: super (null);
1879: setOpaque(tabPane.isOpaque());
1880: Color bgColor = UIManager
1881: .getColor("TabbedPane.tabAreaBackground");
1882: if (bgColor == null) {
1883: bgColor = tabPane.getBackground();
1884: }
1885: setBackground(bgColor);
1886: }
1887:
1888: public void paintComponent(Graphics g) {
1889: super .paintComponent(g);
1890: PlasticTabbedPaneUI.this .paintTabArea(g, tabPane
1891: .getTabPlacement(), tabPane.getSelectedIndex());
1892:
1893: }
1894: }
1895:
1896: private static class ArrowButton extends JButton implements
1897: UIResource {
1898:
1899: private final int buttonWidth;
1900: private final int direction;
1901: private boolean mouseIsOver;
1902:
1903: ArrowButton(int direction, int buttonWidth) {
1904: this .direction = direction;
1905: this .buttonWidth = buttonWidth;
1906: setRequestFocusEnabled(false);
1907: }
1908:
1909: protected void processMouseEvent(MouseEvent e) {
1910: super .processMouseEvent(e);
1911: switch (e.getID()) {
1912: case MouseEvent.MOUSE_ENTERED:
1913: mouseIsOver = true;
1914: revalidate();
1915: repaint();
1916: break;
1917: case MouseEvent.MOUSE_EXITED:
1918: mouseIsOver = false;
1919: revalidate();
1920: repaint();
1921: break;
1922: }
1923: }
1924:
1925: protected void paintBorder(Graphics g) {
1926: if (mouseIsOver && isEnabled()) {
1927: super .paintBorder(g);
1928: }
1929: }
1930:
1931: protected void paintComponent(Graphics g) {
1932: if (mouseIsOver) {
1933: super .paintComponent(g);
1934: } else {
1935: g.setColor(getBackground());
1936: g.fillRect(0, 0, getWidth(), getHeight());
1937: }
1938: paintArrow(g);
1939: }
1940:
1941: private void paintArrow(Graphics g) {
1942: Color oldColor = g.getColor();
1943:
1944: boolean isEnabled = isEnabled();
1945: g.setColor(isEnabled ? PlasticLookAndFeel.getControlInfo()
1946: : PlasticLookAndFeel.getControlDisabled());
1947:
1948: int arrowWidth, arrowHeight;
1949: switch (direction) {
1950: case NORTH:
1951: case SOUTH:
1952: arrowWidth = 9;
1953: arrowHeight = 5;
1954: break;
1955: case WEST:
1956: case EAST:
1957: default:
1958: arrowWidth = 5;
1959: arrowHeight = 9;
1960: break;
1961: }
1962: int x = (getWidth() - arrowWidth) / 2;
1963: int y = (getHeight() - arrowHeight) / 2;
1964: g.translate(x, y);
1965:
1966: boolean paintShadow = !mouseIsOver || !isEnabled;
1967: Color shadow = isEnabled ? PlasticLookAndFeel
1968: .getControlShadow() : UIManager
1969: .getColor("ScrollBar.highlight");
1970:
1971: switch (direction) {
1972: case NORTH:
1973: g.fillRect(0, 4, 9, 1);
1974: g.fillRect(1, 3, 7, 1);
1975: g.fillRect(2, 2, 5, 1);
1976: g.fillRect(3, 1, 3, 1);
1977: g.fillRect(4, 0, 1, 1);
1978: if (paintShadow) {
1979: g.setColor(shadow);
1980: g.fillRect(1, 5, 9, 1);
1981: }
1982: break;
1983: case SOUTH:
1984: g.fillRect(0, 0, 9, 1);
1985: g.fillRect(1, 1, 7, 1);
1986: g.fillRect(2, 2, 5, 1);
1987: g.fillRect(3, 3, 3, 1);
1988: g.fillRect(4, 4, 1, 1);
1989: if (paintShadow) {
1990: g.setColor(shadow);
1991: g.drawLine(5, 4, 8, 1);
1992: g.drawLine(5, 5, 9, 1);
1993: }
1994: break;
1995: case WEST:
1996: g.fillRect(0, 4, 1, 1);
1997: g.fillRect(1, 3, 1, 3);
1998: g.fillRect(2, 2, 1, 5);
1999: g.fillRect(3, 1, 1, 7);
2000: g.fillRect(4, 0, 1, 9);
2001: if (paintShadow) {
2002: g.setColor(shadow);
2003: g.fillRect(5, 1, 1, 9);
2004: }
2005: break;
2006: case EAST:
2007: g.fillRect(0, 0, 1, 9);
2008: g.fillRect(1, 1, 1, 7);
2009: g.fillRect(2, 2, 1, 5);
2010: g.fillRect(3, 3, 1, 3);
2011: g.fillRect(4, 4, 1, 1);
2012: if (paintShadow) {
2013: g.setColor(shadow);
2014: g.drawLine(1, 8, 4, 5);
2015: g.drawLine(1, 9, 5, 5);
2016: }
2017: break;
2018: }
2019:
2020: g.translate(-x, -y);
2021: g.setColor(oldColor);
2022: }
2023:
2024: public Dimension getPreferredSize() {
2025: return new Dimension(buttonWidth, buttonWidth);
2026: }
2027:
2028: public Dimension getMinimumSize() {
2029: return getPreferredSize();
2030: }
2031:
2032: public Dimension getMaximumSize() {
2033: return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
2034: }
2035: }
2036:
2037: /**
2038: * This is the abstract superclass for all TabbedPane renderers.
2039: * Those will be defined in the rest of this file
2040: */
2041: private abstract static class AbstractRenderer {
2042:
2043: protected static final Insets EMPTY_INSETS = new Insets(0, 0,
2044: 0, 0);
2045: protected static final Insets NORTH_INSETS = new Insets(1, 0,
2046: 0, 0);
2047: protected static final Insets WEST_INSETS = new Insets(0, 1, 0,
2048: 0);
2049: protected static final Insets SOUTH_INSETS = new Insets(0, 0,
2050: 1, 0);
2051: protected static final Insets EAST_INSETS = new Insets(0, 0, 0,
2052: 1);
2053:
2054: protected final JTabbedPane tabPane;
2055: protected final int tabPlacement;
2056: protected Color shadowColor;
2057: protected Color darkShadow;
2058: protected Color selectColor;
2059: protected Color selectLight;
2060: protected Color selectHighlight;
2061: protected Color lightHighlight;
2062: protected Color focus;
2063:
2064: private AbstractRenderer(JTabbedPane tabPane) {
2065: initColors();
2066: this .tabPane = tabPane;
2067: this .tabPlacement = tabPane.getTabPlacement();
2068: }
2069:
2070: private static AbstractRenderer createRenderer(
2071: JTabbedPane tabPane) {
2072: switch (tabPane.getTabPlacement()) {
2073: case SwingConstants.TOP:
2074: return new TopRenderer(tabPane);
2075: case SwingConstants.BOTTOM:
2076: return new BottomRenderer(tabPane);
2077: case SwingConstants.LEFT:
2078: return new LeftRenderer(tabPane);
2079: case SwingConstants.RIGHT:
2080: return new RightRenderer(tabPane);
2081: default:
2082: return new TopRenderer(tabPane);
2083: }
2084: }
2085:
2086: private static AbstractRenderer createEmbeddedRenderer(
2087: JTabbedPane tabPane) {
2088: switch (tabPane.getTabPlacement()) {
2089: case SwingConstants.TOP:
2090: return new TopEmbeddedRenderer(tabPane);
2091: case SwingConstants.BOTTOM:
2092: return new BottomEmbeddedRenderer(tabPane);
2093: case SwingConstants.LEFT:
2094: return new LeftEmbeddedRenderer(tabPane);
2095: case SwingConstants.RIGHT:
2096: return new RightEmbeddedRenderer(tabPane);
2097: default:
2098: return new TopEmbeddedRenderer(tabPane);
2099: }
2100: }
2101:
2102: private void initColors() {
2103: shadowColor = UIManager.getColor("TabbedPane.shadow");
2104: darkShadow = UIManager.getColor("TabbedPane.darkShadow");
2105: selectColor = UIManager.getColor("TabbedPane.selected");
2106: focus = UIManager.getColor("TabbedPane.focus");
2107: selectHighlight = UIManager
2108: .getColor("TabbedPane.selectHighlight");
2109: lightHighlight = UIManager.getColor("TabbedPane.highlight");
2110: selectLight = new Color(
2111: (2 * selectColor.getRed() + selectHighlight
2112: .getRed()) / 3,
2113: (2 * selectColor.getGreen() + selectHighlight
2114: .getGreen()) / 3, (2 * selectColor
2115: .getBlue() + selectHighlight.getBlue()) / 3);
2116: }
2117:
2118: protected boolean isFirstDisplayedTab(int tabIndex,
2119: int position, int paneBorder) {
2120: return tabIndex == 0;
2121: // return (position - paneBorder) < 8;
2122: }
2123:
2124: protected Insets getTabAreaInsets(Insets defaultInsets) {
2125: return defaultInsets;
2126: }
2127:
2128: protected Insets getContentBorderInsets(Insets defaultInsets) {
2129: return defaultInsets;
2130: }
2131:
2132: /**
2133: * Returns the amount by which the label should be shifted horizontally.
2134: */
2135: protected int getTabLabelShiftX(int tabIndex, boolean isSelected) {
2136: return 0;
2137: }
2138:
2139: /**
2140: * Returns the amount by which the label should be shifted vertically.
2141: */
2142: protected int getTabLabelShiftY(int tabIndex, boolean isSelected) {
2143: return 0;
2144: }
2145:
2146: /**
2147: * Returns the amount of overlap for two Runs.
2148: */
2149: protected int getTabRunOverlay(int tabRunOverlay) {
2150: return tabRunOverlay;
2151: }
2152:
2153: /**
2154: * Returns if a run should be padded with empty space
2155: * to take up as much room as the others.
2156: */
2157: protected boolean shouldPadTabRun(int run, boolean aPriori) {
2158: return aPriori;
2159: }
2160:
2161: /**
2162: * Returns the amount by which the run number <code>run</code>
2163: * should be indented. Add a few pixels for every run to make
2164: * diagonal lines align.
2165: */
2166: protected int getTabRunIndent(int run) {
2167: return 0;
2168: }
2169:
2170: /**
2171: * Returns the insets for the given tab.
2172: */
2173: protected abstract Insets getTabInsets(int tabIndex,
2174: Insets tabInsets);
2175:
2176: /**
2177: * Draws the rectancle around the Tab label which indicates keyboard focus.
2178: */
2179: protected abstract void paintFocusIndicator(Graphics g,
2180: Rectangle[] rects, int tabIndex, Rectangle iconRect,
2181: Rectangle textRect, boolean isSelected);
2182:
2183: /**
2184: * Fills the background of the given tab to make sure overlap of
2185: * tabs is handled correctly.
2186: */
2187: protected abstract void paintTabBackground(Graphics g,
2188: int tabIndex, int x, int y, int w, int h,
2189: boolean isSelected);
2190:
2191: /**
2192: * Paints the border around the given tab.
2193: */
2194: protected abstract void paintTabBorder(Graphics g,
2195: int tabIndex, int x, int y, int w, int h,
2196: boolean isSelected);
2197:
2198: /**
2199: * Returns additional the insets for the selected tab. This allows to "raise"
2200: * The selected tab over the others
2201: */
2202: protected Insets getSelectedTabPadInsets() {
2203: return EMPTY_INSETS;
2204: }
2205:
2206: /**
2207: * Draws the top edge of the border around the content area.
2208: * Draw unbroken line for tabs are not on TOP
2209: * override where appropriate.
2210: */
2211: protected void paintContentBorderTopEdge(Graphics g, int x,
2212: int y, int w, int h, boolean drawBroken,
2213: Rectangle selRect, boolean isContentBorderPainted) {
2214: if (isContentBorderPainted) {
2215: g.setColor(selectHighlight);
2216: g.fillRect(x, y, w - 1, 1);
2217: }
2218: }
2219:
2220: /**
2221: * Draws the bottom edge of the Border around the content area.
2222: * Draw broken line if selected tab is visible and adjacent to content
2223: * and TabPlacement is same as painted edge.
2224: */
2225: protected void paintContentBorderBottomEdge(Graphics g, int x,
2226: int y, int w, int h, boolean drawBroken,
2227: Rectangle selRect, boolean isContentBorderPainted) {
2228: if (isContentBorderPainted) {
2229: g.setColor(darkShadow);
2230: g.fillRect(x, y + h - 1, w - 1, 1);
2231: }
2232: }
2233:
2234: /**
2235: * Draws the left edge of the Border around the content area.
2236: * Draw broken line if selected tab is visible and adjacent to content
2237: * and TabPlacement is same as painted edge
2238: */
2239: protected void paintContentBorderLeftEdge(Graphics g, int x,
2240: int y, int w, int h, boolean drawBroken,
2241: Rectangle selRect, boolean isContentBorderPainted) {
2242: if (isContentBorderPainted) {
2243: g.setColor(selectHighlight);
2244: g.fillRect(x, y, 1, h - 1);
2245: }
2246: }
2247:
2248: /**
2249: * Draws the right edge of the Border around the content area.
2250: * Draw broken line if selected tab is visible and adjacent to content
2251: * and TabPlacement is same as painted edge
2252: */
2253: protected void paintContentBorderRightEdge(Graphics g, int x,
2254: int y, int w, int h, boolean drawBroken,
2255: Rectangle selRect, boolean isContentBorderPainted) {
2256: if (isContentBorderPainted) {
2257: g.setColor(darkShadow);
2258: g.fillRect(x + w - 1, y, 1, h);
2259: }
2260: }
2261:
2262: /**
2263: * Returns the amount of overlap for two tabs.
2264: */
2265: protected int getTabsOverlay() {
2266: return 0;
2267: }
2268: }
2269:
2270: /**
2271: * The renderer for the case where tabs are displayed below the contents
2272: * and with minimal decoration.
2273: */
2274: private static final class BottomEmbeddedRenderer extends
2275: AbstractRenderer {
2276:
2277: private BottomEmbeddedRenderer(JTabbedPane tabPane) {
2278: super (tabPane);
2279: }
2280:
2281: protected Insets getTabAreaInsets(Insets insets) {
2282: return EMPTY_INSETS;
2283: }
2284:
2285: protected Insets getContentBorderInsets(Insets defaultInsets) {
2286: return SOUTH_INSETS;
2287: }
2288:
2289: protected Insets getSelectedTabPadInsets() {
2290: return EMPTY_INSETS;
2291: }
2292:
2293: protected Insets getTabInsets(int tabIndex, Insets tabInsets) {
2294: return new Insets(tabInsets.top, tabInsets.left,
2295: tabInsets.bottom, tabInsets.right);
2296: }
2297:
2298: /**
2299: * Paints no focus: minimal decoration is really minimal.
2300: */
2301: protected void paintFocusIndicator(Graphics g,
2302: Rectangle[] rects, int tabIndex, Rectangle iconRect,
2303: Rectangle textRect, boolean isSelected) {
2304: // Embedded tabs paint no focus.
2305: }
2306:
2307: protected void paintTabBackground(Graphics g, int tabIndex,
2308: int x, int y, int w, int h, boolean isSelected) {
2309:
2310: g.setColor(selectColor);
2311: g.fillRect(x, y, w + 1, h);
2312: }
2313:
2314: protected void paintTabBorder(Graphics g, int tabIndex, int x,
2315: int y, int w, int h, boolean isSelected) {
2316:
2317: int bottom = h;
2318: int right = w + 1;
2319:
2320: g.translate(x, y);
2321: if (isFirstDisplayedTab(tabIndex, x, tabPane.getBounds().x)) {
2322: if (isSelected) {
2323: // selected and first in line
2324: g.setColor(shadowColor);
2325: g.fillRect(right, 0, 1, bottom - 1);
2326: g.fillRect(right - 1, bottom - 1, 1, 1);
2327: // it is open to discussion if the outer border of the tab
2328: // should be painted because in the primary case it won't
2329: // be visible anyway. uncomment the following two lines if wanted
2330: // g.fillRect(0,bottom, right, 1);
2331: // g.fillRect(-1,0,1,bottom;
2332: g.setColor(selectHighlight);
2333: g.fillRect(0, 0, 1, bottom);
2334: g.fillRect(right - 1, 0, 1, bottom - 1);
2335: g.fillRect(1, bottom - 1, right - 2, 1);
2336: } else {
2337: //not selected and first in line
2338: }
2339: } else {
2340: if (isSelected) {
2341: //selected and not first in line
2342: g.setColor(shadowColor);
2343: g.fillRect(0, 0, 1, bottom - 1);
2344: g.fillRect(1, bottom - 1, 1, 1);
2345: g.fillRect(right, 0, 1, bottom - 1);
2346: g.fillRect(right - 1, bottom - 1, 1, 1);
2347: // outside line:
2348: // g.fillRect(2,bottom, right-3, 1);
2349: g.setColor(selectHighlight);
2350: g.fillRect(1, 0, 1, bottom - 1);
2351: g.fillRect(right - 1, 0, 1, bottom - 1);
2352: g.fillRect(2, bottom - 1, right - 3, 1);
2353: } else {
2354: g.setColor(shadowColor);
2355: g.fillRect(1, h / 2, 1, h - (h / 2));
2356: }
2357: }
2358: g.translate(-x, -y);
2359: }
2360:
2361: protected void paintContentBorderBottomEdge(Graphics g, int x,
2362: int y, int w, int h, boolean drawBroken,
2363: Rectangle selRect, boolean isContentBorderPainted) {
2364:
2365: g.setColor(shadowColor);
2366: g.fillRect(x, y + h - 1, w, 1);
2367: }
2368:
2369: }
2370:
2371: /**
2372: * The renderer for the case where Tabs are below the content and
2373: * decoration is standard.
2374: */
2375: private static final class BottomRenderer extends AbstractRenderer {
2376:
2377: private BottomRenderer(JTabbedPane tabPane) {
2378: super (tabPane);
2379: }
2380:
2381: protected Insets getTabAreaInsets(Insets defaultInsets) {
2382: return new Insets(defaultInsets.top,
2383: defaultInsets.left + 5, defaultInsets.bottom,
2384: defaultInsets.right);
2385: }
2386:
2387: protected int getTabLabelShiftY(int tabIndex, boolean isSelected) {
2388: return isSelected ? 0 : -1;
2389: }
2390:
2391: protected int getTabRunOverlay(int tabRunOverlay) {
2392: return tabRunOverlay - 2;
2393: }
2394:
2395: protected int getTabRunIndent(int run) {
2396: return 6 * run;
2397: }
2398:
2399: protected Insets getSelectedTabPadInsets() {
2400: return SOUTH_INSETS;
2401: }
2402:
2403: protected Insets getTabInsets(int tabIndex, Insets tabInsets) {
2404: return new Insets(tabInsets.top, tabInsets.left - 2,
2405: tabInsets.bottom, tabInsets.right - 2);
2406: }
2407:
2408: protected void paintFocusIndicator(Graphics g,
2409: Rectangle[] rects, int tabIndex, Rectangle iconRect,
2410: Rectangle textRect, boolean isSelected) {
2411:
2412: if (!tabPane.hasFocus() || !isSelected)
2413: return;
2414: Rectangle tabRect = rects[tabIndex];
2415: int top = tabRect.y;
2416: int left = tabRect.x + 6;
2417: int height = tabRect.height - 3;
2418: int width = tabRect.width - 12;
2419: g.setColor(focus);
2420: g.drawRect(left, top, width, height);
2421: }
2422:
2423: protected void paintTabBackground(Graphics g, int tabIndex,
2424: int x, int y, int w, int h, boolean isSelected) {
2425:
2426: g.setColor(selectColor);
2427: g.fillRect(x, y, w, h);
2428: }
2429:
2430: protected void paintTabBorder(Graphics g, int tabIndex, int x,
2431: int y, int w, int h, boolean isSelected) {
2432:
2433: int bottom = h - 1;
2434: int right = w + 4;
2435:
2436: g.translate(x - 3, y);
2437:
2438: // Paint Border
2439: g.setColor(selectHighlight);
2440:
2441: // Paint left
2442: g.fillRect(0, 0, 1, 2);
2443: g.drawLine(0, 2, 4, bottom - 4);
2444: g.fillRect(5, bottom - 3, 1, 2);
2445: g.fillRect(6, bottom - 1, 1, 1);
2446:
2447: // Paint bootom
2448: g.fillRect(7, bottom, 1, 1);
2449: g.setColor(darkShadow);
2450: g.fillRect(8, bottom, right - 13, 1);
2451:
2452: // Paint right
2453: g.drawLine(right + 1, 0, right - 3, bottom - 4);
2454: g.fillRect(right - 4, bottom - 3, 1, 2);
2455: g.fillRect(right - 5, bottom - 1, 1, 1);
2456:
2457: g.translate(-x + 3, -y);
2458: }
2459:
2460: protected void paintContentBorderBottomEdge(Graphics g, int x,
2461: int y, int w, int h, boolean drawBroken,
2462: Rectangle selRect, boolean isContentBorderPainted) {
2463: int bottom = y + h - 1;
2464: int right = x + w - 1;
2465: g.translate(x, bottom);
2466: if (drawBroken && selRect.x >= x && selRect.x <= x + w) {
2467: // Break line to show visual connection to selected tab
2468: g.setColor(darkShadow);
2469: g.fillRect(0, 0, selRect.x - x - 2, 1);
2470: if (selRect.x + selRect.width < x + w - 2) {
2471: g.setColor(darkShadow);
2472: g.fillRect(selRect.x + selRect.width + 2 - x, 0,
2473: right - selRect.x - selRect.width - 2, 1);
2474: }
2475: } else {
2476: g.setColor(darkShadow);
2477: g.fillRect(0, 0, w - 1, 1);
2478: }
2479: g.translate(-x, -bottom);
2480: }
2481:
2482: protected int getTabsOverlay() {
2483: return 4;
2484: }
2485:
2486: }
2487:
2488: /**
2489: * The renderer for tabs on the left with minimal decoration.
2490: */
2491: private static final class LeftEmbeddedRenderer extends
2492: AbstractRenderer {
2493:
2494: private LeftEmbeddedRenderer(JTabbedPane tabPane) {
2495: super (tabPane);
2496: }
2497:
2498: protected Insets getTabAreaInsets(Insets insets) {
2499: return EMPTY_INSETS;
2500: }
2501:
2502: protected Insets getContentBorderInsets(Insets defaultInsets) {
2503: return WEST_INSETS;
2504: }
2505:
2506: protected int getTabRunOverlay(int tabRunOverlay) {
2507: return 0;
2508: }
2509:
2510: protected boolean shouldPadTabRun(int run, boolean aPriori) {
2511: return false;
2512: }
2513:
2514: protected Insets getTabInsets(int tabIndex, Insets tabInsets) {
2515: return new Insets(tabInsets.top, tabInsets.left,
2516: tabInsets.bottom, tabInsets.right);
2517: }
2518:
2519: protected Insets getSelectedTabPadInsets() {
2520: return EMPTY_INSETS;
2521: }
2522:
2523: /**
2524: * minimal decoration is really minimal: no focus.
2525: */
2526: protected void paintFocusIndicator(Graphics g,
2527: Rectangle[] rects, int tabIndex, Rectangle iconRect,
2528: Rectangle textRect, boolean isSelected) {
2529: // Embedded tabs paint no focus.
2530: }
2531:
2532: protected void paintTabBackground(Graphics g, int tabIndex,
2533: int x, int y, int w, int h, boolean isSelected) {
2534: g.setColor(selectColor);
2535: g.fillRect(x, y, w, h);
2536: }
2537:
2538: protected void paintTabBorder(Graphics g, int tabIndex, int x,
2539: int y, int w, int h, boolean isSelected) {
2540:
2541: int bottom = h;
2542: int right = w;
2543:
2544: g.translate(x, y);
2545:
2546: if (isFirstDisplayedTab(tabIndex, y, tabPane.getBounds().y)) {
2547: if (isSelected) {
2548: //selected and first in line
2549: g.setColor(selectHighlight);
2550: g.fillRect(0, 0, right, 1);
2551: g.fillRect(0, 0, 1, bottom - 1);
2552: g.fillRect(1, bottom - 1, right - 1, 1);
2553: g.setColor(shadowColor);
2554: g.fillRect(0, bottom - 1, 1, 1);
2555: g.fillRect(1, bottom, right - 1, 1);
2556: // outside line:
2557: // g.fillRect(-1,0,1,bottom-1)
2558: } else {
2559: //not selected but first in line
2560: }
2561: } else {
2562: if (isSelected) {
2563: //selected but not first in line
2564: g.setColor(selectHighlight);
2565: g.fillRect(1, 1, right - 1, 1);
2566: g.fillRect(0, 2, 1, bottom - 2);
2567: g.fillRect(1, bottom - 1, right - 1, 1);
2568: g.setColor(shadowColor);
2569: g.fillRect(1, 0, right - 1, 1);
2570: g.fillRect(0, 1, 1, 1);
2571: g.fillRect(0, bottom - 1, 1, 1);
2572: g.fillRect(1, bottom, right - 1, 1);
2573: // outside line:
2574: // g.fillRect(-1,2,1,bottom-3)
2575: } else {
2576: g.setColor(shadowColor);
2577: g.fillRect(0, 0, right / 3, 1);
2578: }
2579: }
2580:
2581: g.translate(-x, -y);
2582: }
2583:
2584: protected void paintContentBorderLeftEdge(Graphics g, int x,
2585: int y, int w, int h, boolean drawBroken,
2586: Rectangle selRect, boolean isContentBorderPainted) {
2587: g.setColor(shadowColor);
2588: g.fillRect(x, y, 1, h);
2589: }
2590: }
2591:
2592: /**
2593: * Renderer for tabs on the left with normal decoration.
2594: */
2595: private static final class LeftRenderer extends AbstractRenderer {
2596:
2597: private LeftRenderer(JTabbedPane tabPane) {
2598: super (tabPane);
2599: }
2600:
2601: protected Insets getTabAreaInsets(Insets defaultInsets) {
2602: return new Insets(defaultInsets.top + 4,
2603: defaultInsets.left, defaultInsets.bottom,
2604: defaultInsets.right);
2605: }
2606:
2607: protected int getTabLabelShiftX(int tabIndex, boolean isSelected) {
2608: return 1;
2609: }
2610:
2611: protected int getTabRunOverlay(int tabRunOverlay) {
2612: return 1;
2613: }
2614:
2615: protected boolean shouldPadTabRun(int run, boolean aPriori) {
2616: return false;
2617: }
2618:
2619: protected Insets getTabInsets(int tabIndex, Insets tabInsets) {
2620: return new Insets(tabInsets.top, tabInsets.left - 5,
2621: tabInsets.bottom + 1, tabInsets.right - 5);
2622: }
2623:
2624: protected Insets getSelectedTabPadInsets() {
2625: return WEST_INSETS;
2626: }
2627:
2628: protected void paintFocusIndicator(Graphics g,
2629: Rectangle[] rects, int tabIndex, Rectangle iconRect,
2630: Rectangle textRect, boolean isSelected) {
2631:
2632: if (!tabPane.hasFocus() || !isSelected)
2633: return;
2634: Rectangle tabRect = rects[tabIndex];
2635: int top = tabRect.y + 2;
2636: int left = tabRect.x + 3;
2637: int height = tabRect.height - 5;
2638: int width = tabRect.width - 6;
2639: g.setColor(focus);
2640: g.drawRect(left, top, width, height);
2641: }
2642:
2643: protected void paintTabBackground(Graphics g, int tabIndex,
2644: int x, int y, int w, int h, boolean isSelected) {
2645: if (!isSelected) {
2646: g.setColor(selectLight);
2647: g.fillRect(x + 1, y + 1, w - 1, h - 2);
2648: } else {
2649: g.setColor(selectColor);
2650: g.fillRect(x + 1, y + 1, w - 3, h - 2);
2651: }
2652: }
2653:
2654: protected void paintTabBorder(Graphics g, int tabIndex, int x,
2655: int y, int w, int h, boolean isSelected) {
2656:
2657: int bottom = h - 1;
2658: int left = 0;
2659: g.translate(x, y);
2660:
2661: // Paint Border
2662: g.setColor(selectHighlight);
2663: // Paint top
2664: g.fillRect(left + 2, 0, w - 2 - left, 1);
2665:
2666: // Paint left
2667: g.fillRect(left + 1, 1, 1, 1);
2668: g.fillRect(left, 2, 1, bottom - 3);
2669: g.setColor(darkShadow);
2670: g.fillRect(left + 1, bottom - 1, 1, 1);
2671:
2672: // Paint bottom
2673: g.fillRect(left + 2, bottom, w - 2 - left, 1);
2674:
2675: g.translate(-x, -y);
2676: }
2677:
2678: protected void paintContentBorderLeftEdge(Graphics g, int x,
2679: int y, int w, int h, boolean drawBroken,
2680: Rectangle selRect, boolean isContentBorderPainted) {
2681: g.setColor(selectHighlight);
2682: if (drawBroken && selRect.y >= y && selRect.y <= y + h) {
2683: // Break line to show visual connection to selected tab
2684: g.fillRect(x, y, 1, selRect.y + 1 - y);
2685: if (selRect.y + selRect.height < y + h - 2) {
2686: g.fillRect(x, selRect.y + selRect.height - 1, 1, y
2687: + h - selRect.y - selRect.height);
2688: }
2689: } else {
2690: g.fillRect(x, y, 1, h - 1);
2691: }
2692: }
2693:
2694: }
2695:
2696: /**
2697: * The renderer for tabs on the right with minimal decoration.
2698: */
2699: private static final class RightEmbeddedRenderer extends
2700: AbstractRenderer {
2701:
2702: private RightEmbeddedRenderer(JTabbedPane tabPane) {
2703: super (tabPane);
2704: }
2705:
2706: protected Insets getTabAreaInsets(Insets insets) {
2707: return EMPTY_INSETS;
2708: }
2709:
2710: protected Insets getContentBorderInsets(Insets defaultInsets) {
2711: return EAST_INSETS;
2712: }
2713:
2714: protected int getTabRunIndent(int run) {
2715: return 4 * run;
2716: }
2717:
2718: protected int getTabRunOverlay(int tabRunOverlay) {
2719: return 0;
2720: }
2721:
2722: protected boolean shouldPadTabRun(int run, boolean aPriori) {
2723: return false;
2724: }
2725:
2726: protected Insets getTabInsets(int tabIndex, Insets tabInsets) {
2727: return new Insets(tabInsets.top, tabInsets.left,
2728: tabInsets.bottom, tabInsets.right);
2729: }
2730:
2731: protected Insets getSelectedTabPadInsets() {
2732: return EMPTY_INSETS;
2733: }
2734:
2735: /**
2736: * Minimal decoration: no focus.
2737: */
2738: protected void paintFocusIndicator(Graphics g,
2739: Rectangle[] rects, int tabIndex, Rectangle iconRect,
2740: Rectangle textRect, boolean isSelected) {
2741: // Embedded tabs paint no focus.
2742: }
2743:
2744: protected void paintTabBackground(Graphics g, int tabIndex,
2745: int x, int y, int w, int h, boolean isSelected) {
2746:
2747: g.setColor(selectColor);
2748: g.fillRect(x, y, w, h);
2749: }
2750:
2751: protected void paintTabBorder(Graphics g, int tabIndex, int x,
2752: int y, int w, int h, boolean isSelected) {
2753:
2754: int bottom = h;
2755: int right = w - 1;
2756:
2757: g.translate(x + 1, y);
2758:
2759: if (isFirstDisplayedTab(tabIndex, y, tabPane.getBounds().y)) {
2760: if (isSelected) {
2761: //selected and first in line
2762: g.setColor(shadowColor);
2763: //outside lines:
2764: // g.fillRect(0,-1,right,1);
2765: // g.fillRect(right,-1,1,bottom);
2766: g.fillRect(right - 1, bottom - 1, 1, 1);
2767: g.fillRect(0, bottom, right - 1, 1);
2768: g.setColor(selectHighlight);
2769: g.fillRect(0, 0, right - 1, 1);
2770: g.fillRect(right - 1, 0, 1, bottom - 1);
2771: g.fillRect(0, bottom - 1, right - 1, 1);
2772: }
2773: } else {
2774: if (isSelected) {
2775: //selected but not first in line
2776: g.setColor(shadowColor);
2777: g.fillRect(0, -1, right - 1, 1);
2778: g.fillRect(right - 1, 0, 1, 1);
2779: //outside line:
2780: // g.fillRect(right,0,1,bottom);
2781: g.fillRect(right - 1, bottom - 1, 1, 1);
2782: g.fillRect(0, bottom, right - 1, 1);
2783: g.setColor(selectHighlight);
2784: g.fillRect(0, 0, right - 1, 1);
2785: g.fillRect(right - 1, 1, 1, bottom - 2);
2786: g.fillRect(0, bottom - 1, right - 1, 1);
2787: } else {
2788: //not selected and not first in line
2789: g.setColor(shadowColor);
2790: g.fillRect(2 * right / 3, 0, right / 3, 1);
2791: }
2792: }
2793: g.translate(-x - 1, -y);
2794: }
2795:
2796: protected void paintContentBorderRightEdge(Graphics g, int x,
2797: int y, int w, int h, boolean drawBroken,
2798: Rectangle selRect, boolean isContentBorderPainted) {
2799: g.setColor(shadowColor);
2800: g.fillRect(x + w - 1, y, 1, h);
2801: }
2802:
2803: }
2804:
2805: /**
2806: * Renderer for tabs on the right with normal decoration.
2807: */
2808: private static final class RightRenderer extends AbstractRenderer {
2809:
2810: private RightRenderer(JTabbedPane tabPane) {
2811: super (tabPane);
2812: }
2813:
2814: protected int getTabLabelShiftX(int tabIndex, boolean isSelected) {
2815: return 1;
2816: }
2817:
2818: protected int getTabRunOverlay(int tabRunOverlay) {
2819: return 1;
2820: }
2821:
2822: protected boolean shouldPadTabRun(int run, boolean aPriori) {
2823: return false;
2824: }
2825:
2826: protected Insets getTabInsets(int tabIndex, Insets tabInsets) {
2827: return new Insets(tabInsets.top, tabInsets.left - 5,
2828: tabInsets.bottom + 1, tabInsets.right - 5);
2829: }
2830:
2831: protected Insets getSelectedTabPadInsets() {
2832: return EAST_INSETS;
2833: }
2834:
2835: protected void paintFocusIndicator(Graphics g,
2836: Rectangle[] rects, int tabIndex, Rectangle iconRect,
2837: Rectangle textRect, boolean isSelected) {
2838:
2839: if (!tabPane.hasFocus() || !isSelected)
2840: return;
2841: Rectangle tabRect = rects[tabIndex];
2842: int top = tabRect.y + 2;
2843: int left = tabRect.x + 3;
2844: int height = tabRect.height - 5;
2845: int width = tabRect.width - 6;
2846: g.setColor(focus);
2847: g.drawRect(left, top, width, height);
2848: }
2849:
2850: protected void paintTabBackground(Graphics g, int tabIndex,
2851: int x, int y, int w, int h, boolean isSelected) {
2852: if (!isSelected) {
2853: g.setColor(selectLight);
2854: g.fillRect(x, y, w, h);
2855: } else {
2856: g.setColor(selectColor);
2857: g.fillRect(x + 2, y, w - 2, h);
2858: }
2859: }
2860:
2861: protected void paintTabBorder(Graphics g, int tabIndex, int x,
2862: int y, int w, int h, boolean isSelected) {
2863:
2864: int bottom = h - 1;
2865: int right = w;
2866:
2867: g.translate(x, y);
2868:
2869: // Paint Border
2870:
2871: g.setColor(selectHighlight);
2872: g.fillRect(0, 0, right - 1, 1);
2873: // Paint right
2874: g.setColor(darkShadow);
2875: g.fillRect(right - 1, 1, 1, 1);
2876: g.fillRect(right, 2, 1, bottom - 3);
2877: // Paint bottom
2878: g.fillRect(right - 1, bottom - 1, 1, 1);
2879: g.fillRect(0, bottom, right - 1, 1);
2880:
2881: g.translate(-x, -y);
2882: }
2883:
2884: protected void paintContentBorderRightEdge(Graphics g, int x,
2885: int y, int w, int h, boolean drawBroken,
2886: Rectangle selRect, boolean isContentBorderPainted) {
2887: g.setColor(darkShadow);
2888: if (drawBroken && selRect.y >= y && selRect.y <= y + h) {
2889: // Break line to show visual connection to selected tab
2890: g.fillRect(x + w - 1, y, 1, selRect.y - y);
2891: if (selRect.y + selRect.height < y + h - 2) {
2892: g.fillRect(x + w - 1, selRect.y + selRect.height,
2893: 1, y + h - selRect.y - selRect.height);
2894: }
2895: } else {
2896: g.fillRect(x + w - 1, y, 1, h - 1);
2897: }
2898: }
2899: }
2900:
2901: /**
2902: * Renderer for tabs on top with minimal decoration.
2903: */
2904: private static final class TopEmbeddedRenderer extends
2905: AbstractRenderer {
2906:
2907: private TopEmbeddedRenderer(JTabbedPane tabPane) {
2908: super (tabPane);
2909: }
2910:
2911: protected Insets getTabAreaInsets(Insets insets) {
2912: return EMPTY_INSETS;
2913: }
2914:
2915: protected Insets getContentBorderInsets(Insets defaultInsets) {
2916: return NORTH_INSETS;
2917: }
2918:
2919: protected Insets getTabInsets(int tabIndex, Insets tabInsets) {
2920: return new Insets(tabInsets.top, tabInsets.left + 1,
2921: tabInsets.bottom, tabInsets.right);
2922: }
2923:
2924: protected Insets getSelectedTabPadInsets() {
2925: return EMPTY_INSETS;
2926: }
2927:
2928: /**
2929: * Minimal decoration: no focus.
2930: */
2931: protected void paintFocusIndicator(Graphics g,
2932: Rectangle[] rects, int tabIndex, Rectangle iconRect,
2933: Rectangle textRect, boolean isSelected) {
2934: // Embedded tabs paint no focus.
2935: }
2936:
2937: protected void paintTabBackground(Graphics g, int tabIndex,
2938: int x, int y, int w, int h, boolean isSelected) {
2939:
2940: g.setColor(selectColor);
2941: g.fillRect(x, y, w, h);
2942: }
2943:
2944: protected void paintTabBorder(Graphics g, int tabIndex, int x,
2945: int y, int w, int h, boolean isSelected) {
2946:
2947: g.translate(x, y);
2948:
2949: int right = w;
2950: int bottom = h;
2951:
2952: if (isFirstDisplayedTab(tabIndex, x, tabPane.getBounds().x)) {
2953: if (isSelected) {
2954: g.setColor(selectHighlight);
2955: //left
2956: g.fillRect(0, 0, 1, bottom);
2957: //top
2958: g.fillRect(0, 0, right - 1, 1);
2959: //right
2960: g.fillRect(right - 1, 0, 1, bottom);
2961: g.setColor(shadowColor);
2962: //topright corner
2963: g.fillRect(right - 1, 0, 1, 1);
2964: //right
2965: g.fillRect(right, 1, 1, bottom);
2966: }
2967: } else {
2968: if (isSelected) {
2969: g.setColor(selectHighlight);
2970: //left
2971: g.fillRect(1, 1, 1, bottom - 1);
2972: //top
2973: g.fillRect(2, 0, right - 3, 1);
2974: //right
2975: g.fillRect(right - 1, 1, 1, bottom - 1);
2976: g.setColor(shadowColor);
2977: //left
2978: g.fillRect(0, 1, 1, bottom - 1);
2979: //topleft corner
2980: g.fillRect(1, 0, 1, 1);
2981: //topright corner
2982: g.fillRect(right - 1, 0, 1, 1);
2983: //right
2984: g.fillRect(right, 1, 1, bottom);
2985: } else {
2986: g.setColor(shadowColor);
2987: g.fillRect(0, 0, 1, bottom + 2 - bottom / 2);
2988: }
2989: }
2990: g.translate(-x, -y);
2991: }
2992:
2993: protected void paintContentBorderTopEdge(Graphics g, int x,
2994: int y, int w, int h, boolean drawBroken,
2995: Rectangle selRect, boolean isContentBorderPainted) {
2996: g.setColor(shadowColor);
2997: g.fillRect(x, y, w, 1);
2998: }
2999:
3000: }
3001:
3002: /**
3003: * Renderer for tabs on top with normal decoration.
3004: */
3005: private static final class TopRenderer extends AbstractRenderer {
3006:
3007: private TopRenderer(JTabbedPane tabPane) {
3008: super (tabPane);
3009: }
3010:
3011: protected Insets getTabAreaInsets(Insets defaultInsets) {
3012: return new Insets(defaultInsets.top,
3013: defaultInsets.left + 4, defaultInsets.bottom,
3014: defaultInsets.right);
3015: }
3016:
3017: protected int getTabLabelShiftY(int tabIndex, boolean isSelected) {
3018: return isSelected ? -1 : 0;
3019: }
3020:
3021: protected int getTabRunOverlay(int tabRunOverlay) {
3022: return tabRunOverlay - 2;
3023: }
3024:
3025: protected int getTabRunIndent(int run) {
3026: return 6 * run;
3027: }
3028:
3029: protected Insets getSelectedTabPadInsets() {
3030: return NORTH_INSETS;
3031: }
3032:
3033: protected Insets getTabInsets(int tabIndex, Insets tabInsets) {
3034: return new Insets(tabInsets.top - 1, tabInsets.left - 4,
3035: tabInsets.bottom, tabInsets.right - 4);
3036: }
3037:
3038: protected void paintFocusIndicator(Graphics g,
3039: Rectangle[] rects, int tabIndex, Rectangle iconRect,
3040: Rectangle textRect, boolean isSelected) {
3041:
3042: if (!tabPane.hasFocus() || !isSelected)
3043: return;
3044: Rectangle tabRect = rects[tabIndex];
3045: int top = tabRect.y + 1;
3046: int left = tabRect.x + 4;
3047: int height = tabRect.height - 3;
3048: int width = tabRect.width - 9;
3049: g.setColor(focus);
3050: g.drawRect(left, top, width, height);
3051: }
3052:
3053: protected void paintTabBackground(Graphics g, int tabIndex,
3054: int x, int y, int w, int h, boolean isSelected) {
3055:
3056: int sel = (isSelected) ? 0 : 1;
3057: g.setColor(selectColor);
3058: g.fillRect(x, y + sel, w, h / 2);
3059: g.fillRect(x - 1, y + sel + h / 2, w + 2, h - h / 2);
3060: }
3061:
3062: protected void paintTabBorder(Graphics g, int tabIndex, int x,
3063: int y, int w, int h, boolean isSelected) {
3064:
3065: g.translate(x - 4, y);
3066:
3067: int top = 0;
3068: int right = w + 6;
3069:
3070: // Paint Border
3071: g.setColor(selectHighlight);
3072:
3073: // Paint left
3074: g.drawLine(1, h - 1, 4, top + 4);
3075: g.fillRect(5, top + 2, 1, 2);
3076: g.fillRect(6, top + 1, 1, 1);
3077:
3078: // Paint top
3079: g.fillRect(7, top, right - 12, 1);
3080:
3081: // Paint right
3082: g.setColor(darkShadow);
3083: g.drawLine(right, h - 1, right - 3, top + 4);
3084: g.fillRect(right - 4, top + 2, 1, 2);
3085: g.fillRect(right - 5, top + 1, 1, 1);
3086:
3087: g.translate(-x + 4, -y);
3088: }
3089:
3090: protected void paintContentBorderTopEdge(Graphics g, int x,
3091: int y, int w, int h, boolean drawBroken,
3092: Rectangle selRect, boolean isContentBorderPainted) {
3093: int right = x + w - 1;
3094: int top = y;
3095: g.setColor(selectHighlight);
3096:
3097: if (drawBroken && selRect.x >= x && selRect.x <= x + w) {
3098: // Break line to show visual connection to selected tab
3099: g.fillRect(x, top, selRect.x - 2 - x, 1);
3100: if (selRect.x + selRect.width < x + w - 2) {
3101: g.fillRect(selRect.x + selRect.width + 2, top,
3102: right - 2 - selRect.x - selRect.width, 1);
3103: } else {
3104: g.fillRect(x + w - 2, top, 1, 1);
3105: }
3106: } else {
3107: g.fillRect(x, top, w - 1, 1);
3108: }
3109: }
3110:
3111: protected int getTabsOverlay() {
3112: return 6;
3113: }
3114: }
3115:
3116: }
|