KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > jgoodies > looks > plastic > PlasticTabbedPaneUI


1 /*
2  * Copyright (c) 2001-2005 JGoodies Karsten Lentzsch. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * o Redistributions of source code must retain the above copyright notice,
8  * this list of conditions and the following disclaimer.
9  *
10  * o Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  *
14  * o Neither the name of JGoodies Karsten Lentzsch nor the names of
15  * its contributors may be used to endorse or promote products derived
16  * from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */

30
31 package com.jgoodies.looks.plastic;
32
33 import java.awt.Color JavaDoc;
34 import java.awt.Component JavaDoc;
35 import java.awt.Container JavaDoc;
36 import java.awt.Dimension JavaDoc;
37 import java.awt.Font JavaDoc;
38 import java.awt.FontMetrics JavaDoc;
39 import java.awt.Graphics JavaDoc;
40 import java.awt.Graphics2D JavaDoc;
41 import java.awt.Insets JavaDoc;
42 import java.awt.LayoutManager JavaDoc;
43 import java.awt.Point JavaDoc;
44 import java.awt.Polygon JavaDoc;
45 import java.awt.Rectangle JavaDoc;
46 import java.awt.Shape JavaDoc;
47 import java.awt.event.ActionEvent JavaDoc;
48 import java.awt.event.ActionListener JavaDoc;
49 import java.awt.event.MouseEvent JavaDoc;
50 import java.beans.PropertyChangeEvent JavaDoc;
51 import java.beans.PropertyChangeListener JavaDoc;
52
53 import javax.swing.AbstractAction JavaDoc;
54 import javax.swing.Action JavaDoc;
55 import javax.swing.ActionMap JavaDoc;
56 import javax.swing.Icon JavaDoc;
57 import javax.swing.JButton JavaDoc;
58 import javax.swing.JComponent JavaDoc;
59 import javax.swing.JPanel JavaDoc;
60 import javax.swing.JTabbedPane JavaDoc;
61 import javax.swing.JViewport JavaDoc;
62 import javax.swing.SwingConstants JavaDoc;
63 import javax.swing.SwingUtilities JavaDoc;
64 import javax.swing.UIManager JavaDoc;
65 import javax.swing.event.ChangeEvent JavaDoc;
66 import javax.swing.event.ChangeListener JavaDoc;
67 import javax.swing.plaf.ComponentUI JavaDoc;
68 import javax.swing.plaf.UIResource JavaDoc;
69 import javax.swing.plaf.basic.BasicTabbedPaneUI JavaDoc;
70 import javax.swing.plaf.metal.MetalTabbedPaneUI JavaDoc;
71 import javax.swing.text.View JavaDoc;
72
73 import com.jgoodies.looks.LookUtils;
74 import com.jgoodies.looks.Options;
75
76 /**
77  * The JGoodies Plastic Look&Feel implementation of
78  * <code>TabbedPaneUI</code>. It differs from its superclass
79  * <code>MetalTabbedPaneUI</code> in that it paints new tab shapes,
80  * provides two options, and supports ClearLook.
81  * <p>
82  * You can enable or disable icons in tabs globally via
83  * com.jgoodies.looks.Options.setTabIconsEnabled(boolean).
84  * <p>
85  * To disable the content border set
86  * <pre>
87  * JTabbedPane tabbedPane = new JTabbedPane();
88  * tabbedPane.putClientProperty(Option.NO_CONTENT_BORDER_KEY, Boolean.TRUE);
89  * </pre>
90  * To paint embedded tabs use
91  * <pre>
92  * JTabbedPane tabbedPane = new JTabbedPane();
93  * tabbedPane.putClientProperty(Option.EMBEDDED_TABS_KEY, Boolean.TRUE);
94  * </pre>
95  * <p>
96  * There's a special mode that helps you detect content borders in
97  * heavily wrapped component hierarchies - such as the NetBeans IDE.
98  * In this marked mode the content border is painted as a Magenta line.
99  * You can enable this mode by setting the System property
100  * <tt>markContentBorders</tt> to <tt>true</tt>; in a command line:
101  * <pre>
102  * java -DmarkContentBorders=true
103  * </pre>
104  *
105  * @author Karsten Lentzsch
106  * @author Torge Husfeldt
107  * @author Andrej Golovnin
108  * @version $Revision: 1.3 $
109  *
110  * @see Options
111  */

112 public final class PlasticTabbedPaneUI extends MetalTabbedPaneUI JavaDoc {
113
114
115     // State ******************************************************************
116

117     /**
118      * Describes if tabs are painted with or without icons.
119      */

120     private static boolean isTabIconsEnabled = Options.isTabIconsEnabled();
121     
122     /**
123      * Describes if we paint no content border or not; is false by default.
124      * You can disable the content border by setting the client property
125      * Options.NO_CONTENT_BORDER_KEY to Boolean.TRUE;
126      */

127     private Boolean JavaDoc noContentBorder;
128
129     /**
130      * Describes if we paint tabs in an embedded style that is with
131      * less decoration; this is false by default.
132      * You can enable the embedded tabs style by setting the client property
133      * Options.EMBEDDED_TABS_KEY to Boolean.TRUE.
134      */

135     private Boolean JavaDoc embeddedTabs;
136
137     /**
138      * Holds the renderer that is used to render the tabs.
139      */

140     private AbstractRenderer renderer;
141
142
143     /** For use when tabLayoutPolicy = SCROLL_TAB_LAYOUT */
144     private ScrollableTabSupport tabScroller;
145     
146     /**
147      * Creates the <code>PlasticTabbedPaneUI</code>.
148      *
149      * @see javax.swing.plaf.ComponentUI#createUI(JComponent)
150      */

151     public static ComponentUI JavaDoc createUI(JComponent JavaDoc tabPane) {
152         return new PlasticTabbedPaneUI();
153     }
154
155     /**
156      * Installs the UI.
157      *
158      * @see javax.swing.plaf.ComponentUI#installUI(JComponent)
159      */

160     public void installUI(JComponent JavaDoc c) {
161         super.installUI(c);
162         embeddedTabs = (Boolean JavaDoc) c.getClientProperty(Options.EMBEDDED_TABS_KEY);
163         noContentBorder = (Boolean JavaDoc) c.getClientProperty(Options.NO_CONTENT_BORDER_KEY);
164         renderer = createRenderer(tabPane);
165     }
166
167     /**
168      * Uninstalls the UI.
169      * @see javax.swing.plaf.ComponentUI#uninstallUI(JComponent)
170      */

171     public void uninstallUI(JComponent JavaDoc c) {
172         renderer = null;
173         super.uninstallUI(c);
174     }
175
176     /**
177      * Creates and installs any required subcomponents for the JTabbedPane.
178      * Invoked by installUI.
179      * @see javax.swing.plaf.basic.BasicTabbedPaneUI#installComponents()
180      */

181     protected void installComponents() {
182         if (scrollableTabLayoutEnabled()) {
183             if (tabScroller == null) {
184                 tabScroller = new ScrollableTabSupport(tabPane.getTabPlacement());
185                 tabPane.add(tabScroller.viewport);
186             }
187         }
188     }
189     
190     /**
191      * Removes any installed subcomponents from the JTabbedPane.
192      * Invoked by uninstallUI.
193      * @see javax.swing.plaf.basic.BasicTabbedPaneUI#uninstallComponents()
194      */

195     protected void uninstallComponents() {
196         if (scrollableTabLayoutEnabled()) {
197             tabPane.remove(tabScroller.viewport);
198             tabPane.remove(tabScroller.scrollForwardButton);
199             tabPane.remove(tabScroller.scrollBackwardButton);
200             tabScroller = null;
201         }
202     }
203     
204     protected void installListeners() {
205         super.installListeners();
206         // if the layout policy is the SCROLL_TAB_LAYOUT, the super class
207
// will install the mouse listener on tabPane instead of
208
// tabScroller#tabPanel and there is no way to prevent this.
209
// That's why the mouse listener must be removed from tabPane and
210
// added to tabScroller#tabPanel when the scroll tab layout is enabled.
211
// This applies only to JDK 1.4!!!
212
if ((mouseListener != null) && (LookUtils.IS_JAVA_1_4)) {
213             if (scrollableTabLayoutEnabled()) {
214                 tabPane.removeMouseListener(mouseListener);
215                 tabScroller.tabPanel.addMouseListener(mouseListener);
216             }
217         }
218     }
219     
220     protected void uninstallListeners() {
221         if ((mouseListener != null) && (LookUtils.IS_JAVA_1_4)) {
222             if (scrollableTabLayoutEnabled()) { // SCROLL_TAB_LAYOUT
223
tabScroller.tabPanel.removeMouseListener(mouseListener);
224             } else { // WRAP_TAB_LAYOUT
225
tabPane.removeMouseListener(mouseListener);
226             }
227             mouseListener = null;
228         }
229         super.uninstallListeners();
230     }
231     
232     protected void installKeyboardActions() {
233         super.installKeyboardActions();
234         // if the layout policy is the SCROLL_TAB_LAYOUT, then replace
235
// the forward and backward actions, installed in the action map
236
// in the supper class, by our own.
237
if (scrollableTabLayoutEnabled()) {
238             Action JavaDoc forwardAction = new ScrollTabsForwardAction();
239             Action JavaDoc backwardAction = new ScrollTabsBackwardAction();
240             ActionMap JavaDoc am = SwingUtilities.getUIActionMap(tabPane);
241             am.put("scrollTabsForwardAction", forwardAction);
242             am.put("scrollTabsBackwardAction", backwardAction);
243             tabScroller.scrollForwardButton.setAction(forwardAction);
244             tabScroller.scrollBackwardButton.setAction(backwardAction);
245         }
246     }
247     
248     /**
249      * Checks and answers if content border will be painted.
250      * This is controlled by the component's client property
251      * Options.NO_CONTENT_BORDER or Options.EMBEDDED.
252      */

253     private boolean hasNoContentBorder() {
254         return Boolean.TRUE.equals(noContentBorder);
255     }
256
257     /**
258      * Checks and answers if tabs are painted with minimal decoration.
259      */

260     private boolean hasEmbeddedTabs() {
261         return Boolean.TRUE.equals(embeddedTabs);
262     }
263
264     /**
265      * Creates the renderer used to lay out and paint the tabs.
266      * @param tabbedPane the UIs component
267      * @return AbstractRenderer the renderer that will be used to paint
268      */

269     private AbstractRenderer createRenderer(JTabbedPane JavaDoc tabbedPane) {
270         return hasEmbeddedTabs()
271                 ? AbstractRenderer.createEmbeddedRenderer(tabbedPane)
272                 : AbstractRenderer.createRenderer(tabPane);
273     }
274
275     /**
276      * Creates and answer a handler that listens to property changes.
277      * Unlike the superclass BasicTabbedPane, the PlasticTabbedPaneUI
278      * uses an extended Handler.
279      */

280     protected PropertyChangeListener JavaDoc createPropertyChangeListener() {
281         return new MyPropertyChangeHandler();
282     }
283
284     protected ChangeListener JavaDoc createChangeListener() {
285         return new TabSelectionHandler();
286     }
287     
288     /*
289      * Private helper method for the next three methods.
290      */

291     private void doLayout() {
292         tabPane.revalidate();
293         tabPane.repaint();
294     }
295
296      /**
297       * Updates the renderer and layout. This message is sent by
298       * my PropertyChangeHandler whenever the tab placement changes.
299       */

300      private void tabPlacementChanged() {
301          renderer = createRenderer(tabPane);
302          if (scrollableTabLayoutEnabled()) {
303              tabScroller.createButtons();
304          }
305          doLayout();
306      }
307
308     /**
309      * Updates the embedded tabs property. This message is sent by
310      * my PropertyChangeHandler whenever the embedded tabs property changes.
311      */

312     private void embeddedTabsPropertyChanged(Boolean JavaDoc newValue) {
313         embeddedTabs = newValue;
314         renderer = createRenderer(tabPane);
315         doLayout();
316     }
317
318      /**
319       * Updates the no content border property. This message is sent
320       * by my PropertyChangeHandler whenever the noContentBorder
321       * property changes.
322       */

323      private void noContentBorderPropertyChanged(Boolean JavaDoc newValue) {
324          noContentBorder = newValue;
325          tabPane.repaint();
326      }
327
328      public void paint(Graphics JavaDoc g, JComponent JavaDoc c) {
329          int selectedIndex = tabPane.getSelectedIndex();
330          int tabPlacement = tabPane.getTabPlacement();
331
332          ensureCurrentLayout();
333
334          // Paint tab area
335
// If scrollable tabs are enabled, the tab area will be
336
// painted by the scrollable tab panel instead.
337
//
338
if (!scrollableTabLayoutEnabled()) { // WRAP_TAB_LAYOUT
339
paintTabArea(g, tabPlacement, selectedIndex);
340          }
341      
342          // Paint content border
343
paintContentBorder(g, tabPlacement, selectedIndex);
344      }
345      
346      protected void paintTab(Graphics JavaDoc g, int tabPlacement, Rectangle JavaDoc[] rects,
347              int tabIndex, Rectangle JavaDoc iconRect, Rectangle JavaDoc textRect) {
348          Rectangle JavaDoc tabRect = rects[tabIndex];
349          int selectedIndex = tabPane.getSelectedIndex();
350          boolean isSelected = selectedIndex == tabIndex;
351          Graphics2D JavaDoc g2 = null;
352          Polygon JavaDoc cropShape = null;
353          Shape JavaDoc save = null;
354          int cropx = 0;
355          int cropy = 0;
356
357          if (scrollableTabLayoutEnabled()) {
358              if (g instanceof Graphics2D JavaDoc) {
359                  g2 = (Graphics2D JavaDoc) g;
360
361                  // Render visual for cropped tab edge...
362
Rectangle JavaDoc viewRect = tabScroller.viewport.getViewRect();
363                  int cropline;
364                  switch (tabPlacement) {
365                  case LEFT:
366                  case RIGHT:
367                      cropline = viewRect.y + viewRect.height;
368                      if ((tabRect.y < cropline)
369                              && (tabRect.y + tabRect.height > cropline)) {
370                          cropShape = createCroppedTabClip(tabPlacement, tabRect,
371                                  cropline);
372                          cropx = tabRect.x;
373                          cropy = cropline - 1;
374                      }
375                      break;
376                  case TOP:
377                  case BOTTOM:
378                  default:
379                      cropline = viewRect.x + viewRect.width;
380                      if ((tabRect.x < cropline)
381                              && (tabRect.x + tabRect.width > cropline)) {
382                          cropShape = createCroppedTabClip(tabPlacement, tabRect,
383                                  cropline);
384                          cropx = cropline - 1;
385                          cropy = tabRect.y;
386                      }
387                  }
388                  if (cropShape != null) {
389                      save = g2.getClip();
390                      g2.clip(cropShape);
391                  }
392              }
393          }
394
395          paintTabBackground(g, tabPlacement, tabIndex, tabRect.x, tabRect.y,
396                  tabRect.width, tabRect.height, isSelected);
397
398          paintTabBorder(g, tabPlacement, tabIndex, tabRect.x, tabRect.y,
399                  tabRect.width, tabRect.height, isSelected);
400
401          String JavaDoc title = tabPane.getTitleAt(tabIndex);
402          Font JavaDoc font = tabPane.getFont();
403          FontMetrics JavaDoc metrics = g.getFontMetrics(font);
404          Icon JavaDoc icon = getIconForTab(tabIndex);
405
406          layoutLabel(tabPlacement, metrics, tabIndex, title, icon, tabRect,
407                  iconRect, textRect, isSelected);
408
409          paintText(g, tabPlacement, font, metrics, tabIndex, title, textRect,
410                  isSelected);
411
412          paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected);
413
414          paintFocusIndicator(g, tabPlacement, rects, tabIndex, iconRect,
415                  textRect, isSelected);
416
417          if (cropShape != null) {
418              paintCroppedTabEdge(g, tabPlacement, tabIndex, isSelected, cropx,
419                      cropy);
420              g2.setClip(save);
421          }
422      }
423
424      /*
425       * This method will create and return a polygon shape for the given tab
426       * rectangle which has been cropped at the specified cropline with a torn
427       * edge visual. e.g. A "File" tab which has cropped been cropped just after
428       * the "i":
429       * -------------
430       * | ..... |
431       * | . |
432       * | ... . |
433       * | . . |
434       * | . . |
435       * | . . |
436       * --------------
437       *
438       * The x, y arrays below define the pattern used to create a "torn" edge
439       * segment which is repeated to fill the edge of the tab. For tabs placed on
440       * TOP and BOTTOM, this righthand torn edge is created by line segments
441       * which are defined by coordinates obtained by subtracting xCropLen[i] from
442       * (tab.x + tab.width) and adding yCroplen[i] to (tab.y). For tabs placed on
443       * LEFT or RIGHT, the bottom torn edge is created by subtracting xCropLen[i]
444       * from (tab.y + tab.height) and adding yCropLen[i] to (tab.x).
445       */

