e.g. Calendar Search Help
You must enter a value before pressing Search
jgoodies_looks

Class: com.jgoodies.looks.plastic.PlasticTabbedPaneUI   ©

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

            
All Examples in File:
Example
Line
Rating (found
useful by...)
813 0% of 0
1809 0% of 0
1822 0% of 0