446      private int xCropLen[] = { 1, 1, 0, 0, 1, 1, 2, 2 };
447
448      private int yCropLen[] = { 0, 3, 3, 6, 6, 9, 9, 12 };
449
450      private static final int CROP_SEGMENT = 12;
451
452      private Polygon JavaDoc createCroppedTabClip(int tabPlacement, Rectangle JavaDoc tabRect,
453              int cropline) {
454          int rlen = 0;
455          int start = 0;
456          int end = 0;
457          int ostart = 0;
458
459          switch (tabPlacement) {
460              case LEFT:
461              case RIGHT:
462                  rlen = tabRect.width;
463                  start = tabRect.x;
464                  end = tabRect.x + tabRect.width;
465                  ostart = tabRect.y;
466                  break;
467              case TOP:
468              case BOTTOM:
469              default:
470                  rlen = tabRect.height;
471                  start = tabRect.y;
472                  end = tabRect.y + tabRect.height;
473                  ostart = tabRect.x;
474          }
475          int rcnt = rlen / CROP_SEGMENT;
476          if (rlen % CROP_SEGMENT > 0) {
477              rcnt++;
478          }
479          int npts = 2 + (rcnt * 8);
480          int xp[] = new int[npts];
481          int yp[] = new int[npts];
482          int pcnt = 0;
483
484          xp[pcnt] = ostart;
485          yp[pcnt++] = end;
486          xp[pcnt] = ostart;
487          yp[pcnt++] = start;
488          for (int i = 0; i < rcnt; i++) {
489              for (int j = 0; j < xCropLen.length; j++) {
490                  xp[pcnt] = cropline - xCropLen[j];
491                  yp[pcnt] = start + (i * CROP_SEGMENT) + yCropLen[j];
492                  if (yp[pcnt] >= end) {
493                      yp[pcnt] = end;
494                      pcnt++;
495                      break;
496                  }
497                  pcnt++;
498              }
499          }
500          if (tabPlacement == SwingConstants.TOP
501                  || tabPlacement == SwingConstants.BOTTOM) {
502              return new Polygon JavaDoc(xp, yp, pcnt);
503
504          }
505          //LEFT or RIGHT
506
return new Polygon JavaDoc(yp, xp, pcnt);
507      }
508
509      /* If tabLayoutPolicy == SCROLL_TAB_LAYOUT, this method will paint an edge
510       * indicating the tab is cropped in the viewport display
511       */

512      private void paintCroppedTabEdge(Graphics JavaDoc g, int tabPlacement,
513              int tabIndex, boolean isSelected, int x, int y) {
514          switch (tabPlacement) {
515              case LEFT:
516              case RIGHT:
517                  int xx = x;
518                  g.setColor(shadow);
519                  while (xx <= x + rects[tabIndex].width) {
520                      for (int i = 0; i < xCropLen.length; i += 2) {
521                          g.drawLine(xx + yCropLen[i], y - xCropLen[i], xx
522                                  + yCropLen[i + 1] - 1, y - xCropLen[i + 1]);
523                      }
524                      xx += CROP_SEGMENT;
525                  }
526                  break;
527              case TOP:
528              case BOTTOM:
529              default:
530                  int yy = y;
531                  g.setColor(shadow);
532                  while (yy <= y + rects[tabIndex].height) {
533                      for (int i = 0; i < xCropLen.length; i += 2) {
534                          g.drawLine(x - xCropLen[i], yy + yCropLen[i], x
535                                  - xCropLen[i + 1], yy + yCropLen[i + 1] - 1);
536                      }
537                      yy += CROP_SEGMENT;
538                  }
539          }
540      }
541
542      private void ensureCurrentLayout() {
543          if (!tabPane.isValid()) {
544              tabPane.validate();
545          }
546          /* If tabPane doesn't have a peer yet, the validate() call will
547           * silently fail. We handle that by forcing a layout if tabPane
548           * is still invalid. See bug 4237677.
549           */

550          if (!tabPane.isValid()) {
551              TabbedPaneLayout layout = (TabbedPaneLayout) tabPane.getLayout();
552              layout.calculateLayoutInfo();
553          }
554      }
555      
556      /**
557       * Returns the tab index which intersects the specified point
558       * in the JTabbedPane's coordinate space.
559       */

560      public int tabForCoordinate(JTabbedPane JavaDoc pane, int x, int y) {
561          ensureCurrentLayout();
562          Point JavaDoc p = new Point JavaDoc(x, y);
563      
564          if (scrollableTabLayoutEnabled()) {
565              translatePointToTabPanel(x, y, p);
566              Rectangle JavaDoc viewRect = tabScroller.viewport.getViewRect();
567              if (!viewRect.contains(p)) {
568                  return -1;
569              }
570          }
571          int tabCount = tabPane.getTabCount();
572          for (int i = 0; i < tabCount; i++) {
573              if (rects[i].contains(p.x, p.y)) {
574                  return i;
575              }
576          }
577          return -1;
578      }
579
580      protected Rectangle JavaDoc getTabBounds(int tabIndex, Rectangle JavaDoc dest) {
581          dest.width = rects[tabIndex].width;
582          dest.height = rects[tabIndex].height;
583          if (scrollableTabLayoutEnabled()) { // SCROLL_TAB_LAYOUT
584
// Need to translate coordinates based on viewport location &
585
// view position
586
Point JavaDoc vpp = tabScroller.viewport.getLocation();
587              Point JavaDoc viewp = tabScroller.viewport.getViewPosition();
588              dest.x = rects[tabIndex].x + vpp.x - viewp.x;
589              dest.y = rects[tabIndex].y + vpp.y - viewp.y;
590          } else { // WRAP_TAB_LAYOUT
591
dest.x = rects[tabIndex].x;
592              dest.y = rects[tabIndex].y;
593          }
594          return dest;
595      }
596      
597      /**
598       * Returns the index of the tab closest to the passed in location, note
599       * that the returned tab may not contain the location x,y.
600       */

601      private int getClosestTab(int x, int y) {
602          int min = 0;
603          int tabCount = Math.min(rects.length, tabPane.getTabCount());
604          int max = tabCount;
605          int tabPlacement = tabPane.getTabPlacement();
606          boolean useX = (tabPlacement == TOP || tabPlacement == BOTTOM);
607          int want = (useX) ? x : y;
608
609          while (min != max) {
610              int current = (max + min) / 2;
611              int minLoc;
612              int maxLoc;
613
614              if (useX) {
615                  minLoc = rects[current].x;
616                  maxLoc = minLoc + rects[current].width;
617              } else {
618                  minLoc = rects[current].y;
619                  maxLoc = minLoc + rects[current].height;
620              }
621              if (want < minLoc) {
622                  max = current;
623                  if (min == max) {
624                      return Math.max(0, current - 1);
625                  }
626              } else if (want >= maxLoc) {
627                  min = current;
628                  if (max - min <= 1) {
629                      return Math.max(current + 1, tabCount - 1);
630                  }
631              } else {
632                  return current;
633              }
634          }
635          return min;
636      }
637      
638      /**
639       * Returns a point which is translated from the specified point in the
640       * JTabbedPane's coordinate space to the coordinate space of the
641       * ScrollableTabPanel. This is used for SCROLL_TAB_LAYOUT ONLY.
642       */

643      private Point JavaDoc translatePointToTabPanel(int srcx, int srcy, Point JavaDoc dest) {
644          Point JavaDoc vpp = tabScroller.viewport.getLocation();
645          Point JavaDoc viewp = tabScroller.viewport.getViewPosition();
646          dest.x = srcx - vpp.x + viewp.x;
647          dest.y = srcy - vpp.y + viewp.y;
648          return dest;
649      }
650      
651     protected void paintTabArea(Graphics JavaDoc g, int tabPlacement, int selectedIndex) {
652         int tabCount = tabPane.getTabCount();
653
654         Rectangle JavaDoc iconRect = new Rectangle JavaDoc(),
655                   textRect = new Rectangle JavaDoc();
656         Rectangle JavaDoc clipRect = g.getClipBounds();
657
658         // Paint tabRuns of tabs from back to front
659
for (int i = runCount - 1; i >= 0; i--) {
660             int start = tabRuns[i];
661             int next = tabRuns[(i == runCount - 1)? 0 : i + 1];
662             int end = (next != 0? next - 1: tabCount - 1);
663             for (int j = end; j >= start; j--) {
664                 if (j != selectedIndex && rects[j].intersects(clipRect)) {
665                     paintTab(g, tabPlacement, rects, j, iconRect, textRect);
666                 }
667             }
668         }
669
670         // Paint selected tab if its in the front run
671
// since it may overlap other tabs
672
if (selectedIndex >= 0 && rects[selectedIndex].intersects(clipRect)) {
673             paintTab(g, tabPlacement, rects, selectedIndex, iconRect, textRect);
674         }
675     }
676     
677     /*
678      * Copied here from super(super)class to avoid labels being centered on
679      * vertical tab runs if they consist of icon and text
680      */

681     protected void layoutLabel(
682         int tabPlacement,
683         FontMetrics JavaDoc metrics,
684         int tabIndex,
685         String JavaDoc title,
686         Icon JavaDoc icon,
687         Rectangle JavaDoc tabRect,
688         Rectangle JavaDoc iconRect,
689         Rectangle JavaDoc textRect,
690         boolean isSelected) {
691         textRect.x = textRect.y = iconRect.x = iconRect.y = 0;
692         //fix of issue #4
693
View JavaDoc v = getTextViewForTab(tabIndex);
694         if (v != null) {
695             tabPane.putClientProperty("html", v);
696         }
697
698         Rectangle JavaDoc calcRectangle = new Rectangle JavaDoc(tabRect);
699         if (isSelected) {
700             Insets JavaDoc calcInsets = getSelectedTabPadInsets(tabPlacement);
701             calcRectangle.x += calcInsets.left;
702             calcRectangle.y += calcInsets.top;
703             calcRectangle.width -= calcInsets.left + calcInsets.right ;
704             calcRectangle.height -= calcInsets.bottom + calcInsets.top;
705         }
706         int xNudge = getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
707         int yNudge = getTabLabelShiftY(tabPlacement, tabIndex, isSelected);
708         if ((tabPlacement == RIGHT || tabPlacement == LEFT) && icon != null && title != null && !title.equals("")) {
709             SwingUtilities.layoutCompoundLabel(
710                 tabPane,
711                 metrics,
712                 title,
713                 icon,
714                 SwingConstants.CENTER,
715                 SwingConstants.LEFT,
716                 SwingConstants.CENTER,
717                 SwingConstants.TRAILING,
718                 calcRectangle,
719                 iconRect,
720                 textRect,
721                 textIconGap);
722             xNudge += 4;
723         } else {
724             SwingUtilities.layoutCompoundLabel(
725                 tabPane,
726                 metrics,
727                 title,
728                 icon,
729                 SwingConstants.CENTER,
730                 SwingConstants.CENTER,
731                 SwingConstants.CENTER,
732                 SwingConstants.TRAILING,
733                 calcRectangle,
734                 iconRect,
735                 textRect,
736                 textIconGap);
737                 iconRect.y += calcRectangle.height %2;
738         }
739
740         //fix of issue #4
741
tabPane.putClientProperty("html", null);
742         
743         iconRect.x += xNudge;
744         iconRect.y += yNudge;
745         textRect.x += xNudge;
746         textRect.y += yNudge;
747     }
748
749     /**
750      * Answers the icon for the tab with the specified index.
751      * In case, we have globally switched of the use tab icons,
752      * we answer <code>null</code> if and only if we have a title.
753      */

754     protected Icon JavaDoc getIconForTab(int tabIndex) {
755         String JavaDoc title = tabPane.getTitleAt(tabIndex);
756         boolean hasTitle = (title != null) && (title.length() > 0);
757         return !isTabIconsEnabled && hasTitle
758                     ? null
759                     : super.getIconForTab(tabIndex);
760     }
761
762     /**
763      * Creates the layout manager used to set the tab's bounds.
764      */

765     protected LayoutManager JavaDoc createLayoutManager() {
766         if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
767             return new TabbedPaneScrollLayout();
768         }
769         /* WRAP_TAB_LAYOUT */
770         return new TabbedPaneLayout();
771     }
772
773     /* In an attempt to preserve backward compatibility for programs
774      * which have extended BasicTabbedPaneUI to do their own layout, the
775      * UI uses the installed layoutManager (and not tabLayoutPolicy) to
776      * determine if scrollTabLayout is enabled.
777      */

778     private boolean scrollableTabLayoutEnabled() {
779         return tabPane.getLayout() instanceof TabbedPaneScrollLayout;
780     }
781     
782     protected boolean isTabInFirstRun(int tabIndex) {
783         return getRunForTab(tabPane.getTabCount(), tabIndex) == 0;
784     }
785
786     protected void paintContentBorder(Graphics JavaDoc g, int tabPlacement, int selectedIndex) {
787         int width = tabPane.getWidth();
788         int height = tabPane.getHeight();
789         Insets JavaDoc insets = tabPane.getInsets();
790
791         int x = insets.left;
792         int y = insets.top;
793         int w = width - insets.right - insets.left;
794         int h = height - insets.top - insets.bottom;
795
796         switch (tabPlacement) {
797             case LEFT :
798                 x += calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
799                 w -= (x - insets.left);
800                 break;
801             case RIGHT :
802                 w -= calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
803                 break;
804             case BOTTOM :
805                 h -= calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
806                 break;
807             case TOP :
808             default :
809                 y += calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
810                 h -= (y - insets.top);
811         }
812         // Fill region behind content area
813
g.setColor(selectColor == null
814                         ? tabPane.getBackground()
815                         : selectColor);
816         g.fillRect(x, y, w, h);
817
818         Rectangle JavaDoc selRect;
819         selRect = (selectedIndex < 0) ? null : getTabBounds(selectedIndex, calcRect);
820         boolean drawBroken = selectedIndex >= 0 && isTabInFirstRun(selectedIndex);
821         boolean isContentBorderPainted = !hasNoContentBorder();
822         // It sounds a bit odd to call paintContentBorder with
823
// a parameter isContentBorderPainted set to false.
824
// But in this case the part of the border touching the tab
825
// area will still be painted so best let the renderer decide.
826
renderer.paintContentBorderTopEdge (g, x, y, w, h, drawBroken, selRect, isContentBorderPainted);
827         renderer.paintContentBorderLeftEdge (g, x, y, w, h, drawBroken, selRect, isContentBorderPainted);
828         renderer.paintContentBorderBottomEdge(g, x, y, w, h, drawBroken, selRect, isContentBorderPainted);
829         renderer.paintContentBorderRightEdge (g, x, y, w, h, drawBroken, selRect, isContentBorderPainted);
830     }
831
832     //
833
// Here comes a number of methods that are just delegated to the
834
// appropriate renderer
835
//
836
/**
837      * Returns the insets (i.e. the width) of the content Border
838      */

839     protected Insets JavaDoc getContentBorderInsets(int tabPlacement) {
840         return renderer.getContentBorderInsets(super.getContentBorderInsets(tabPlacement));
841     }
842
843     /**
844      * Returns the amount by which the Tab Area is inset
845      */

846     protected Insets JavaDoc getTabAreaInsets(int tabPlacement) {
847         return renderer.getTabAreaInsets(super.getTabAreaInsets(tabPlacement));
848     }
849
850     /**
851      * Returns the amount by which the label should be shifted horizontally
852      */

853     protected int getTabLabelShiftX(int tabPlacement, int tabIndex, boolean isSelected) {
854         return renderer.getTabLabelShiftX(tabIndex, isSelected);
855     }
856
857     /**
858      * Returns the amount by which the label should be shifted vertically
859      */

860     protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) {
861         return renderer.getTabLabelShiftY(tabIndex, isSelected);
862     }
863
864     /**
865      * Returns the amount (in pixels) by which two runs should overlap
866      */

867     protected int getTabRunOverlay(int tabPlacement) {
868         return renderer.getTabRunOverlay(tabRunOverlay);
869     }
870
871     /**
872      * This boolean controls wheather the given run should be padded to
873      * use up as much space as the others (with more tabs in them)
874      */

875     protected boolean shouldPadTabRun(int tabPlacement, int run) {
876         return renderer.shouldPadTabRun(run, super.shouldPadTabRun(tabPlacement, run));
877     }
878
879     /**
880      * Returns the amount by which the run number <code>run</code>
881      * should be indented. Add six pixels for every run to make
882      * diagonal lines align.
883      */

884     protected int getTabRunIndent(int tabPlacement, int run) {
885         return renderer.getTabRunIndent(run);
886     }
887
888     /**
889      * Returns the insets for this tab.
890      */

891     protected Insets JavaDoc getTabInsets(int tabPlacement, int tabIndex) {
892         return renderer.getTabInsets(tabIndex, tabInsets);
893     }
894
895     /**
896      * Returns the insets for selected tab.
897      */

898     protected Insets JavaDoc getSelectedTabPadInsets(int tabPlacement) {
899         return renderer.getSelectedTabPadInsets();
900     }
901
902     /**
903      * Draws the rectancle around the Tab label which indicates keyboard focus
904      */

905     protected void paintFocusIndicator(
906         Graphics JavaDoc g,
907         int tabPlacement,
908         Rectangle JavaDoc[] rectangles,
909         int tabIndex,
910         Rectangle JavaDoc iconRect,
911         Rectangle JavaDoc textRect,
912         boolean isSelected) {
913         renderer.paintFocusIndicator(g, rectangles, tabIndex, iconRect, textRect, isSelected);
914     }
915
916     /**
917      * Fills the background of the given tab to make sure overlap of
918      * tabs is handled correctly.
919      * Note: that tab backgrounds seem to be painted somewhere else, too.
920      */

921     protected void paintTabBackground(Graphics JavaDoc g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
922         renderer.paintTabBackground(g, tabIndex, x, y, w, h, isSelected);
923     }
924
925     /**
926      * Paints the border for one tab. Gets the bounds of the tab as parameters.
927      * Note that the result is not clipped so you can paint outside that
928      * rectangle. Tabs painted later on have a chance to overwrite though.
929      */

930     protected void paintTabBorder(Graphics JavaDoc g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
931         renderer.paintTabBorder(g, tabIndex, x, y, w, h, isSelected);
932     }
933
934     /**
935      * Answers wheather tab runs should be rotated. If true, the layout mechanism
936      * will move the run containing the selected tab so that it touches
937      * the content pane.
938      */

939     protected boolean shouldRotateTabRuns(int tabPlacement) {
940         return false;
941     }
942
943     private class TabSelectionHandler implements ChangeListener JavaDoc {
944         
945         private Rectangle JavaDoc rect = new Rectangle JavaDoc();
946         
947         public void stateChanged(ChangeEvent JavaDoc e) {
948             JTabbedPane JavaDoc tabPane = (JTabbedPane JavaDoc)e.getSource();
949             tabPane.revalidate();
950             tabPane.repaint();
951
952             if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
953                 int index = tabPane.getSelectedIndex();
954                 if (index < rects.length && index != -1) {
955                     rect.setBounds(rects[index]);
956                     Point JavaDoc viewPosition = tabScroller.viewport.getViewPosition();
957                     if (rect.x < viewPosition.x) {
958                         rect.x -= renderer.getTabsOverlay();
959                     } else {
960                         rect.x += renderer.getTabsOverlay();
961                     }
962                     tabScroller.tabPanel.scrollRectToVisible(rect);
963                 }
964             }
965         }
966     }
967     
968     /**
969      * Catches and handles property change events. In addition to the super
970      * class behavior we listen to changes of the ancestor, tab placement,
971      * and JGoodies options for content border, and embedded tabs.
972      */

973     private class MyPropertyChangeHandler
974         extends BasicTabbedPaneUI.PropertyChangeHandler JavaDoc {
975         public void propertyChange(PropertyChangeEvent JavaDoc e) {
976             String JavaDoc pName = e.getPropertyName();
977
978             if (null == pName) {
979                 return;
980             }
981             
982             super.propertyChange(e);
983
984             if (pName.equals("tabPlacement")) {
985                 tabPlacementChanged();
986                 return;
987             }
988             if (pName.equals(Options.EMBEDDED_TABS_KEY)) {
989                 embeddedTabsPropertyChanged((Boolean JavaDoc) e.getNewValue());
990                 return;
991             }
992             if (pName.equals(Options.NO_CONTENT_BORDER_KEY)) {
993                 noContentBorderPropertyChanged((Boolean JavaDoc) e.getNewValue());
994                 return;
995             }
996         }
997     }
998     
999     /**
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 JavaDoc implements LayoutManager JavaDoc {
1004
1005        protected void calculateTabRects(int tabPlacement, int tabCount) {
1006            FontMetrics JavaDoc metrics = getFontMetrics();
1007            Dimension JavaDoc size = tabPane.getSize();
1008            Insets JavaDoc insets = tabPane.getInsets();
1009            Insets JavaDoc 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 JavaDoc 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
1215    private boolean requestFocusForVisibleComponent() {
1216        Component JavaDoc visibleComponent = getVisibleComponent();
1217        if (visibleComponent.isFocusable()) {
1218             visibleComponent.requestFocus();
1219             return true;
1220        } else if (visibleComponent instanceof JComponent JavaDoc) {
1221             if (((JComponent JavaDoc) visibleComponent).requestDefaultFocus()) {
1222                 return true;
1223             }
1224        }
1225        return false;
1226    }
1227    
1228    private static class ScrollTabsForwardAction extends AbstractAction JavaDoc {
1229
1230        public void actionPerformed(ActionEvent JavaDoc e) {
1231            JTabbedPane JavaDoc pane = null;
1232            Object JavaDoc src = e.getSource();
1233            if (src instanceof JTabbedPane JavaDoc) {
1234                pane = (JTabbedPane JavaDoc) src;
1235            } else if (src instanceof PlasticArrowButton) {
1236                pane = (JTabbedPane JavaDoc) ((PlasticArrowButton) src).getParent();
1237            } else {
1238                return; // shouldn't happen
1239
}
1240            PlasticTabbedPaneUI ui = (PlasticTabbedPaneUI) pane.getUI();
1241
1242            if (ui.scrollableTabLayoutEnabled()) {
1243                ui.tabScroller.scrollForward(pane.getTabPlacement());
1244            }
1245        }
1246    }
1247
1248    private static class ScrollTabsBackwardAction extends AbstractAction JavaDoc {
1249
1250        public void actionPerformed(ActionEvent JavaDoc e) {
1251            JTabbedPane JavaDoc pane = null;
1252            Object JavaDoc src = e.getSource();
1253            if (src instanceof JTabbedPane JavaDoc) {
1254                pane = (JTabbedPane JavaDoc) src;
1255            } else if (src instanceof PlasticArrowButton) {
1256                pane = (JTabbedPane JavaDoc) ((PlasticArrowButton) src).getParent();
1257            } else {
1258                return; // shouldn't happen
1259
}
1260            PlasticTabbedPaneUI ui = (PlasticTabbedPaneUI) pane.getUI();
1261
1262            if (ui.scrollableTabLayoutEnabled()) {
1263                ui.tabScroller.scrollBackward(pane.getTabPlacement());
1264            }
1265        }
1266    }
1267
1268    private class TabbedPaneScrollLayout extends TabbedPaneLayout {
1269
1270        protected int preferredTabAreaHeight(int tabPlacement, int width) {
1271            return calculateMaxTabHeight(tabPlacement);
1272        }
1273
1274        protected int preferredTabAreaWidth(int tabPlacement, int height) {
1275            return calculateMaxTabWidth(tabPlacement);
1276        }
1277
1278        public void layoutContainer(Container JavaDoc parent) {
1279            int tabPlacement = tabPane.getTabPlacement();
1280            int tabCount = tabPane.getTabCount();
1281            Insets JavaDoc insets = tabPane.getInsets();
1282            int selectedIndex = tabPane.getSelectedIndex();
1283            Component JavaDoc visibleComponent = getVisibleComponent();
1284
1285            calculateLayoutInfo();
1286
1287            if (selectedIndex < 0) {
1288                if (visibleComponent != null) {
1289                    // The last tab was removed, so remove the component
1290
setVisibleComponent(null);
1291                }
1292            } else {
1293                Component JavaDoc selectedComponent = tabPane.getComponentAt(selectedIndex);
1294                boolean shouldChangeFocus = false;
1295
1296                // In order to allow programs to use a single component
1297
// as the display for multiple tabs, we will not change
1298
// the visible compnent if the currently selected tab
1299
// has a null component. This is a bit dicey, as we don't
1300
// explicitly state we support this in the spec, but since
1301
// programs are now depending on this, we're making it work.
1302
//
1303
if (selectedComponent != null) {
1304                    if (selectedComponent != visibleComponent &&
1305                            visibleComponent != null) {
1306                        if (SwingUtilities.findFocusOwner(visibleComponent) != null) {
1307                            shouldChangeFocus = true;
1308                        }
1309                    }
1310                    setVisibleComponent(selectedComponent);
1311                }
1312                int tx, ty, tw, th; // tab area bounds
1313
int cx, cy, cw, ch; // content area bounds
1314
Insets JavaDoc contentInsets = getContentBorderInsets(tabPlacement);
1315                Rectangle JavaDoc bounds = tabPane.getBounds();
1316                int numChildren = tabPane.getComponentCount();
1317
1318                if (numChildren > 0) {
1319                    switch (tabPlacement) {
1320                        case LEFT:
1321                            // calculate tab area bounds
1322
tw = calculateTabAreaWidth(tabPlacement, runCount,
1323                                    maxTabWidth);
1324                            th = bounds.height - insets.top - insets.bottom;
1325                            tx = insets.left;
1326                            ty = insets.top;
1327    
1328                            // calculate content area bounds
1329
cx = tx + tw + contentInsets.left;
1330                            cy = ty + contentInsets.top;
1331                            cw = bounds.width - insets.left - insets.right - tw
1332                                    - contentInsets.left - contentInsets.right;
1333                            ch = bounds.height - insets.top - insets.bottom
1334                                    - contentInsets.top - contentInsets.bottom;
1335                            break;
1336                        case RIGHT:
1337                            // calculate tab area bounds
1338
tw = calculateTabAreaWidth(tabPlacement, runCount,
1339                                    maxTabWidth);
1340                            th = bounds.height - insets.top - insets.bottom;
1341                            tx = bounds.width - insets.right - tw;
1342                            ty = insets.top;
1343    
1344                            // calculate content area bounds
1345
cx = insets.left + contentInsets.left;
1346                            cy = insets.top + 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 BOTTOM:
1353                            // calculate tab area bounds
1354
tw = bounds.width - insets.left - insets.right;
1355                            th = calculateTabAreaHeight(tabPlacement, runCount,
1356                                    maxTabHeight);
1357                            tx = insets.left;
1358                            ty = bounds.height - insets.bottom - th;
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
1364                                    - contentInsets.left - contentInsets.right;
1365                            ch = bounds.height - insets.top - insets.bottom - th
1366                                    - contentInsets.top - contentInsets.bottom;
1367                            break;
1368                        case TOP:
1369                        default:
1370                            // calculate tab area bounds
1371
tw = bounds.width - insets.left - insets.right;
1372                            th = calculateTabAreaHeight(tabPlacement, runCount,
1373                                    maxTabHeight);
1374                            tx = insets.left;
1375                            ty = insets.top;
1376    
1377                            // calculate content area bounds
1378
cx = tx + contentInsets.left;
1379                            cy = ty + th + contentInsets.top;
1380                            cw = bounds.width - insets.left - insets.right
1381                                    - contentInsets.left - contentInsets.right;
1382                            ch = bounds.height - insets.top - insets.bottom - th
1383                                    - contentInsets.top - contentInsets.bottom;
1384                    }
1385
1386                    for (int i=0; i < numChildren; i++) {
1387                        Component JavaDoc child = tabPane.getComponent(i);
1388
1389                        if (tabScroller != null && child == tabScroller.viewport) {
1390                            JViewport JavaDoc viewport = (JViewport JavaDoc)child;
1391                            Rectangle JavaDoc viewRect = viewport.getViewRect();
1392                            int vw = tw;
1393                            int vh = th;
1394                            Dimension JavaDoc butSize = tabScroller.scrollForwardButton.getPreferredSize();
1395                            switch (tabPlacement) {
1396                                case LEFT:
1397                                case RIGHT:
1398                                    int totalTabHeight = rects[tabCount - 1].y
1399                                            + rects[tabCount - 1].height;
1400                                    if (totalTabHeight > th) {
1401                                        // Allow space for scrollbuttons
1402
vh = (th > 2 * butSize.height) ? th - 2
1403                                                * butSize.height : 0;
1404                                        if (totalTabHeight - viewRect.y <= vh) {
1405                                            // Scrolled to the end, so ensure the
1406
// viewport size is
1407
// such that the scroll offset aligns
1408
// with a tab
1409
vh = totalTabHeight - viewRect.y;
1410                                        }
1411                                    }
1412                                    break;
1413                                case BOTTOM:
1414                                case TOP:
1415                                default:
1416                                    int totalTabWidth = rects[tabCount - 1].x
1417                                            + rects[tabCount - 1].width + renderer.getTabsOverlay();
1418                                    if (totalTabWidth > tw) {
1419                                        // Need to allow space for scrollbuttons
1420
vw = (tw > 2 * butSize.width) ? tw - 2
1421                                                * butSize.width : 0;
1422                                        if (totalTabWidth - viewRect.x <= vw) {
1423                                            // Scrolled to the end, so ensure the
1424
// viewport size is
1425
// such that the scroll offset aligns
1426
// with a tab
1427
vw = totalTabWidth - viewRect.x;
1428                                        }
1429                                    }
1430                            }
1431                            child.setBounds(tx, ty, vw, vh);
1432                    
1433                        } else if (tabScroller != null &&
1434                                   (child == tabScroller.scrollForwardButton ||
1435                                    child == tabScroller.scrollBackwardButton)) {
1436                            Component JavaDoc scrollbutton = child;
1437                            Dimension JavaDoc bsize = scrollbutton.getPreferredSize();
1438                            int bx = 0;
1439                            int by = 0;
1440                            int bw = bsize.width;
1441                            int bh = bsize.height;
1442                            boolean visible = false;
1443                
1444                            switch (tabPlacement) {
1445                                case LEFT:
1446                                case RIGHT:
1447                                    int totalTabHeight = rects[tabCount - 1].y
1448                                            + rects[tabCount - 1].height;
1449                                    if (totalTabHeight > th) {
1450                                        visible = true;
1451                                        bx = (tabPlacement == LEFT ? tx + tw
1452                                                - bsize.width : tx);
1453                                        by = (child == tabScroller.scrollForwardButton) ? bounds.height
1454                                                - insets.bottom - bsize.height
1455                                                : bounds.height - insets.bottom - 2
1456                                                        * bsize.height;
1457                                    }
1458                                    break;
1459    
1460                                case BOTTOM:
1461                                case TOP:
1462                                default:
1463                                    int totalTabWidth = rects[tabCount - 1].x
1464                                            + rects[tabCount - 1].width;
1465    
1466                                    if (totalTabWidth > tw) {
1467                                        visible = true;
1468                                        bx = (child == tabScroller.scrollForwardButton) ? bounds.width
1469                                                - insets.left - bsize.width
1470                                                : bounds.width - insets.left - 2
1471                                                        * bsize.width;
1472                                        by = (tabPlacement == TOP ? ty + th
1473                                                - bsize.height : ty);
1474                                    }
1475                            }
1476                            child.setVisible(visible);
1477                            if (visible) {
1478                                child.setBounds(bx, by, bw, bh);
1479                            }
1480
1481                        } else {
1482                            // All content children...
1483
child.setBounds(cx, cy, cw, ch);
1484                        }
1485                    }
1486                    if (shouldChangeFocus) {
1487                        if (!requestFocusForVisibleComponent()) {
1488                           tabPane.requestFocus();
1489                        }
1490                    }
1491                }
1492            }
1493        }
1494
1495        protected void calculateTabRects(int tabPlacement, int tabCount) {
1496            FontMetrics JavaDoc metrics = getFontMetrics();
1497            Dimension JavaDoc size = tabPane.getSize();
1498            Insets JavaDoc insets = tabPane.getInsets();
1499            Insets JavaDoc tabAreaInsets = getTabAreaInsets(tabPlacement);
1500            int fontHeight = metrics.getHeight();
1501            int selectedIndex = tabPane.getSelectedIndex();
1502            int i, j;
1503            boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
1504            boolean leftToRight = PlasticUtils.isLeftToRight(tabPane);
1505            int x = tabAreaInsets.left;
1506            int y = tabAreaInsets.top;
1507            int totalWidth = 0;
1508            int totalHeight = 0;
1509
1510            //
1511
// Calculate bounds within which a tab run must fit
1512
//
1513
switch(tabPlacement) {
1514                case LEFT:
1515                case RIGHT:
1516                    maxTabWidth = calculateMaxTabWidth(tabPlacement);
1517                    break;
1518                case BOTTOM:
1519                case TOP:
1520                default:
1521                    maxTabHeight = calculateMaxTabHeight(tabPlacement);
1522            }
1523
1524            runCount = 0;
1525            selectedRun = -1;
1526
1527            if (tabCount == 0) {
1528                return;
1529            }
1530
1531            selectedRun = 0;
1532            runCount = 1;
1533
1534            // Run through tabs and lay them out in a single run
1535
Rectangle JavaDoc rect;
1536            for (i = 0; i < tabCount; i++) {
1537                rect = rects[i];
1538
1539                if (!verticalTabRuns) {
1540                    // Tabs on TOP or BOTTOM....
1541
if (i > 0) {
1542                        rect.x = rects[i-1].x + rects[i-1].width;
1543                    } else {
1544                        tabRuns[0] = 0;
1545                        maxTabWidth = 0;
1546                        totalHeight += maxTabHeight;
1547                        rect.x = x;
1548                    }
1549                    rect.width = calculateTabWidth(tabPlacement, i, metrics);
1550                    totalWidth = rect.x + rect.width + renderer.getTabsOverlay();
1551                    maxTabWidth = Math.max(maxTabWidth, rect.width);
1552
1553                    rect.y = y;
1554                    rect.height = maxTabHeight/* - 2*/;
1555
1556                } else {
1557                    // Tabs on LEFT or RIGHT...
1558
if (i > 0) {
1559                        rect.y = rects[i-1].y + rects[i-1].height;
1560                    } else {
1561                        tabRuns[0] = 0;
1562                        maxTabHeight = 0;
1563                        totalWidth = maxTabWidth;
1564                        rect.y = y;
1565                    }
1566                    rect.height = calculateTabHeight(tabPlacement, i, fontHeight);
1567                    totalHeight = rect.y + rect.height;
1568                    maxTabHeight = Math.max(maxTabHeight, rect.height);
1569
1570                    rect.x = x;
1571                    rect.width = maxTabWidth/* - 2*/;
1572
1573                }
1574            }
1575
1576            // Pad the selected tab so that it appears raised in front
1577
padSelectedTab(tabPlacement, selectedIndex);
1578
1579            // if right to left and tab placement on the top or
1580
// the bottom, flip x positions and adjust by widths
1581
if (!leftToRight && !verticalTabRuns) {
1582                int rightMargin = size.width
1583                                  - (insets.right + tabAreaInsets.right);
1584                for (i = 0; i < tabCount; i++) {
1585                    rects[i].x = rightMargin - rects[i].x - rects[i].width;
1586                }
1587            }
1588            tabScroller.tabPanel.setPreferredSize(new Dimension JavaDoc(totalWidth, totalHeight));
1589        }
1590    }
1591
1592    private class ScrollableTabSupport implements ActionListener JavaDoc,
1593            ChangeListener JavaDoc {
1594
1595        public ScrollableTabViewport viewport;
1596        public ScrollableTabPanel tabPanel;
1597        public JButton JavaDoc scrollForwardButton;
1598        public JButton JavaDoc scrollBackwardButton;
1599        public int leadingTabIndex;
1600        private Point JavaDoc tabViewPosition = new Point JavaDoc(0, 0);
1601
1602        ScrollableTabSupport(int tabPlacement) {
1603            viewport = new ScrollableTabViewport();
1604            tabPanel = new ScrollableTabPanel();
1605            viewport.setView(tabPanel);
1606            viewport.addChangeListener(this);
1607            createButtons();
1608        }
1609
1610        /**
1611         * Recreates the scroll buttons and adds them to the TabbedPane.
1612         */

1613        void createButtons() {
1614            if (scrollForwardButton != null) {
1615                tabPane.remove(scrollForwardButton);
1616                scrollForwardButton.removeActionListener(this);
1617                tabPane.remove(scrollBackwardButton);
1618                scrollBackwardButton.removeActionListener(this);
1619            }
1620            int tabPlacement = tabPane.getTabPlacement();
1621            int width = UIManager.getInt("ScrollBar.width");
1622            if (tabPlacement == TOP || tabPlacement == BOTTOM) {
1623                scrollForwardButton = new ArrowButton(EAST, width);
1624                scrollBackwardButton = new ArrowButton(WEST, width);
1625            } else { // tabPlacement = LEFT || RIGHT
1626
scrollForwardButton = new ArrowButton(SOUTH, width);
1627                scrollBackwardButton = new ArrowButton(NORTH, width);
1628            }
1629            scrollForwardButton.addActionListener(this);
1630            scrollBackwardButton.addActionListener(this);
1631            tabPane.add(scrollForwardButton);
1632            tabPane.add(scrollBackwardButton);
1633        }
1634
1635        public void scrollForward(int tabPlacement) {
1636            Dimension JavaDoc viewSize = viewport.getViewSize();
1637            Rectangle JavaDoc viewRect = viewport.getViewRect();
1638
1639            if (tabPlacement == TOP || tabPlacement == BOTTOM) {
1640                if (viewRect.width >= viewSize.width - viewRect.x) {
1641                    return; // no room left to scroll
1642
}
1643            } else { // tabPlacement == LEFT || tabPlacement == RIGHT
1644
if (viewRect.height >= viewSize.height - viewRect.y) {
1645                    return;
1646                }
1647            }
1648            setLeadingTabIndex(tabPlacement, leadingTabIndex + 1);
1649        }
1650
1651        public void scrollBackward(int tabPlacement) {
1652            if (leadingTabIndex == 0) {
1653                return; // no room left to scroll
1654
}
1655            setLeadingTabIndex(tabPlacement, leadingTabIndex - 1);
1656        }
1657
1658        public void setLeadingTabIndex(int tabPlacement, int index) {
1659            leadingTabIndex = index;
1660            Dimension JavaDoc viewSize = viewport.getViewSize();
1661            Rectangle JavaDoc viewRect = viewport.getViewRect();
1662
1663            switch (tabPlacement) {
1664                case TOP:
1665                case BOTTOM:
1666                    tabViewPosition.x = leadingTabIndex == 0 ? 0
1667                            : rects[leadingTabIndex].x - renderer.getTabsOverlay();
1668    
1669                    if ((viewSize.width - tabViewPosition.x) < viewRect.width) {
1670                        // We've scrolled to the end, so adjust the viewport size
1671
// to ensure the view position remains aligned on a tab
1672
// boundary
1673
Dimension JavaDoc extentSize = new Dimension JavaDoc(viewSize.width
1674                                - tabViewPosition.x, viewRect.height);
1675                        viewport.setExtentSize(extentSize);
1676                    }
1677                    break;
1678                case LEFT:
1679                case RIGHT:
1680                    tabViewPosition.y = leadingTabIndex == 0 ? 0
1681                            : rects[leadingTabIndex].y;
1682    
1683                    if ((viewSize.height - tabViewPosition.y) < viewRect.height) {
1684                        // We've scrolled to the end, so adjust the viewport size
1685
// to ensure the view position remains aligned on a tab
1686
// boundary
1687
Dimension JavaDoc extentSize = new Dimension JavaDoc(viewRect.width,
1688                                viewSize.height - tabViewPosition.y);
1689                        viewport.setExtentSize(extentSize);
1690                    }
1691            }
1692            viewport.setViewPosition(tabViewPosition);
1693        }
1694
1695        public void stateChanged(ChangeEvent JavaDoc e) {
1696            JViewport JavaDoc viewport = (JViewport JavaDoc) e.getSource();
1697            int tabPlacement = tabPane.getTabPlacement();
1698            int tabCount = tabPane.getTabCount();
1699            Rectangle JavaDoc vpRect = viewport.getBounds();
1700            Dimension JavaDoc viewSize = viewport.getViewSize();
1701            Rectangle JavaDoc viewRect = viewport.getViewRect();
1702
1703            leadingTabIndex = getClosestTab(viewRect.x, viewRect.y);
1704
1705            // If the tab isn't right aligned, adjust it.
1706
if (leadingTabIndex + 1 < tabCount) {
1707                switch (tabPlacement) {
1708                    case TOP:
1709                    case BOTTOM:
1710                        if (rects[leadingTabIndex].x < viewRect.x) {
1711                            leadingTabIndex++;
1712                        }
1713                        break;
1714                    case LEFT:
1715                    case RIGHT:
1716                        if (rects[leadingTabIndex].y < viewRect.y) {
1717                            leadingTabIndex++;
1718                        }
1719                        break;
1720                }
1721            }
1722            Insets JavaDoc contentInsets = getContentBorderInsets(tabPlacement);
1723            switch (tabPlacement) {
1724                case LEFT:
1725                    tabPane.repaint(vpRect.x + vpRect.width, vpRect.y,
1726                            contentInsets.left, vpRect.height);
1727                    scrollBackwardButton.setEnabled(viewRect.y > 0
1728                            && leadingTabIndex > 0);
1729                    scrollForwardButton.setEnabled(leadingTabIndex < tabCount - 1
1730                            && viewSize.height - viewRect.y > viewRect.height);
1731                    break;
1732                case RIGHT:
1733                    tabPane.repaint(vpRect.x - contentInsets.right, vpRect.y,
1734                            contentInsets.right, vpRect.height);
1735                    scrollBackwardButton.setEnabled(viewRect.y > 0
1736                            && leadingTabIndex > 0);
1737                    scrollForwardButton.setEnabled(leadingTabIndex < tabCount - 1
1738                            && viewSize.height - viewRect.y > viewRect.height);
1739                    break;
1740                case BOTTOM:
1741                    tabPane.repaint(vpRect.x, vpRect.y - contentInsets.bottom,
1742                            vpRect.width, contentInsets.bottom);
1743                    scrollBackwardButton.setEnabled(viewRect.x > 0
1744                            && leadingTabIndex > 0);
1745                    scrollForwardButton.setEnabled(leadingTabIndex < tabCount - 1
1746                            && viewSize.width - viewRect.x > viewRect.width);
1747                    break;
1748                case TOP:
1749                default:
1750                    tabPane.repaint(vpRect.x, vpRect.y + vpRect.height,
1751                            vpRect.width, contentInsets.top);
1752                    scrollBackwardButton.setEnabled(viewRect.x > 0
1753                            && leadingTabIndex > 0);
1754                    scrollForwardButton.setEnabled(leadingTabIndex < tabCount - 1
1755                            && viewSize.width - viewRect.x > viewRect.width);
1756            }
1757        }
1758
1759        /**
1760         * ActionListener for the scroll buttons.
1761         */

1762        public void actionPerformed(ActionEvent JavaDoc e) {
1763            ActionMap JavaDoc map = tabPane.getActionMap();
1764
1765            if (map != null) {
1766                String JavaDoc actionKey;
1767
1768                if (e.getSource() == scrollForwardButton) {
1769                    actionKey = "scrollTabsForwardAction";
1770                } else {
1771                    actionKey = "scrollTabsBackwardAction";
1772                }
1773                Action JavaDoc action = map.get(actionKey);
1774
1775                if (action != null && action.isEnabled()) {
1776                    action.actionPerformed(new ActionEvent JavaDoc(tabPane,
1777                            ActionEvent.ACTION_PERFORMED, null, e.getWhen(), e
1778                                    .getModifiers()));
1779                }
1780            }
1781        }
1782
1783    }
1784
1785    private class ScrollableTabViewport extends JViewport JavaDoc implements UIResource JavaDoc {
1786
1787        public ScrollableTabViewport() {
1788            super();
1789            setName("TabbedPane.scrollableViewport");
1790            setScrollMode(SIMPLE_SCROLL_MODE);
1791            setOpaque(tabPane.isOpaque());
1792            Color JavaDoc bgColor = UIManager.getColor("TabbedPane.tabAreaBackground");
1793            if (bgColor == null) {
1794                bgColor = tabPane.getBackground();
1795            }
1796            setBackground(bgColor);
1797        }
1798    }
1799
1800    private class ScrollableTabPanel extends JPanel JavaDoc implements UIResource JavaDoc {
1801
1802        public ScrollableTabPanel() {
1803            super(null);
1804            setOpaque(tabPane.isOpaque());
1805            Color JavaDoc bgColor = UIManager.getColor("TabbedPane.tabAreaBackground");
1806            if (bgColor == null) {
1807                bgColor = tabPane.getBackground();
1808            }
1809            setBackground(bgColor);
1810        }
1811
1812        public void paintComponent(Graphics JavaDoc g) {
1813            super.paintComponent(g);
1814            PlasticTabbedPaneUI.this.paintTabArea(g, tabPane.getTabPlacement(),
1815                    tabPane.getSelectedIndex());
1816
1817        }
1818    }
1819
1820    private static class ArrowButton extends JButton JavaDoc implements UIResource JavaDoc {
1821
1822        private final int buttonWidth;
1823        private final int direction;
1824        private boolean mouseIsOver;
1825
1826        ArrowButton(int direction, int buttonWidth) {
1827            this.direction = direction;
1828            this.buttonWidth = buttonWidth;
1829            setRequestFocusEnabled(false);
1830        }
1831
1832        protected void processMouseEvent(MouseEvent JavaDoc e) {
1833            super.processMouseEvent(e);
1834            switch (e.getID()) {
1835                case MouseEvent.MOUSE_ENTERED:
1836                    mouseIsOver = true;
1837                    revalidate();
1838                    repaint();
1839                    break;
1840                case MouseEvent.MOUSE_EXITED:
1841                    mouseIsOver = false;
1842                    revalidate();
1843                    repaint();
1844                    break;
1845            }
1846        }
1847
1848        protected void paintBorder(Graphics JavaDoc g) {
1849            if (mouseIsOver && isEnabled()) {
1850                super.paintBorder(g);
1851            }
1852        }
1853
1854        protected void paintComponent(Graphics JavaDoc g) {
1855            if (mouseIsOver) {
1856                super.paintComponent(g);
1857            } else {
1858                g.setColor(getBackground());
1859                g.fillRect(0, 0, getWidth(), getHeight());
1860            }
1861            paintArrow(g);
1862        }
1863
1864        private void paintArrow(Graphics JavaDoc g) {
1865            Color JavaDoc oldColor = g.getColor();
1866
1867            boolean isEnabled = isEnabled();
1868            g.setColor(isEnabled ? PlasticLookAndFeel.getControlInfo()
1869                                 : PlasticLookAndFeel.getControlDisabled());
1870
1871            int arrowWidth, arrowHeight;
1872            switch (direction) {
1873                case NORTH:
1874                case SOUTH:
1875                    arrowWidth = 9;
1876                    arrowHeight = 5;
1877                    break;
1878                case WEST:
1879                case EAST:
1880                default:
1881                    arrowWidth = 5;
1882                    arrowHeight = 9;
1883                    break;
1884            }
1885            int x = (getWidth() - arrowWidth ) / 2;
1886            int y = (getHeight() - arrowHeight) / 2;
1887            g.translate(x, y);
1888
1889            boolean paintShadow = !mouseIsOver || !isEnabled;
1890            Color JavaDoc shadow = isEnabled ? PlasticLookAndFeel.getControlShadow()
1891                                     : UIManager.getColor("ScrollBar.highlight");
1892
1893            switch (direction) {
1894                case NORTH:
1895                    g.fillRect(0, 4, 9, 1);
1896                    g.fillRect(1, 3, 7, 1);
1897                    g.fillRect(2, 2, 5, 1);
1898                    g.fillRect(3, 1, 3, 1);
1899                    g.fillRect(4, 0, 1, 1);
1900                    if (paintShadow) {
1901                        g.setColor(shadow);
1902                        g.fillRect(1, 5, 9, 1);
1903                    }
1904                    break;
1905                case SOUTH:
1906                    g.fillRect(0, 0, 9, 1);
1907                    g.fillRect(1, 1, 7, 1);
1908                    g.fillRect(2, 2, 5, 1);
1909                    g.fillRect(3, 3, 3, 1);
1910                    g.fillRect(4, 4, 1, 1);
1911                    if (paintShadow) {
1912                        g.setColor(shadow);
1913                        g.drawLine(5, 4, 8, 1);
1914                        g.drawLine(5, 5, 9, 1);
1915                    }
1916                    break;
1917                case WEST:
1918                    g.fillRect(0, 4, 1, 1);
1919                    g.fillRect(1, 3, 1, 3);
1920                    g.fillRect(2, 2, 1, 5);
1921                    g.fillRect(3, 1, 1, 7);
1922                    g.fillRect(4, 0, 1, 9);
1923                    if (paintShadow) {
1924                        g.setColor(shadow);
1925                        g.fillRect(5, 1, 1, 9);
1926                    }
1927                    break;
1928                case EAST:
1929                    g.fillRect(0, 0, 1, 9);
1930                    g.fillRect(1, 1, 1, 7);
1931                    g.fillRect(2, 2, 1, 5);
1932                    g.fillRect(3, 3, 1, 3);
1933                    g.fillRect(4, 4, 1, 1);
1934                    if (paintShadow) {
1935                        g.setColor(shadow);
1936                        g.drawLine(1, 8, 4, 5);
1937                        g.drawLine(1, 9, 5, 5);
1938                    }
1939                    break;
1940            }
1941
1942            g.translate(-x, -y);
1943            g.setColor(oldColor);
1944        }
1945
1946        public Dimension JavaDoc getPreferredSize() {
1947            return new Dimension JavaDoc(buttonWidth, buttonWidth);
1948        }
1949
1950        public Dimension JavaDoc getMinimumSize() {
1951            return getPreferredSize();
1952        }
1953
1954        public Dimension JavaDoc getMaximumSize() {
1955            return new Dimension JavaDoc(Integer.MAX_VALUE, Integer.MAX_VALUE);
1956        }
1957    }
1958
1959    /**
1960     * This is the abstract superclass for all TabbedPane renderers.
1961     * Those will be defined in the rest of this file
1962     */

1963    private static abstract class AbstractRenderer {
1964
1965        protected static final Insets JavaDoc EMPTY_INSETS = new Insets JavaDoc(0, 0, 0, 0);
1966        protected static final Insets JavaDoc NORTH_INSETS = new Insets JavaDoc(1, 0, 0, 0);
1967        protected static final Insets JavaDoc WEST_INSETS = new Insets JavaDoc(0, 1, 0, 0);
1968        protected static final Insets JavaDoc SOUTH_INSETS = new Insets JavaDoc(0, 0, 1, 0);
1969        protected static final Insets JavaDoc EAST_INSETS = new Insets JavaDoc(0, 0, 0, 1);
1970
1971        protected final JTabbedPane JavaDoc tabPane;
1972        protected final int tabPlacement;
1973        protected Color JavaDoc shadowColor;
1974        protected Color JavaDoc darkShadow;
1975        protected Color JavaDoc selectColor;
1976        protected Color JavaDoc selectLight;
1977        protected Color JavaDoc selectHighlight;
1978        protected Color JavaDoc lightHighlight;
1979        protected Color JavaDoc focus;
1980
1981        private AbstractRenderer(JTabbedPane JavaDoc tabPane) {
1982            initColors();
1983            this.tabPane = tabPane;
1984            this.tabPlacement = tabPane.getTabPlacement();
1985        }
1986
1987        private static AbstractRenderer createRenderer(JTabbedPane JavaDoc tabPane) {
1988            switch (tabPane.getTabPlacement()) {
1989                case SwingConstants.TOP :
1990                    return new TopRenderer(tabPane);
1991                case SwingConstants.BOTTOM :
1992                    return new BottomRenderer(tabPane);
1993                case SwingConstants.LEFT :
1994                    return new LeftRenderer(tabPane);
1995                case SwingConstants.RIGHT :
1996                    return new RightRenderer(tabPane);
1997                default :
1998                    return new TopRenderer(tabPane);
1999            }
2000        }
2001
2002        private static AbstractRenderer createEmbeddedRenderer(JTabbedPane JavaDoc tabPane) {
2003            switch (tabPane.getTabPlacement()) {
2004                case SwingConstants.TOP :
2005                    return new TopEmbeddedRenderer(tabPane);
2006                case SwingConstants.BOTTOM :
2007                    return new BottomEmbeddedRenderer(tabPane);
2008                case SwingConstants.LEFT :
2009                    return new LeftEmbeddedRenderer(tabPane);
2010                case SwingConstants.RIGHT :
2011                    return new RightEmbeddedRenderer(tabPane);
2012                default :
2013                    return new TopEmbeddedRenderer(tabPane);
2014            }
2015        }
2016
2017        private void initColors() {
2018            shadowColor = UIManager.getColor("TabbedPane.shadow");
2019            darkShadow = UIManager.getColor("TabbedPane.darkShadow");
2020            selectColor = UIManager.getColor("TabbedPane.selected");
2021            focus = UIManager.getColor("TabbedPane.focus");
2022            selectHighlight = UIManager.getColor("TabbedPane.selectHighlight");
2023            lightHighlight = UIManager.getColor("TabbedPane.highlight");
2024            selectLight =
2025                new Color JavaDoc(
2026                    (2 * selectColor.getRed() + selectHighlight.getRed()) / 3,
2027                    (2 * selectColor.getGreen() + selectHighlight.getGreen()) / 3,
2028                    (2 * selectColor.getBlue() + selectHighlight.getBlue()) / 3);
2029        }
2030
2031        protected boolean isFirstDisplayedTab(int tabIndex, int position, int paneBorder) {
2032            return tabIndex == 0;
2033// return (position - paneBorder) < 8;
2034
}
2035
2036        protected Insets JavaDoc getTabAreaInsets(Insets JavaDoc defaultInsets) {
2037            return defaultInsets;
2038        }
2039
2040        protected Insets JavaDoc getContentBorderInsets(Insets JavaDoc defaultInsets) {
2041            return defaultInsets;
2042        }
2043
2044        /**
2045         * Returns the amount by which the label should be shifted horizontally
2046         */

2047        protected int getTabLabelShiftX(int tabIndex, boolean isSelected) {
2048            return 0;
2049        }
2050
2051        /**
2052         * Returns the amount by which the label should be shifted vertically
2053         */

2054        protected int getTabLabelShiftY(int tabIndex, boolean isSelected) {
2055            return 0;
2056        }
2057
2058        /**
2059         * Returns the amount of overlap for two Runs
2060         */

2061        protected int getTabRunOverlay(int tabRunOverlay) {
2062            return tabRunOverlay;
2063        }
2064
2065        /**
2066         * Returns if a run should be padded with empty space
2067         * to take up as much room as the others
2068         */

2069        protected boolean shouldPadTabRun(int run, boolean aPriori) {
2070            return aPriori;
2071        }
2072
2073        /**
2074         * Returns the amount by which the run number <code>run</code>
2075         * should be indented. Add a few pixels for every run to make
2076         * diagonal lines align.
2077         */

2078        protected int getTabRunIndent(int run) {
2079            return 0;
2080        }
2081
2082        /**
2083         * Returns the insets for the given tab.
2084         */

2085        abstract protected Insets JavaDoc getTabInsets(int tabIndex, Insets JavaDoc tabInsets);
2086
2087        /**
2088         * Draws the rectancle around the Tab label which indicates keyboard focus
2089         */

2090        abstract protected void paintFocusIndicator(
2091            Graphics JavaDoc g,
2092            Rectangle JavaDoc[] rects,
2093            int tabIndex,
2094            Rectangle JavaDoc iconRect,
2095            Rectangle JavaDoc textRect,
2096            boolean isSelected);
2097
2098        /**
2099         * Fills the background of the given tab to make sure overlap of
2100         * tabs is handled correctly.
2101         */

2102        abstract protected void paintTabBackground(Graphics JavaDoc g, int tabIndex, int x, int y, int w, int h, boolean isSelected);
2103
2104        /**
2105         * Paints the border around the given tab
2106         */

2107        abstract protected void paintTabBorder(Graphics JavaDoc g, int tabIndex, int x, int y, int w, int h, boolean isSelected);
2108
2109        /**
2110         * Returns additional the insets for the selected tab. This allows to "raise"
2111         * The selected tab over the others
2112         */

2113        protected Insets JavaDoc getSelectedTabPadInsets() {
2114            return EMPTY_INSETS;
2115        }
2116
2117        /**
2118         * Draws the top edge of the border around the content area
2119         * Draw unbroken line for tabs are not on TOP
2120         * override where appropriate
2121         */

2122        protected void paintContentBorderTopEdge(
2123            Graphics JavaDoc g,
2124            int x,
2125            int y,
2126            int w,
2127            int h,
2128            boolean drawBroken,
2129            Rectangle JavaDoc selRect,
2130            boolean isContentBorderPainted) {
2131            if (isContentBorderPainted) {
2132                g.setColor(selectHighlight);
2133                g.fillRect(x, y, w - 1, 1);
2134            }
2135        }
2136
2137        /**
2138         * Draws the bottom edge of the Border around the content area
2139         * Draw broken line if selected tab is visible and adjacent to content
2140         * and TabPlacement is same as painted edge
2141         */

2142        protected void paintContentBorderBottomEdge(
2143            Graphics JavaDoc g,
2144            int x,
2145            int y,
2146            int w,
2147            int h,
2148            boolean drawBroken,
2149            Rectangle JavaDoc selRect,
2150            boolean isContentBorderPainted) {
2151            if (isContentBorderPainted) {
2152                g.setColor(darkShadow);
2153                g.fillRect(x, y + h - 1, w - 1, 1);
2154            }
2155        }
2156
2157        /**
2158         * Draws the left edge of the Border around the content area
2159         * Draw broken line if selected tab is visible and adjacent to content
2160         * and TabPlacement is same as painted edge
2161         */

2162        protected void paintContentBorderLeftEdge(
2163            Graphics JavaDoc g,
2164            int x,
2165            int y,
2166            int w,
2167            int h,
2168            boolean drawBroken,
2169            Rectangle JavaDoc selRect,
2170            boolean isContentBorderPainted) {
2171            if (isContentBorderPainted) {
2172                g.setColor(selectHighlight);
2173                g.fillRect(x, y, 1, h - 1);
2174            }
2175        }
2176
2177        /**
2178         * Draws the right edge of the Border around the content area
2179         * Draw broken line if selected tab is visible and adjacent to content
2180         * and TabPlacement is same as painted edge
2181         */

2182        protected void paintContentBorderRightEdge(
2183            Graphics JavaDoc g,
2184            int x,
2185            int y,
2186            int w,
2187            int h,
2188            boolean drawBroken,
2189            Rectangle JavaDoc selRect,
2190            boolean isContentBorderPainted) {
2191            if (isContentBorderPainted) {
2192                g.setColor(darkShadow);
2193               g.fillRect(x + w - 1, y, 1, h);
2194            }
2195        }
2196        
2197        /**
2198         * Returns the amount of overlap for two tabs
2199         */

2200        protected int getTabsOverlay() {
2201            return 0;
2202        }
2203    }
2204
2205    /**
2206     * The renderer for the case where tabs are displayed below the contents
2207     * and with minimal decoration.
2208     */

2209    private static class BottomEmbeddedRenderer extends AbstractRenderer {
2210
2211        private BottomEmbeddedRenderer(JTabbedPane JavaDoc tabPane) {
2212            super(tabPane);
2213        }
2214
2215        protected Insets JavaDoc getTabAreaInsets(Insets JavaDoc insets) {
2216            return EMPTY_INSETS;
2217        }
2218
2219        protected Insets JavaDoc getContentBorderInsets(Insets JavaDoc defaultInsets) {
2220            return SOUTH_INSETS;
2221        }
2222
2223        protected Insets JavaDoc getSelectedTabPadInsets() {
2224            return EMPTY_INSETS;
2225        }
2226
2227        protected Insets JavaDoc getTabInsets(int tabIndex, Insets JavaDoc tabInsets) {
2228            return new Insets JavaDoc(tabInsets.top, tabInsets.left, tabInsets.bottom, tabInsets.right);
2229        }
2230        /**
2231         * minimal decoration is really minimal: noFocus
2232         */

2233        protected void paintFocusIndicator(
2234            Graphics JavaDoc g,
2235            Rectangle JavaDoc[] rects,
2236            int tabIndex,
2237            Rectangle JavaDoc iconRect,
2238            Rectangle JavaDoc textRect,
2239            boolean isSelected) {
2240            // Embedded tabs paint no focus.
2241
}
2242
2243        protected void paintTabBackground(Graphics JavaDoc g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
2244
2245            g.setColor(selectColor);
2246            g.fillRect(x, y, w + 1, h);
2247        }
2248
2249        protected void paintTabBorder(Graphics JavaDoc g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
2250
2251            int bottom = h;
2252            int right = w + 1;
2253
2254            g.translate(x, y);
2255            if (isFirstDisplayedTab(tabIndex, x, tabPane.getBounds().x)) {
2256                if (isSelected) {
2257                    // selected and first in line
2258
g.setColor(shadowColor);
2259                    g.fillRect(right, 0, 1, bottom - 1);
2260                    g.fillRect(right - 1, bottom - 1, 1, 1);
2261                    // it is open to discussion if the outer border of the tab
2262
// should be painted because in the primary case it won't
2263
// be visible anyway. uncomment the following two lines if wanted
2264
// g.fillRect(0,bottom, right, 1);
2265
// g.fillRect(-1,0,1,bottom;
2266
g.setColor(selectHighlight);
2267                    g.fillRect(0, 0, 1, bottom);
2268                    g.fillRect(right - 1, 0, 1, bottom - 1);
2269                    g.fillRect(1, bottom - 1, right - 2, 1);
2270                } else {
2271                    //not selected and first in line
2272
}
2273            } else {
2274                if (isSelected) {
2275                    //selected and not first in line
2276
g.setColor(shadowColor);
2277                    g.fillRect(0, 0, 1, bottom - 1);
2278                    g.fillRect(1, bottom - 1, 1, 1);
2279                    g.fillRect(right, 0, 1, bottom - 1);
2280                    g.fillRect(right - 1, bottom - 1, 1, 1);
2281                    // outside line:
2282
// g.fillRect(2,bottom, right-3, 1);
2283
g.setColor(selectHighlight);
2284                    g.fillRect(1, 0, 1, bottom - 1);
2285                    g.fillRect(right - 1, 0, 1, bottom - 1);
2286                    g.fillRect(2, bottom - 1, right - 3, 1);
2287                } else {
2288                    g.setColor(shadowColor);
2289                    g.fillRect(1, h / 2, 1, h - (h / 2));
2290                }
2291            }
2292            g.translate(-x, -y);
2293        }
2294
2295        protected void paintContentBorderBottomEdge(
2296            Graphics JavaDoc g,
2297            int x,
2298            int y,
2299            int w,
2300            int h,
2301            boolean drawBroken,
2302            Rectangle JavaDoc selRect,
2303            boolean isContentBorderPainted) {
2304
2305            g.setColor(shadowColor);
2306            g.fillRect(x, y + h - 1, w, 1);
2307        }
2308
2309    }
2310
2311    /**
2312     * The renderer for the case where Tabs are below the content and
2313     * decoration is standard
2314     */

2315    private static final class BottomRenderer extends AbstractRenderer {
2316        
2317        private BottomRenderer(JTabbedPane JavaDoc tabPane) {
2318            super(tabPane);
2319        }
2320
2321        protected Insets JavaDoc getTabAreaInsets(Insets JavaDoc defaultInsets) {
2322            return new Insets JavaDoc(defaultInsets.top, defaultInsets.left + 5, defaultInsets.bottom, defaultInsets.right);
2323        }
2324
2325        protected int getTabLabelShiftY(int tabIndex, boolean isSelected) {
2326            return isSelected? 0 : -1;
2327        }
2328
2329        protected int getTabRunOverlay(int tabRunOverlay) {
2330            return tabRunOverlay - 2;
2331        }
2332
2333        protected int getTabRunIndent(int run) {
2334            return 6 * run;
2335        }
2336
2337        protected Insets JavaDoc getSelectedTabPadInsets() {
2338            return SOUTH_INSETS;
2339        }
2340
2341        protected Insets JavaDoc getTabInsets(int tabIndex, Insets JavaDoc tabInsets) {
2342            return new Insets JavaDoc(tabInsets.top, tabInsets.left - 2, tabInsets.bottom, tabInsets.right - 2);
2343        }
2344
2345        protected void paintFocusIndicator(
2346            Graphics JavaDoc g,
2347            Rectangle JavaDoc[] rects,
2348            int tabIndex,
2349            Rectangle JavaDoc iconRect,
2350            Rectangle JavaDoc textRect,
2351            boolean isSelected) {
2352
2353            if (!tabPane.hasFocus() || !isSelected)
2354                return;
2355            Rectangle JavaDoc tabRect = rects[tabIndex];
2356            int top = tabRect.y;
2357            int left = tabRect.x + 6;
2358            int height = tabRect.height - 3;
2359            int width = tabRect.width - 12;
2360            g.setColor(focus);
2361            g.drawRect(left, top, width, height);
2362        }
2363
2364        protected void paintTabBackground(Graphics JavaDoc g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
2365
2366            g.setColor(selectColor);
2367            g.fillRect(x, y, w, h);
2368        }
2369
2370        protected void paintTabBorder(Graphics JavaDoc g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
2371
2372            int bottom = h - 1;
2373            int right = w + 4;
2374
2375            g.translate(x - 3, y);
2376
2377            // Paint Border
2378
g.setColor(selectHighlight);
2379
2380            // Paint left
2381
g.fillRect(0, 0, 1, 2);
2382            g.drawLine(0, 2, 4, bottom - 4);
2383            g.fillRect(5, bottom - 3, 1, 2);
2384            g.fillRect(6, bottom - 1, 1, 1);
2385
2386            // Paint bootom
2387
g.fillRect(7, bottom, 1, 1);
2388            g.setColor(darkShadow);
2389            g.fillRect(8, bottom, right - 13, 1);
2390
2391            // Paint right
2392
g.drawLine(right + 1, 0, right - 3, bottom - 4);
2393            g.fillRect(right - 4, bottom - 3, 1, 2);
2394            g.fillRect(right - 5, bottom - 1, 1, 1);
2395
2396            g.translate(-x + 3, -y);
2397        }
2398
2399        protected void paintContentBorderBottomEdge(
2400            Graphics JavaDoc g,
2401            int x,
2402            int y,
2403            int w,
2404            int h,
2405            boolean drawBroken,
2406            Rectangle JavaDoc selRect,
2407            boolean isContentBorderPainted) {
2408            int bottom = y + h - 1;
2409            int right = x + w - 1;
2410            g.translate(x, bottom);
2411            if (drawBroken && selRect.x >= x && selRect.x <= x + w) {
2412                // Break line to show visual connection to selected tab
2413
g.setColor(darkShadow);
2414                g.fillRect(0, 0, selRect.x - x - 2, 1);
2415                if (selRect.x + selRect.width < x + w - 2) {
2416                    g.setColor(darkShadow);
2417                   g.fillRect(selRect.x + selRect.width + 2 - x, 0, right - selRect.x - selRect.width - 2, 1);
2418                }
2419            } else {
2420                g.setColor(darkShadow);
2421                g.fillRect(0, 0, w - 1, 1);
2422            }
2423            g.translate(-x, -bottom);
2424        }
2425        
2426        protected int getTabsOverlay() {
2427            return 4;
2428        }
2429
2430    }
2431
2432    /**
2433     * The renderer for tabs on the left with minimal decoration
2434     */

2435    private static class LeftEmbeddedRenderer extends AbstractRenderer {
2436
2437        private LeftEmbeddedRenderer(JTabbedPane JavaDoc tabPane) {
2438            super(tabPane);
2439        }
2440
2441        protected Insets JavaDoc getTabAreaInsets(Insets JavaDoc insets) {
2442            return EMPTY_INSETS;
2443        }
2444
2445        protected Insets JavaDoc getContentBorderInsets(Insets JavaDoc defaultInsets) {
2446            return WEST_INSETS;
2447        }
2448
2449        protected int getTabRunOverlay(int tabRunOverlay) {
2450            return 0;
2451        }
2452
2453        protected boolean shouldPadTabRun(int run, boolean aPriori) {
2454            return false;
2455        }
2456
2457        protected Insets JavaDoc getTabInsets(int tabIndex, Insets JavaDoc tabInsets) {
2458            return new Insets JavaDoc(tabInsets.top, tabInsets.left, tabInsets.bottom, tabInsets.right);
2459        }
2460
2461        protected Insets JavaDoc getSelectedTabPadInsets() {
2462            return EMPTY_INSETS;
2463        }
2464
2465        /**
2466         * minimal decoration is really minimal: no focus
2467         */

2468        protected void paintFocusIndicator(
2469            Graphics JavaDoc g,
2470            Rectangle JavaDoc[] rects,
2471            int tabIndex,
2472            Rectangle JavaDoc iconRect,
2473            Rectangle JavaDoc textRect,
2474            boolean isSelected) {
2475            // Embedded tabs paint no focus.
2476
}
2477
2478        protected void paintTabBackground(Graphics JavaDoc g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
2479            g.setColor(selectColor);
2480            g.fillRect(x, y, w, h);
2481        }
2482
2483        protected void paintTabBorder(Graphics JavaDoc g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
2484
2485            int bottom = h;
2486            int right = w;
2487
2488            g.translate(x, y);
2489
2490            if (isFirstDisplayedTab(tabIndex, y, tabPane.getBounds().y)) {
2491                if (isSelected) {
2492                    //selected and first in line
2493
g.setColor(selectHighlight);
2494                    g.fillRect(0, 0, right, 1);
2495                    g.fillRect(0, 0, 1, bottom - 1);
2496                    g.fillRect(1, bottom - 1, right - 1, 1);
2497                    g.setColor(shadowColor);
2498                    g.fillRect(0, bottom - 1, 1, 1);
2499                    g.fillRect(1, bottom, right - 1, 1);
2500                    // outside line:
2501
// g.fillRect(-1,0,1,bottom-1)
2502
} else {
2503                    //not selected but first in line
2504
}
2505            } else {
2506                if (isSelected) {
2507                    //selected but not first in line
2508
g.setColor(selectHighlight);
2509                    g.fillRect(1, 1, right - 1, 1);
2510                    g.fillRect(0, 2, 1, bottom - 2);
2511                    g.fillRect(1, bottom - 1, right - 1, 1);
2512                    g.setColor(shadowColor);
2513                    g.fillRect(1, 0, right - 1, 1);
2514                    g.fillRect(0, 1, 1, 1);
2515                    g.fillRect(0, bottom - 1, 1, 1);
2516                    g.fillRect(1, bottom, right - 1, 1);
2517                    // outside line:
2518
// g.fillRect(-1,2,1,bottom-3)
2519
} else {
2520                    g.setColor(shadowColor);
2521                    g.fillRect(0, 0, right / 3, 1);
2522                }
2523            }
2524
2525            g.translate(-x, -y);
2526        }
2527
2528        protected void paintContentBorderLeftEdge(
2529            Graphics JavaDoc g,
2530            int x,
2531            int y,
2532            int w,
2533            int h,
2534            boolean drawBroken,
2535            Rectangle JavaDoc selRect,
2536            boolean isContentBorderPainted) {
2537            g.setColor(shadowColor);
2538            g.fillRect(x, y, 1, h);
2539        }
2540    }
2541
2542    /**
2543     * Renderer for tabs on the left with normal decoration
2544     */

2545    private static class LeftRenderer extends AbstractRenderer {
2546
2547        private LeftRenderer(JTabbedPane JavaDoc tabPane) {
2548            super(tabPane);
2549        }
2550
2551        protected Insets JavaDoc getTabAreaInsets(Insets JavaDoc defaultInsets) {
2552            return new Insets JavaDoc(defaultInsets.top + 4, defaultInsets.left, defaultInsets.bottom, defaultInsets.right);
2553        }
2554
2555        protected int getTabLabelShiftX(int tabIndex, boolean isSelected) {
2556            return 1;
2557        }
2558
2559        protected int getTabRunOverlay(int tabRunOverlay) {
2560            return 1;
2561        }
2562
2563        protected boolean shouldPadTabRun(int run, boolean aPriori) {
2564            return false;
2565        }
2566
2567        protected Insets JavaDoc getTabInsets(int tabIndex, Insets JavaDoc tabInsets) {
2568            return new Insets JavaDoc(tabInsets.top, tabInsets.left - 5, tabInsets.bottom + 1, tabInsets.right - 5);
2569        }
2570
2571        protected Insets JavaDoc getSelectedTabPadInsets() {
2572            return WEST_INSETS;
2573        }
2574
2575        protected void paintFocusIndicator(
2576            Graphics JavaDoc g,
2577            Rectangle JavaDoc[] rects,
2578            int tabIndex,
2579            Rectangle JavaDoc iconRect,
2580            Rectangle JavaDoc textRect,
2581            boolean isSelected) {
2582
2583            if (!tabPane.hasFocus() || !isSelected)
2584                return;
2585            Rectangle JavaDoc tabRect = rects[tabIndex];
2586            int top = tabRect.y + 2;
2587            int left = tabRect.x + 3;
2588            int height = tabRect.height - 5;
2589            int width = tabRect.width - 6;
2590            g.setColor(focus);
2591            g.drawRect(left, top, width, height);
2592        }
2593
2594        protected void paintTabBackground(Graphics JavaDoc g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
2595            if (!isSelected) {
2596                g.setColor(selectLight);
2597                g.fillRect(x + 1, y + 1, w - 1, h - 2);
2598            } else {
2599                g.setColor(selectColor);
2600                g.fillRect(x + 1, y + 1, w - 3, h - 2);
2601            }
2602        }
2603
2604        protected void paintTabBorder(Graphics JavaDoc g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
2605
2606            int bottom = h - 1;
2607            int left = 0;
2608            g.translate(x, y);
2609
2610            // Paint Border
2611
g.setColor(selectHighlight);
2612            // Paint top
2613
g.fillRect(left + 2, 0, w - 2 - left, 1);
2614
2615            // Paint left
2616
g.fillRect(left + 1, 1, 1, 1);
2617            g.fillRect(left, 2, 1, bottom - 3);
2618            g.setColor(darkShadow);
2619            g.fillRect(left + 1, bottom - 1, 1, 1);
2620
2621            // Paint bottom
2622
g.fillRect(left + 2, bottom, w - 2 - left, 1);
2623
2624            g.translate(-x, -y);
2625        }
2626
2627        protected void paintContentBorderLeftEdge(
2628            Graphics JavaDoc g,
2629            int x,
2630            int y,
2631            int w,
2632            int h,
2633            boolean drawBroken,
2634            Rectangle JavaDoc selRect,
2635            boolean isContentBorderPainted) {
2636            g.setColor(selectHighlight);
2637            if (drawBroken && selRect.y >= y && selRect.y <= y + h) {
2638                // Break line to show visual connection to selected tab
2639
g.fillRect(x, y, 1, selRect.y + 1 - y);
2640                if (selRect.y + selRect.height < y + h - 2) {
2641                    g.fillRect(x, selRect.y + selRect.height - 1, 1, y + h - selRect.y - selRect.height);
2642                }
2643            } else {
2644                g.fillRect(x, y, 1, h - 1);
2645            }
2646        }
2647
2648    }
2649
2650    /**
2651     * The renderer for tabs on the right with minimal decoration
2652     */

2653    private static class RightEmbeddedRenderer extends AbstractRenderer {
2654
2655        private RightEmbeddedRenderer(JTabbedPane JavaDoc tabPane) {
2656            super(tabPane);
2657        }
2658
2659        protected Insets JavaDoc getTabAreaInsets(Insets JavaDoc insets) {
2660            return EMPTY_INSETS;
2661        }
2662
2663        protected Insets JavaDoc getContentBorderInsets(Insets JavaDoc defaultInsets) {
2664            return EAST_INSETS;
2665        }
2666
2667        protected int getTabRunIndent(int run) {
2668            return 4 * run;
2669        }
2670
2671        protected int getTabRunOverlay(int tabRunOverlay) {
2672            return 0;
2673        }
2674
2675        protected boolean shouldPadTabRun(int run, boolean aPriori) {
2676            return false;
2677        }
2678
2679        protected Insets JavaDoc getTabInsets(int tabIndex, Insets JavaDoc tabInsets) {
2680            return new Insets JavaDoc(tabInsets.top, tabInsets.left, tabInsets.bottom, tabInsets.right);
2681        }
2682
2683        protected Insets JavaDoc getSelectedTabPadInsets() {
2684            return EMPTY_INSETS;
2685        }
2686
2687        /**
2688         * minmal decoration : no focus
2689         */

2690        protected void paintFocusIndicator(
2691            Graphics JavaDoc g,
2692            Rectangle JavaDoc[] rects,
2693            int tabIndex,
2694            Rectangle JavaDoc iconRect,
2695            Rectangle JavaDoc textRect,
2696            boolean isSelected) {
2697            // Embedded tabs paint no focus.
2698
}
2699
2700        protected void paintTabBackground(Graphics JavaDoc g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
2701
2702            g.setColor(selectColor);
2703            g.fillRect(x, y, w, h);
2704        }
2705
2706        protected void paintTabBorder(Graphics JavaDoc g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
2707
2708            int bottom = h;
2709            int right = w - 1;
2710
2711            g.translate(x + 1, y);
2712
2713            if (isFirstDisplayedTab(tabIndex, y, tabPane.getBounds().y)) {
2714                if (isSelected) {
2715                    //selected and first in line
2716
g.setColor(shadowColor);
2717                    //outside lines:
2718
// g.fillRect(0,-1,right,1);
2719
// g.fillRect(right,-1,1,bottom);
2720
g.fillRect(right - 1, bottom - 1, 1, 1);
2721                    g.fillRect(0, bottom, right - 1, 1);
2722                    g.setColor(selectHighlight);
2723                    g.fillRect(0, 0, right - 1, 1);
2724                    g.fillRect(right - 1, 0, 1, bottom - 1);
2725                    g.fillRect(0, bottom - 1, right - 1, 1);
2726                }
2727            } else {
2728                if (isSelected) {
2729                    //selected but not first in line
2730
g.setColor(shadowColor);
2731                    g.fillRect(0, -1, right - 1, 1);
2732                    g.fillRect(right - 1, 0, 1, 1);
2733                    //outside line:
2734
// g.fillRect(right,0,1,bottom);
2735
g.fillRect(right - 1, bottom - 1, 1, 1);
2736                    g.fillRect(0, bottom, right - 1, 1);
2737                    g.setColor(selectHighlight);
2738                    g.fillRect(0, 0, right - 1, 1);
2739                    g.fillRect(right - 1, 1, 1, bottom - 2);
2740                    g.fillRect(0, bottom - 1, right - 1, 1);
2741                } else {
2742                    //not selected and not first in line
2743
g.setColor(shadowColor);
2744                    g.fillRect(2 * right / 3, 0, right / 3, 1);
2745                }
2746            }
2747            g.translate(-x - 1, -y);
2748        }
2749
2750        protected void paintContentBorderRightEdge(
2751            Graphics JavaDoc g,
2752            int x,
2753            int y,
2754            int w,
2755            int h,
2756            boolean drawBroken,
2757            Rectangle JavaDoc selRect,
2758            boolean isContentBorderPainted) {
2759            g.setColor(shadowColor);
2760            g.fillRect(x + w - 1, y, 1, h);
2761        }
2762
2763    }
2764
2765    /**
2766     * renderer for tabs on the right with normal decoration
2767     */

2768    private static class RightRenderer extends AbstractRenderer {
2769
2770        private RightRenderer(JTabbedPane JavaDoc tabPane) {
2771            super(tabPane);
2772        }
2773
2774        protected int getTabLabelShiftX(int tabIndex, boolean isSelected) {
2775            return 1;
2776        }
2777
2778        protected int getTabRunOverlay(int tabRunOverlay) {
2779            return 1;
2780        }
2781
2782        protected boolean shouldPadTabRun(int run, boolean aPriori) {
2783            return false;
2784        }
2785
2786        protected Insets JavaDoc getTabInsets(int tabIndex, Insets JavaDoc tabInsets) {
2787            return new Insets JavaDoc(tabInsets.top, tabInsets.left - 5, tabInsets.bottom + 1, tabInsets.right - 5);
2788        }
2789
2790        protected Insets JavaDoc getSelectedTabPadInsets() {
2791            return EAST_INSETS;
2792        }
2793
2794        protected void paintFocusIndicator(
2795            Graphics JavaDoc g,
2796            Rectangle JavaDoc[] rects,
2797            int tabIndex,
2798            Rectangle JavaDoc iconRect,
2799            Rectangle JavaDoc textRect,
2800            boolean isSelected) {
2801
2802            if (!tabPane.hasFocus() || !isSelected)
2803                return;
2804            Rectangle JavaDoc tabRect = rects[tabIndex];
2805            int top = tabRect.y + 2;
2806            int left = tabRect.x + 3;
2807            int height = tabRect.height - 5;
2808            int width = tabRect.width - 6;
2809            g.setColor(focus);
2810            g.drawRect(left, top, width, height);
2811        }
2812
2813        protected void paintTabBackground(Graphics JavaDoc g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
2814            if (!isSelected) {
2815                g.setColor(selectLight);
2816                g.fillRect(x, y, w, h);
2817            } else {
2818                g.setColor(selectColor);
2819                g.fillRect(x + 2, y, w - 2, h);
2820            }
2821        }
2822
2823        protected void paintTabBorder(Graphics JavaDoc g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
2824
2825            int bottom = h - 1;
2826            int right = w;
2827
2828            g.translate(x, y);
2829
2830            // Paint Border
2831

2832            g.setColor(selectHighlight);
2833            g.fillRect(0, 0, right - 1, 1);
2834            // Paint right
2835
g.setColor(darkShadow);
2836            g.fillRect(right - 1, 1, 1, 1);
2837            g.fillRect(right, 2, 1, bottom - 3);
2838            // Paint bottom
2839
g.fillRect(right - 1, bottom - 1, 1, 1);
2840            g.fillRect(0, bottom, right - 1, 1);
2841
2842            g.translate(-x, -y);
2843        }
2844
2845        protected void paintContentBorderRightEdge(
2846            Graphics JavaDoc g,
2847            int x,
2848            int y,
2849            int w,
2850            int h,
2851            boolean drawBroken,
2852            Rectangle JavaDoc selRect,
2853            boolean isContentBorderPainted) {
2854            g.setColor(darkShadow);
2855            if (drawBroken && selRect.y >= y && selRect.y <= y + h) {
2856                // Break line to show visual connection to selected tab
2857
g.fillRect(x + w - 1, y, 1, selRect.y - y);
2858                if (selRect.y + selRect.height < y + h - 2) {
2859                    g.fillRect(x + w - 1, selRect.y + selRect.height, 1, y + h - selRect.y - selRect.height);
2860                }
2861            } else {
2862                g.fillRect(x + w - 1, y, 1, h - 1);
2863            }
2864        }
2865    }
2866
2867    /**
2868     * Renderer for tabs on top with minimal decoration
2869     */

2870    private static class TopEmbeddedRenderer extends AbstractRenderer {
2871
2872        private TopEmbeddedRenderer(JTabbedPane JavaDoc tabPane) {
2873            super(tabPane);
2874        }
2875
2876        protected Insets JavaDoc getTabAreaInsets(Insets JavaDoc insets) {
2877            return EMPTY_INSETS;
2878        }
2879
2880        protected Insets JavaDoc getContentBorderInsets(Insets JavaDoc defaultInsets) {
2881            return NORTH_INSETS;
2882        }
2883
2884        protected Insets JavaDoc getTabInsets(int tabIndex, Insets JavaDoc tabInsets) {
2885            return new Insets JavaDoc(tabInsets.top, tabInsets.left + 1, tabInsets.bottom, tabInsets.right);
2886        }
2887
2888        protected Insets JavaDoc getSelectedTabPadInsets() {
2889            return EMPTY_INSETS;
2890        }
2891
2892        /**
2893         * Minimal decoration: no focus
2894         */

2895        protected void paintFocusIndicator(
2896            Graphics JavaDoc g,
2897            Rectangle JavaDoc[] rects,
2898            int tabIndex,
2899            Rectangle JavaDoc iconRect,
2900            Rectangle JavaDoc textRect,
2901            boolean isSelected) {
2902            // Embedded tabs paint no focus.
2903
}
2904
2905        protected void paintTabBackground(Graphics JavaDoc g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
2906
2907            g.setColor(selectColor);
2908            g.fillRect(x, y, w, h);
2909        }
2910
2911        protected void paintTabBorder(Graphics JavaDoc g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
2912
2913            g.translate(x, y);
2914
2915            int right = w;
2916            int bottom = h;
2917
2918            if (isFirstDisplayedTab(tabIndex, x, tabPane.getBounds().x)) {
2919                if (isSelected) {
2920                    g.setColor(selectHighlight);
2921                    //left
2922
g.fillRect(0, 0, 1, bottom);
2923                    //top
2924
g.fillRect(0, 0, right - 1, 1);
2925                    //right
2926
g.fillRect(right - 1, 0, 1, bottom);
2927                    g.setColor(shadowColor);
2928                    //topright corner
2929
g.fillRect(right - 1, 0, 1, 1);
2930                    //right
2931
g.fillRect(right, 1, 1, bottom);
2932                }
2933            } else {
2934                if (isSelected) {
2935                    g.setColor(selectHighlight);
2936                    //left
2937
g.fillRect(1, 1, 1, bottom - 1);
2938                    //top
2939
g.fillRect(2, 0, right - 3, 1);
2940                    //right
2941
g.fillRect(right - 1, 1, 1, bottom - 1);
2942                    g.setColor(shadowColor);
2943                    //left
2944
g.fillRect(0, 1, 1, bottom - 1);
2945                    //topleft corner
2946
g.fillRect(1, 0, 1, 1);
2947                    //topright corner
2948
g.fillRect(right - 1, 0, 1, 1);
2949                    //right
2950
g.fillRect(right, 1, 1, bottom);
2951                } else {
2952                    g.setColor(shadowColor);
2953                    g.fillRect(0, 0, 1, bottom +2 - bottom / 2);
2954                }
2955            }
2956            g.translate(-x, -y);
2957        }
2958
2959        protected void paintContentBorderTopEdge(
2960            Graphics JavaDoc g,
2961            int x,
2962            int y,
2963            int w,
2964            int h,
2965            boolean drawBroken,
2966            Rectangle JavaDoc selRect,
2967            boolean isContentBorderPainted) {
2968            g.setColor(shadowColor);
2969            g.fillRect(x, y, w, 1);
2970        }
2971        
2972    }
2973
2974    /**
2975     * the renderer for tabs on top with normal decoration
2976     */

2977    private static class TopRenderer extends AbstractRenderer {
2978
2979        private TopRenderer(JTabbedPane JavaDoc tabPane) {
2980            super(tabPane);
2981        }
2982
2983        protected Insets JavaDoc getTabAreaInsets(Insets JavaDoc defaultInsets) {
2984            return new Insets JavaDoc(defaultInsets.top, defaultInsets.left + 4, defaultInsets.bottom, defaultInsets.right);
2985        }
2986
2987        protected int getTabLabelShiftY(int tabIndex, boolean isSelected) {
2988            return isSelected? -1 : 0;
2989        }
2990
2991         protected int getTabRunOverlay(int tabRunOverlay) {
2992            return tabRunOverlay - 2;
2993        }
2994
2995        protected int getTabRunIndent(int run) {
2996            return 6 * run;
2997        }
2998
2999        protected Insets JavaDoc getSelectedTabPadInsets() {
3000            return NORTH_INSETS;
3001        }
3002
3003        protected Insets JavaDoc getTabInsets(int tabIndex, Insets JavaDoc tabInsets) {
3004            return new Insets JavaDoc(tabInsets.top-1, tabInsets.left - 4, tabInsets.bottom, tabInsets.right - 4);
3005        }
3006
3007        protected void paintFocusIndicator(
3008            Graphics JavaDoc g,
3009            Rectangle JavaDoc[] rects,
3010            int tabIndex,
3011            Rectangle JavaDoc iconRect,
3012            Rectangle JavaDoc textRect,
3013            boolean isSelected) {
3014
3015            if (!tabPane.hasFocus() || !isSelected)
3016                return;
3017            Rectangle JavaDoc tabRect = rects[tabIndex];
3018            int top = tabRect.y +1 ;
3019            int left = tabRect.x + 4;
3020            int height = tabRect.height - 3;
3021            int width = tabRect.width - 9;
3022            g.setColor(focus);
3023            g.drawRect(left, top, width, height);
3024        }
3025
3026        protected void paintTabBackground(Graphics JavaDoc g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
3027
3028            int sel = (isSelected) ? 0 : 1;
3029            g.setColor(selectColor);
3030            g.fillRect(x, y + sel, w, h / 2);
3031            g.fillRect(x - 1, y + sel + h / 2, w + 2, h - h / 2);
3032        }
3033
3034        protected void paintTabBorder(Graphics JavaDoc g, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
3035
3036            g.translate(x - 4, y);
3037
3038            int top = 0;
3039            int right = w + 6;
3040
3041            // Paint Border
3042
g.setColor(selectHighlight);
3043
3044            // Paint left
3045
g.drawLine(1, h - 1, 4, top + 4);
3046            g.fillRect(5, top + 2, 1, 2);
3047            g.fillRect(6, top + 1, 1, 1);
3048
3049            // Paint top
3050
g.fillRect(7, top, right - 12, 1);
3051
3052            // Paint right
3053
g.setColor(darkShadow);
3054            g.drawLine(right, h - 1, right - 3, top + 4);
3055            g.fillRect(right - 4, top + 2, 1, 2);
3056            g.fillRect(right - 5, top + 1, 1, 1);
3057
3058            g.translate(-x + 4, -y);
3059        }
3060
3061        protected void paintContentBorderTopEdge(
3062            Graphics JavaDoc g,
3063            int x,
3064            int y,
3065            int w,
3066            int h,
3067            boolean drawBroken,
3068            Rectangle JavaDoc selRect,
3069            boolean isContentBorderPainted) {
3070            int right = x + w - 1;
3071            int top = y;
3072            g.setColor(selectHighlight);
3073
3074            if (drawBroken && selRect.x >= x && selRect.x <= x + w) {
3075                // Break line to show visual connection to selected tab
3076
g.fillRect(x, top, selRect.x - 2 - x, 1);
3077                if (selRect.x + selRect.width < x + w - 2) {
3078                    g.fillRect(selRect.x + selRect.width + 2, top, right - 2 - selRect.x - selRect.width, 1);
3079                } else {
3080                    g.fillRect(x + w - 2, top, 1, 1);
3081                }
3082            } else {
3083                g.fillRect(x, top, w - 1, 1);
3084            }
3085        }
3086        
3087        protected int getTabsOverlay() {
3088            return 6;
3089        }
3090    }
3091
3092}
Popular Tags