KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > plaf > basic > BasicTabbedPaneUI


1 /*
2  * @(#)BasicTabbedPaneUI.java 1.153 05/03/03
3  *
4  * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package javax.swing.plaf.basic;
9
10 import com.sun.java.swing.SwingUtilities2;
11
12 import javax.swing.*;
13 import javax.swing.event.*;
14 import javax.swing.plaf.*;
15 import javax.swing.text.View JavaDoc;
16
17 import java.awt.*;
18 import java.awt.event.*;
19 import java.beans.PropertyChangeListener JavaDoc;
20 import java.beans.PropertyChangeEvent JavaDoc;
21 import java.util.Vector JavaDoc;
22 import java.util.Hashtable JavaDoc;
23
24 import sun.swing.DefaultLookup;
25 import sun.swing.UIAction;
26
27 /**
28  * A Basic L&F implementation of TabbedPaneUI.
29  *
30  * @version 1.87 06/08/99
31  * @author Amy Fowler
32  * @author Philip Milne
33  * @author Steve Wilson
34  * @author Tom Santos
35  * @author Dave Moore
36  */

37 public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants {
38
39
40 // Instance variables initialized at installation
41

42     protected JTabbedPane tabPane;
43
44     protected Color highlight;
45     protected Color lightHighlight;
46     protected Color shadow;
47     protected Color darkShadow;
48     protected Color focus;
49     private Color selectedColor;
50
51     protected int textIconGap;
52
53     protected int tabRunOverlay;
54
55     protected Insets tabInsets;
56     protected Insets selectedTabPadInsets;
57     protected Insets tabAreaInsets;
58     protected Insets contentBorderInsets;
59     private boolean tabsOverlapBorder;
60     private boolean tabsOpaque = true;
61     private boolean contentOpaque = true;
62
63     /**
64      * As of Java 2 platform v1.3 this previously undocumented field is no
65      * longer used.
66      * Key bindings are now defined by the LookAndFeel, please refer to
67      * the key bindings specification for further details.
68      *
69      * @deprecated As of Java 2 platform v1.3.
70      */

71     @Deprecated JavaDoc
72     protected KeyStroke upKey;
73     /**
74      * As of Java 2 platform v1.3 this previously undocumented field is no
75      * longer used.
76      * Key bindings are now defined by the LookAndFeel, please refer to
77      * the key bindings specification for further details.
78      *
79      * @deprecated As of Java 2 platform v1.3.
80      */

81     @Deprecated JavaDoc
82     protected KeyStroke downKey;
83     /**
84      * As of Java 2 platform v1.3 this previously undocumented field is no
85      * longer used.
86      * Key bindings are now defined by the LookAndFeel, please refer to
87      * the key bindings specification for further details.
88      *
89      * @deprecated As of Java 2 platform v1.3.
90      */

91     @Deprecated JavaDoc
92     protected KeyStroke leftKey;
93     /**
94      * As of Java 2 platform v1.3 this previously undocumented field is no
95      * longer used.
96      * Key bindings are now defined by the LookAndFeel, please refer to
97      * the key bindings specification for further details.
98      *
99      * @deprecated As of Java 2 platform v1.3.
100      */

101     @Deprecated JavaDoc
102     protected KeyStroke rightKey;
103
104
105 // Transient variables (recalculated each time TabbedPane is layed out)
106

107     protected int tabRuns[] = new int[10];
108     protected int runCount = 0;
109     protected int selectedRun = -1;
110     protected Rectangle rects[] = new Rectangle[0];
111     protected int maxTabHeight;
112     protected int maxTabWidth;
113
114 // Listeners
115

116     protected ChangeListener JavaDoc tabChangeListener;
117     protected PropertyChangeListener JavaDoc propertyChangeListener;
118     protected MouseListener mouseListener;
119     protected FocusListener focusListener;
120
121 // Private instance data
122

123     private Insets currentPadInsets = new Insets(0,0,0,0);
124     private Insets currentTabAreaInsets = new Insets(0,0,0,0);
125
126     private Component visibleComponent;
127     // PENDING(api): See comment for ContainerHandler
128
private Vector JavaDoc htmlViews;
129
130     private Hashtable JavaDoc mnemonicToIndexMap;
131
132     /**
133      * InputMap used for mnemonics. Only non-null if the JTabbedPane has
134      * mnemonics associated with it. Lazily created in initMnemonics.
135      */

136     private InputMap mnemonicInputMap;
137
138     // For use when tabLayoutPolicy = SCROLL_TAB_LAYOUT
139
private ScrollableTabSupport tabScroller;
140
141     /**
142      * A rectangle used for general layout calculations in order
143      * to avoid constructing many new Rectangles on the fly.
144      */

145     protected transient Rectangle calcRect = new Rectangle(0,0,0,0);
146
147     /**
148      * Tab that has focus.
149      */

150     private int focusIndex;
151
152     /**
153      * Combined listeners.
154      */

155     private Handler handler;
156
157     /**
158      * Index of the tab the mouse is over.
159      */

160     private int rolloverTabIndex;
161
162     /**
163      * This is set to true when a component is added/removed from the tab
164      * pane and set to false when layout happens. If true it indicates that
165      * tabRuns is not valid and shouldn't be used.
166      */

167     private boolean isRunsDirty;
168
169 // UI creation
170

171     public static ComponentUI createUI(JComponent c) {
172         return new BasicTabbedPaneUI JavaDoc();
173     }
174
175     static void loadActionMap(LazyActionMap JavaDoc map) {
176         map.put(new Actions(Actions.NEXT));
177         map.put(new Actions(Actions.PREVIOUS));
178         map.put(new Actions(Actions.RIGHT));
179         map.put(new Actions(Actions.LEFT));
180         map.put(new Actions(Actions.UP));
181         map.put(new Actions(Actions.DOWN));
182         map.put(new Actions(Actions.PAGE_UP));
183         map.put(new Actions(Actions.PAGE_DOWN));
184         map.put(new Actions(Actions.REQUEST_FOCUS));
185         map.put(new Actions(Actions.REQUEST_FOCUS_FOR_VISIBLE));
186         map.put(new Actions(Actions.SET_SELECTED));
187         map.put(new Actions(Actions.SELECT_FOCUSED));
188         map.put(new Actions(Actions.SCROLL_FORWARD));
189         map.put(new Actions(Actions.SCROLL_BACKWARD));
190     }
191
192 // UI Installation/De-installation
193

194     public void installUI(JComponent c) {
195         this.tabPane = (JTabbedPane)c;
196
197         rolloverTabIndex = -1;
198         focusIndex = -1;
199         c.setLayout(createLayoutManager());
200         installComponents();
201         installDefaults();
202         installListeners();
203         installKeyboardActions();
204     }
205
206     public void uninstallUI(JComponent c) {
207         uninstallKeyboardActions();
208         uninstallListeners();
209         uninstallDefaults();
210         uninstallComponents();
211         c.setLayout(null);
212  
213         this.tabPane = null;
214     }
215
216     /**
217      * Invoked by <code>installUI</code> to create
218      * a layout manager object to manage
219      * the <code>JTabbedPane</code>.
220      *
221      * @return a layout manager object
222      *
223      * @see TabbedPaneLayout
224      * @see javax.swing.JTabbedPane#getTabLayoutPolicy
225      */

226     protected LayoutManager createLayoutManager() {
227         if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
228             return new TabbedPaneScrollLayout();
229         } else { /* WRAP_TAB_LAYOUT */
230             return new TabbedPaneLayout();
231         }
232     }
233
234     /* In an attempt to preserve backward compatibility for programs
235      * which have extended BasicTabbedPaneUI to do their own layout, the
236      * UI uses the installed layoutManager (and not tabLayoutPolicy) to
237      * determine if scrollTabLayout is enabled.
238      */

239     private boolean scrollableTabLayoutEnabled() {
240         return (tabPane.getLayout() instanceof TabbedPaneScrollLayout);
241     }
242
243     /**
244      * Creates and installs any required subcomponents for the JTabbedPane.
245      * Invoked by installUI.
246      *
247      * @since 1.4
248      */

249     protected void installComponents() {
250         if (scrollableTabLayoutEnabled()) {
251             if (tabScroller == null) {
252                 tabScroller = new ScrollableTabSupport(tabPane.getTabPlacement());
253                 tabPane.add(tabScroller.viewport);
254             }
255         }
256     }
257
258     /**
259      * Creates and returns a JButton that will provide the user
260      * with a way to scroll the tabs in a particular direction. The
261      * returned JButton must be instance of UIResource.
262      *
263      * @param direction One of the SwingConstants constants:
264      * SOUTH, NORTH, EAST or WEST
265      * @return Widget for user to
266      * @see javax.swing.JTabbedPane#setTabPlacement
267      * @see javax.swing.SwingConstants
268      * @throws IllegalArgumentException if direction is not one of
269      * NORTH, SOUTH, EAST or WEST
270      * @since 1.5
271      */

272     protected JButton createScrollButton(int direction) {
273         if (direction != SOUTH && direction != NORTH && direction != EAST &&
274                                   direction != WEST) {
275             throw new IllegalArgumentException JavaDoc("Direction must be one of: " +
276                                                "SOUTH, NORTH, EAST or WEST");
277         }
278         return new ScrollableTabButton(direction);
279     }
280
281     /**
282      * Removes any installed subcomponents from the JTabbedPane.
283      * Invoked by uninstallUI.
284      *
285      * @since 1.4
286      */

287     protected void uninstallComponents() {
288         if (scrollableTabLayoutEnabled()) {
289             tabPane.remove(tabScroller.viewport);
290             tabPane.remove(tabScroller.scrollForwardButton);
291             tabPane.remove(tabScroller.scrollBackwardButton);
292             tabScroller = null;
293         }
294     }
295
296     protected void installDefaults() {
297         LookAndFeel.installColorsAndFont(tabPane, "TabbedPane.background",
298                                     "TabbedPane.foreground", "TabbedPane.font");
299         highlight = UIManager.getColor("TabbedPane.light");
300         lightHighlight = UIManager.getColor("TabbedPane.highlight");
301         shadow = UIManager.getColor("TabbedPane.shadow");
302         darkShadow = UIManager.getColor("TabbedPane.darkShadow");
303         focus = UIManager.getColor("TabbedPane.focus");
304         selectedColor = UIManager.getColor("TabbedPane.selected");
305
306         textIconGap = UIManager.getInt("TabbedPane.textIconGap");
307         tabInsets = UIManager.getInsets("TabbedPane.tabInsets");
308         selectedTabPadInsets = UIManager.getInsets("TabbedPane.selectedTabPadInsets");
309         tabAreaInsets = UIManager.getInsets("TabbedPane.tabAreaInsets");
310     tabsOverlapBorder = UIManager.getBoolean("TabbedPane.tabsOverlapBorder");
311         contentBorderInsets = UIManager.getInsets("TabbedPane.contentBorderInsets");
312         tabRunOverlay = UIManager.getInt("TabbedPane.tabRunOverlay");
313         tabsOpaque = UIManager.getBoolean("TabbedPane.tabsOpaque");
314         contentOpaque = UIManager.getBoolean("TabbedPane.contentOpaque");
315         Object JavaDoc opaque = UIManager.get("TabbedPane.opaque");
316         if (opaque == null) {
317             opaque = Boolean.FALSE;
318         }
319         LookAndFeel.installProperty(tabPane, "opaque", opaque);
320     }
321
322     protected void uninstallDefaults() {
323         highlight = null;
324         lightHighlight = null;
325         shadow = null;
326         darkShadow = null;
327         focus = null;
328         tabInsets = null;
329         selectedTabPadInsets = null;
330         tabAreaInsets = null;
331         contentBorderInsets = null;
332     }
333
334     protected void installListeners() {
335         if ((propertyChangeListener = createPropertyChangeListener()) != null) {
336             tabPane.addPropertyChangeListener(propertyChangeListener);
337         }
338         if ((tabChangeListener = createChangeListener()) != null) {
339             tabPane.addChangeListener(tabChangeListener);
340         }
341         if ((mouseListener = createMouseListener()) != null) {
342             tabPane.addMouseListener(mouseListener);
343         }
344         tabPane.addMouseMotionListener(getHandler());
345         if ((focusListener = createFocusListener()) != null) {
346             tabPane.addFocusListener(focusListener);
347         }
348         tabPane.addContainerListener(getHandler());
349         if (tabPane.getTabCount()>0) {
350             htmlViews = createHTMLVector();
351         }
352     }
353
354     protected void uninstallListeners() {
355         if (mouseListener != null) {
356             tabPane.removeMouseListener(mouseListener);
357             mouseListener = null;
358         }
359         tabPane.removeMouseMotionListener(getHandler());
360         if (focusListener != null) {
361             tabPane.removeFocusListener(focusListener);
362             focusListener = null;
363         }
364             
365         tabPane.removeContainerListener(getHandler());
366         if (htmlViews!=null) {
367             htmlViews.removeAllElements();
368             htmlViews = null;
369         }
370         if (tabChangeListener != null) {
371             tabPane.removeChangeListener(tabChangeListener);
372             tabChangeListener = null;
373         }
374         if (propertyChangeListener != null) {
375             tabPane.removePropertyChangeListener(propertyChangeListener);
376             propertyChangeListener = null;
377         }
378         handler = null;
379     }
380
381     protected MouseListener createMouseListener() {
382         return getHandler();
383     }
384
385     protected FocusListener createFocusListener() {
386         return getHandler();
387     }
388
389     protected ChangeListener JavaDoc createChangeListener() {
390         return getHandler();
391     }
392
393     protected PropertyChangeListener JavaDoc createPropertyChangeListener() {
394         return getHandler();
395     }
396
397     private Handler getHandler() {
398         if (handler == null) {
399             handler = new Handler();
400         }
401         return handler;
402     }
403
404     protected void installKeyboardActions() {
405         InputMap km = getInputMap(JComponent.
406                                   WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
407
408         SwingUtilities.replaceUIInputMap(tabPane, JComponent.
409                                          WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
410                                          km);
411         km = getInputMap(JComponent.WHEN_FOCUSED);
412         SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, km);
413
414         LazyActionMap.installLazyActionMap(tabPane, BasicTabbedPaneUI JavaDoc.class,
415                                            "TabbedPane.actionMap");
416     }
417
418     InputMap getInputMap(int condition) {
419         if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
420             return (InputMap)DefaultLookup.get(tabPane, this,
421                                                "TabbedPane.ancestorInputMap");
422         }
423         else if (condition == JComponent.WHEN_FOCUSED) {
424             return (InputMap)DefaultLookup.get(tabPane, this,
425                                                "TabbedPane.focusInputMap");
426         }
427         return null;
428     }
429
430     protected void uninstallKeyboardActions() {
431         SwingUtilities.replaceUIActionMap(tabPane, null);
432         SwingUtilities.replaceUIInputMap(tabPane, JComponent.
433                                          WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
434                                          null);
435         SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED,
436                                          null);
437     }
438
439     /**
440      * Reloads the mnemonics. This should be invoked when a memonic changes,
441      * when the title of a mnemonic changes, or when tabs are added/removed.
442      */

443     private void updateMnemonics() {
444         resetMnemonics();
445         for (int counter = tabPane.getTabCount() - 1; counter >= 0;
446              counter--) {
447             int mnemonic = tabPane.getMnemonicAt(counter);
448
449             if (mnemonic > 0) {
450                 addMnemonic(counter, mnemonic);
451             }
452         }
453     }
454
455     /**
456      * Resets the mnemonics bindings to an empty state.
457      */

458     private void resetMnemonics() {
459         if (mnemonicToIndexMap != null) {
460             mnemonicToIndexMap.clear();
461             mnemonicInputMap.clear();
462         }
463     }
464
465     /**
466      * Adds the specified mnemonic at the specified index.
467      */

468     private void addMnemonic(int index, int mnemonic) {
469         if (mnemonicToIndexMap == null) {
470             initMnemonics();
471         }
472         mnemonicInputMap.put(KeyStroke.getKeyStroke(mnemonic, Event.ALT_MASK),
473                              "setSelectedIndex");
474         mnemonicToIndexMap.put(new Integer JavaDoc(mnemonic), new Integer JavaDoc(index));
475     }
476
477     /**
478      * Installs the state needed for mnemonics.
479      */

480     private void initMnemonics() {
481         mnemonicToIndexMap = new Hashtable JavaDoc();
482         mnemonicInputMap = new ComponentInputMapUIResource(tabPane);
483         mnemonicInputMap.setParent(SwingUtilities.getUIInputMap(tabPane,
484                               JComponent.WHEN_IN_FOCUSED_WINDOW));
485         SwingUtilities.replaceUIInputMap(tabPane,
486                               JComponent.WHEN_IN_FOCUSED_WINDOW,
487                                          mnemonicInputMap);
488     }
489
490     /**
491      * Sets the tab the mouse is over by location. This is a cover method
492      * for <code>setRolloverTab(tabForCoordinate(x, y, false))</code>.
493      */

494     private void setRolloverTab(int x, int y) {
495         // NOTE:
496
// This calls in with false otherwise it could trigger a validate,
497
// which should NOT happen if the user is only dragging the
498
// mouse around.
499
setRolloverTab(tabForCoordinate(tabPane, x, y, false));
500     }
501
502     /**
503      * Sets the tab the mouse is currently over to <code>index</code>.
504      * <code>index</code> will be -1 if the mouse is no longer over any
505      * tab. No checking is done to ensure the passed in index identifies a
506      * valid tab.
507      *
508      * @param index Index of the tab the mouse is over.
509      * @since 1.5
510      */

511     protected void setRolloverTab(int index) {
512         rolloverTabIndex = index;
513     }
514
515     /**
516      * Returns the tab the mouse is currently over, or -1 if the mouse is no
517      * longer over any tab.
518      *
519      * @param index Index of the tab the mouse is over.
520      * @since 1.5
521      */

522     protected int getRolloverTab() {
523         return rolloverTabIndex;
524     }
525
526     public Dimension getMinimumSize(JComponent c) {
527         // Default to LayoutManager's minimumLayoutSize
528
return null;
529     }
530     
531     public Dimension getMaximumSize(JComponent c) {
532         // Default to LayoutManager's maximumLayoutSize
533
return null;
534     }
535
536 // UI Rendering
537

538     public void paint(Graphics g, JComponent c) {
539         int selectedIndex = tabPane.getSelectedIndex();
540         int tabPlacement = tabPane.getTabPlacement();
541
542         ensureCurrentLayout();
543
544         // Paint content border and tab area
545
if (tabsOverlapBorder) {
546         paintContentBorder(g, tabPlacement, selectedIndex);
547     }
548         // If scrollable tabs are enabled, the tab area will be
549
// painted by the scrollable tab panel instead.
550
//
551
if (!scrollableTabLayoutEnabled()) { // WRAP_TAB_LAYOUT
552
paintTabArea(g, tabPlacement, selectedIndex);
553         }
554     if (!tabsOverlapBorder) {
555         paintContentBorder(g, tabPlacement, selectedIndex);
556     }
557     }
558
559     /**
560      * Paints the tabs in the tab area.
561      * Invoked by paint().
562      * The graphics parameter must be a valid <code>Graphics</code>
563      * object. Tab placement may be either:
564      * <code>JTabbedPane.TOP</code>, <code>JTabbedPane.BOTTOM</code>,
565      * <code>JTabbedPane.LEFT</code>, or <code>JTabbedPane.RIGHT</code>.
566      * The selected index must be a valid tabbed pane tab index (0 to
567      * tab count - 1, inclusive) or -1 if no tab is currently selected.
568      * The handling of invalid parameters is unspecified.
569      *
570      * @param g the graphics object to use for rendering
571      * @param tabPlacement the placement for the tabs within the JTabbedPane
572      * @param selectedIndex the tab index of the selected component
573      *
574      * @since 1.4
575      */

576     protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex) {
577         int tabCount = tabPane.getTabCount();
578
579         Rectangle iconRect = new Rectangle(),
580                   textRect = new Rectangle();
581         Rectangle clipRect = g.getClipBounds();
582
583         // Paint tabRuns of tabs from back to front
584
for (int i = runCount - 1; i >= 0; i--) {
585             int start = tabRuns[i];
586             int next = tabRuns[(i == runCount - 1)? 0 : i + 1];
587             int end = (next != 0? next - 1: tabCount - 1);
588             for (int j = start; j <= end; j++) {
589                 if (j != selectedIndex && rects[j].intersects(clipRect)) {
590                     paintTab(g, tabPlacement, rects, j, iconRect, textRect);
591                 }
592             }
593         }
594
595         // Paint selected tab if its in the front run
596
// since it may overlap other tabs
597
if (selectedIndex >= 0 && rects[selectedIndex].intersects(clipRect)) {
598             paintTab(g, tabPlacement, rects, selectedIndex, iconRect, textRect);
599         }
600     }
601
602     protected void paintTab(Graphics g, int tabPlacement,
603                             Rectangle[] rects, int tabIndex,
604                             Rectangle iconRect, Rectangle textRect) {
605         Rectangle tabRect = rects[tabIndex];
606         int selectedIndex = tabPane.getSelectedIndex();
607         boolean isSelected = selectedIndex == tabIndex;
608         Graphics2D g2 = null;
609         Polygon cropShape = null;
610         Shape save = null;
611         int cropx = 0;
612         int cropy = 0;
613
614         if (scrollableTabLayoutEnabled()) {
615             if (g instanceof Graphics2D) {
616                 g2 = (Graphics2D)g;
617
618                 // Render visual for cropped tab edge...
619
Rectangle viewRect = tabScroller.viewport.getViewRect();
620                 int cropline;
621                 switch(tabPlacement) {
622                   case LEFT:
623                   case RIGHT:
624                     cropline = viewRect.y + viewRect.height;
625                     if ((tabRect.y < cropline) && (tabRect.y + tabRect.height > cropline)) {
626                         cropShape = createCroppedTabClip(tabPlacement, tabRect, cropline);
627                         cropx = tabRect.x;
628                         cropy = cropline-1;
629                     }
630                     break;
631                   case TOP:
632                   case BOTTOM:
633                   default:
634                     cropline = viewRect.x + viewRect.width;
635                     if ((tabRect.x < cropline) && (tabRect.x + tabRect.width > cropline)) {
636                         cropShape = createCroppedTabClip(tabPlacement, tabRect, cropline);
637                         cropx = cropline-1;
638                         cropy = tabRect.y;
639                     }
640                 }
641                 if (cropShape != null) {
642                     save = g2.getClip();
643                     g2.clip(cropShape);
644                 }
645             }
646         }
647
648         if (tabsOpaque || tabPane.isOpaque()) {
649             paintTabBackground(g, tabPlacement, tabIndex, tabRect.x, tabRect.y,
650                     tabRect.width, tabRect.height, isSelected);
651         }
652
653         paintTabBorder(g, tabPlacement, tabIndex, tabRect.x, tabRect.y,
654                        tabRect.width, tabRect.height, isSelected);
655         
656         String JavaDoc title = tabPane.getTitleAt(tabIndex);
657         Font font = tabPane.getFont();
658         FontMetrics metrics = SwingUtilities2.getFontMetrics(tabPane, g, font);
659         Icon icon = getIconForTab(tabIndex);
660
661         layoutLabel(tabPlacement, metrics, tabIndex, title, icon,
662                     tabRect, iconRect, textRect, isSelected);
663
664         paintText(g, tabPlacement, font, metrics,
665                   tabIndex, title, textRect, isSelected);
666
667         paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected);
668
669         paintFocusIndicator(g, tabPlacement, rects, tabIndex,
670                   iconRect, textRect, isSelected);
671
672         if (cropShape != null) {
673             paintCroppedTabEdge(g, tabPlacement, tabIndex, isSelected, cropx, cropy);
674             g2.setClip(save);
675         }
676     }
677
678
679     /* This method will create and return a polygon shape for the given tab rectangle
680      * which has been cropped at the specified cropline with a torn edge visual.
681      * e.g. A "File" tab which has cropped been cropped just after the "i":
682      * -------------
683      * | ..... |
684      * | . |
685      * | ... . |
686      * | . . |
687      * | . . |
688      * | . . |
689      * --------------
690      *
691      * The x, y arrays below define the pattern used to create a "torn" edge
692      * segment which is repeated to fill the edge of the tab.
693      * For tabs placed on TOP and BOTTOM, this righthand torn edge is created by
694      * line segments which are defined by coordinates obtained by
695      * subtracting xCropLen[i] from (tab.x + tab.width) and adding yCroplen[i]
696      * to (tab.y).
697      * For tabs placed on LEFT or RIGHT, the bottom torn edge is created by
698      * subtracting xCropLen[i] from (tab.y + tab.height) and adding yCropLen[i]
699      * to (tab.x).
700      */

701     private int xCropLen[] = {1,1,0,0,1,1,2,2};
702     private int yCropLen[] = {0,3,3,6,6,9,9,12};
703     private static final int CROP_SEGMENT = 12;
704
705     private Polygon createCroppedTabClip(int tabPlacement, Rectangle tabRect, int cropline) {
706         int rlen = 0;
707         int start = 0;
708         int end = 0;
709         int ostart = 0;
710
711         switch(tabPlacement) {
712           case LEFT:
713           case RIGHT:
714               rlen = tabRect.width;
715               start = tabRect.x;
716               end = tabRect.x + tabRect.width;
717               ostart = tabRect.y;
718               break;
719           case TOP:
720           case BOTTOM:
721           default:
722              rlen = tabRect.height;
723              start = tabRect.y;
724              end = tabRect.y + tabRect.height;
725              ostart = tabRect.x;
726         }
727         int rcnt = rlen/CROP_SEGMENT;
728         if (rlen%CROP_SEGMENT > 0) {
729             rcnt++;
730         }
731         int npts = 2 + (rcnt*8);
732         int xp[] = new int[npts];
733         int yp[] = new int[npts];
734         int pcnt = 0;
735  
736         xp[pcnt] = ostart;
737         yp[pcnt++] = end;
738         xp[pcnt] = ostart;
739         yp[pcnt++] = start;
740         for(int i = 0; i < rcnt; i++) {
741             for(int j = 0; j < xCropLen.length; j++) {
742                 xp[pcnt] = cropline - xCropLen[j];
743                 yp[pcnt] = start + (i*CROP_SEGMENT) + yCropLen[j];
744                 if (yp[pcnt] >= end) {
745                     yp[pcnt] = end;
746                     pcnt++;
747                     break;
748                 }
749                 pcnt++;
750             }
751         }
752         if (tabPlacement == JTabbedPane.TOP || tabPlacement == JTabbedPane.BOTTOM) {
753            return new Polygon(xp, yp, pcnt);
754
755         } else { // LEFT or RIGHT
756
return new Polygon(yp, xp, pcnt);
757         }
758     }
759
760     /* If tabLayoutPolicy == SCROLL_TAB_LAYOUT, this method will paint an edge
761      * indicating the tab is cropped in the viewport display
762      */

763     private void paintCroppedTabEdge(Graphics g, int tabPlacement, int tabIndex,
764                                      boolean isSelected,
765                                      int x, int y) {
766         switch(tabPlacement) {
767           case LEFT:
768           case RIGHT:
769             int xx = x;
770             g.setColor(shadow);
771             while(xx <= x+rects[tabIndex].width) {
772                 for (int i=0; i < xCropLen.length; i+=2) {
773                     g.drawLine(xx+yCropLen[i],y-xCropLen[i],
774                                xx+yCropLen[i+1]-1,y-xCropLen[i+1]);
775                 }
776                 xx+=CROP_SEGMENT;
777             }
778             break;
779           case TOP:
780           case BOTTOM:
781           default:
782             int yy = y;
783             g.setColor(shadow);
784             while(yy <= y+rects[tabIndex].height) {
785                 for (int i=0; i < xCropLen.length; i+=2) {
786                     g.drawLine(x-xCropLen[i],yy+yCropLen[i],
787                                x-xCropLen[i+1],yy+yCropLen[i+1]-1);
788                 }
789                 yy+=CROP_SEGMENT;
790             }
791         }
792     }
793
794     protected void layoutLabel(int tabPlacement,
795                                FontMetrics metrics, int tabIndex,
796                                String JavaDoc title, Icon icon,
797                                Rectangle tabRect, Rectangle iconRect,
798                                Rectangle textRect, boolean isSelected ) {
799         textRect.x = textRect.y = iconRect.x = iconRect.y = 0;
800
801         View JavaDoc v = getTextViewForTab(tabIndex);
802         if (v != null) {
803             tabPane.putClientProperty("html", v);
804         }
805
806         SwingUtilities.layoutCompoundLabel((JComponent) tabPane,
807                                            metrics, title, icon,
808                                            SwingUtilities.CENTER,
809                                            SwingUtilities.CENTER,
810                                            SwingUtilities.CENTER,
811                                            SwingUtilities.TRAILING,
812                                            tabRect,
813                                            iconRect,
814                                            textRect,
815                                            textIconGap);
816
817         tabPane.putClientProperty("html", null);
818
819         int xNudge = getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
820         int yNudge = getTabLabelShiftY(tabPlacement, tabIndex, isSelected);
821         iconRect.x += xNudge;
822         iconRect.y += yNudge;
823         textRect.x += xNudge;
824         textRect.y += yNudge;
825     }
826
827     protected void paintIcon(Graphics g, int tabPlacement,
828                              int tabIndex, Icon icon, Rectangle iconRect,
829                              boolean isSelected ) {
830         if (icon != null) {
831             icon.paintIcon(tabPane, g, iconRect.x, iconRect.y);
832         }
833     }
834
835     protected void paintText(Graphics g, int tabPlacement,
836                              Font font, FontMetrics metrics, int tabIndex,
837                              String JavaDoc title, Rectangle textRect,
838                              boolean isSelected) {
839
840         g.setFont(font);
841
842         View JavaDoc v = getTextViewForTab(tabIndex);
843         if (v != null) {
844             // html
845
v.paint(g, textRect);
846         } else {
847             // plain text
848
int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
849
850             if (tabPane.isEnabled() && tabPane.isEnabledAt(tabIndex)) {
851                 Color fg = tabPane.getForegroundAt(tabIndex);
852                 if (isSelected && (fg instanceof UIResource)) {
853                     Color selectedFG = UIManager.getColor(
854                                   "TabbedPane.selectedForeground");
855                     if (selectedFG != null) {
856                         fg = selectedFG;
857                     }
858                 }
859                 g.setColor(fg);
860                 SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
861                              title, mnemIndex,
862                              textRect.x, textRect.y + metrics.getAscent());
863                 
864             } else { // tab disabled
865
g.setColor(tabPane.getBackgroundAt(tabIndex).brighter());
866                 SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
867                              title, mnemIndex,
868                              textRect.x, textRect.y + metrics.getAscent());
869                 g.setColor(tabPane.getBackgroundAt(tabIndex).darker());
870                 SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
871                              title, mnemIndex,
872                              textRect.x - 1, textRect.y + metrics.getAscent() - 1);
873
874             }
875         }
876     }
877
878
879     protected int getTabLabelShiftX(int tabPlacement, int tabIndex, boolean isSelected) {
880         Rectangle tabRect = rects[tabIndex];
881         int nudge = 0;
882         switch(tabPlacement) {
883           case LEFT:
884               nudge = isSelected? -1 : 1;
885               break;
886           case RIGHT:
887               nudge = isSelected? 1 : -1;
888               break;
889           case BOTTOM:
890           case TOP:
891           default:
892               nudge = tabRect.width % 2;
893         }
894         return nudge;
895     }
896
897     protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) {
898         Rectangle tabRect = rects[tabIndex];
899         int nudge = 0;
900         switch(tabPlacement) {
901            case BOTTOM:
902               nudge = isSelected? 1 : -1;
903               break;
904           case LEFT:
905           case RIGHT:
906               nudge = tabRect.height % 2;
907               break;
908           case TOP:
909           default:
910               nudge = isSelected? -1 : 1;;
911         }
912         return nudge;
913     }
914
915     protected void paintFocusIndicator(Graphics g, int tabPlacement,
916                                        Rectangle[] rects, int tabIndex,
917                                        Rectangle iconRect, Rectangle textRect,
918                                        boolean isSelected) {
919         Rectangle tabRect = rects[tabIndex];
920         if (tabPane.hasFocus() && isSelected) {
921             int x, y, w, h;
922             g.setColor(focus);
923             switch(tabPlacement) {
924               case LEFT:
925                   x = tabRect.x + 3;
926                   y = tabRect.y + 3;
927                   w = tabRect.width - 5;
928                   h = tabRect.height - 6;
929                   break;
930               case RIGHT:
931                   x = tabRect.x + 2;
932                   y = tabRect.y + 3;
933                   w = tabRect.width - 5;
934                   h = tabRect.height - 6;
935                   break;
936               case BOTTOM:
937                   x = tabRect.x + 3;
938                   y = tabRect.y + 2;
939                   w = tabRect.width - 6;
940                   h = tabRect.height - 5;
941                   break;
942               case TOP:
943               default:
944                   x = tabRect.x + 3;
945                   y = tabRect.y + 3;
946                   w = tabRect.width - 6;
947                   h = tabRect.height - 5;
948             }
949             BasicGraphicsUtils.drawDashedRect(g, x, y, w, h);
950         }
951     }
952
953     /**
954       * this function draws the border around each tab
955       * note that this function does now draw the background of the tab.
956       * that is done elsewhere
957       */

958     protected void paintTabBorder(Graphics g, int tabPlacement,
959                                   int tabIndex,
960                                   int x, int y, int w, int h,
961                                   boolean isSelected ) {
962         g.setColor(lightHighlight);
963
964         switch (tabPlacement) {
965           case LEFT:
966               g.drawLine(x+1, y+h-2, x+1, y+h-2); // bottom-left highlight
967
g.drawLine(x, y+2, x, y+h-3); // left highlight
968
g.drawLine(x+1, y+1, x+1, y+1); // top-left highlight
969
g.drawLine(x+2, y, x+w-1, y); // top highlight
970

971               g.setColor(shadow);
972               g.drawLine(x+2, y+h-2, x+w-1, y+h-2); // bottom shadow
973

974               g.setColor(darkShadow);
975               g.drawLine(x+2, y+h-1, x+w-1, y+h-1); // bottom dark shadow
976
break;
977           case RIGHT:
978               g.drawLine(x, y, x+w-3, y); // top highlight
979

980               g.setColor(shadow);
981               g.drawLine(x, y+h-2, x+w-3, y+h-2); // bottom shadow
982
g.drawLine(x+w-2, y+2, x+w-2, y+h-3); // right shadow
983

984               g.setColor(darkShadow);
985               g.drawLine(x+w-2, y+1, x+w-2, y+1); // top-right dark shadow
986
g.drawLine(x+w-2, y+h-2, x+w-2, y+h-2); // bottom-right dark shadow
987
g.drawLine(x+w-1, y+2, x+w-1, y+h-3); // right dark shadow
988
g.drawLine(x, y+h-1, x+w-3, y+h-1); // bottom dark shadow
989
break;
990           case BOTTOM:
991               g.drawLine(x, y, x, y+h-3); // left highlight
992
g.drawLine(x+1, y+h-2, x+1, y+h-2); // bottom-left highlight
993

994               g.setColor(shadow);
995               g.drawLine(x+2, y+h-2, x+w-3, y+h-2); // bottom shadow
996
g.drawLine(x+w-2, y, x+w-2, y+h-3); // right shadow
997

998               g.setColor(darkShadow);
999               g.drawLine(x+2, y+h-1, x+w-3, y+h-1); // bottom dark shadow
1000
g.drawLine(x+w-2, y+h-2, x+w-2, y+h-2); // bottom-right dark shadow
1001
g.drawLine(x+w-1, y, x+w-1, y+h-3); // right dark shadow
1002
break;
1003          case TOP:
1004          default:
1005              g.drawLine(x, y+2, x, y+h-1); // left highlight
1006
g.drawLine(x+1, y+1, x+1, y+1); // top-left highlight
1007
g.drawLine(x+2, y, x+w-3, y); // top highlight
1008

1009              g.setColor(shadow);
1010              g.drawLine(x+w-2, y+2, x+w-2, y+h-1); // right shadow
1011

1012              g.setColor(darkShadow);
1013              g.drawLine(x+w-1, y+2, x+w-1, y+h-1); // right dark-shadow
1014
g.drawLine(x+w-2, y+1, x+w-2, y+1); // top-right shadow
1015
}
1016    }
1017
1018    protected void paintTabBackground(Graphics g, int tabPlacement,
1019                                      int tabIndex,
1020                                      int x, int y, int w, int h,
1021                                      boolean isSelected ) {
1022        g.setColor(!isSelected || selectedColor == null?
1023                   tabPane.getBackgroundAt(tabIndex) : selectedColor);
1024        switch(tabPlacement) {
1025          case LEFT:
1026              g.fillRect(x+1, y+1, w-1, h-3);
1027              break;
1028          case RIGHT:
1029              g.fillRect(x, y+1, w-2, h-3);
1030              break;
1031          case BOTTOM:
1032              g.fillRect(x+1, y, w-3, h-1);
1033              break;
1034          case TOP:
1035          default:
1036              g.fillRect(x+1, y+1, w-3, h-1);
1037        }
1038    }
1039
1040    protected void paintContentBorder(Graphics g, int tabPlacement, int selectedIndex) {
1041        int width = tabPane.getWidth();
1042        int height = tabPane.getHeight();
1043        Insets insets = tabPane.getInsets();
1044        Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
1045
1046        int x = insets.left;
1047        int y = insets.top;
1048        int w = width - insets.right - insets.left;
1049        int h = height - insets.top - insets.bottom;
1050
1051        switch(tabPlacement) {
1052          case LEFT:
1053              x += calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
1054          if (tabsOverlapBorder) {
1055          x -= tabAreaInsets.right;
1056          }
1057              w -= (x - insets.left);
1058              break;
1059          case RIGHT:
1060              w -= calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
1061          if (tabsOverlapBorder) {
1062          w += tabAreaInsets.left;
1063          }
1064              break;
1065          case BOTTOM:
1066              h -= calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
1067          if (tabsOverlapBorder) {
1068          h += tabAreaInsets.top;
1069          }
1070              break;
1071          case TOP:
1072          default:
1073              y += calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
1074          if (tabsOverlapBorder) {
1075          y -= tabAreaInsets.bottom;
1076          }
1077              h -= (y - insets.top);
1078        }
1079
1080        if (contentOpaque || tabPane.isOpaque()) {
1081            // Fill region behind content area
1082
Color color = UIManager.getColor("TabbedPane.contentAreaColor");
1083            if (color != null) {
1084                g.setColor(color);
1085            }
1086            else if (selectedColor == null) {
1087                g.setColor(tabPane.getBackground());
1088            }
1089            else {
1090                g.setColor(selectedColor);
1091            }
1092            g.fillRect(x,y,w,h);
1093        }
1094
1095        paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h);
1096        paintContentBorderLeftEdge(g, tabPlacement, selectedIndex, x, y, w, h);
1097        paintContentBorderBottomEdge(g, tabPlacement, selectedIndex, x, y, w, h);
1098        paintContentBorderRightEdge(g, tabPlacement, selectedIndex, x, y, w, h);
1099
1100    }
1101         
1102    protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
1103                                         int selectedIndex,
1104                                         int x, int y, int w, int h) {
1105        Rectangle selRect = selectedIndex < 0? null :
1106                               getTabBounds(selectedIndex, calcRect);
1107
1108        g.setColor(lightHighlight);
1109
1110        // Draw unbroken line if tabs are not on TOP, OR
1111
// selected tab is not in run adjacent to content, OR
1112
// selected tab is not visible (SCROLL_TAB_LAYOUT)
1113
//
1114
if (tabPlacement != TOP || selectedIndex < 0 ||
1115            (selRect.y + selRect.height + 1 < y) ||
1116            (selRect.x < x || selRect.x > x + w)) {
1117            g.drawLine(x, y, x+w-2, y);
1118        } else {
1119            // Break line to show visual connection to selected tab
1120
g.drawLine(x, y, selRect.x - 1, y);
1121            if (selRect.x + selRect.width < x + w - 2) {
1122                g.drawLine(selRect.x + selRect.width, y,
1123                           x+w-2, y);
1124            } else {
1125                g.setColor(shadow);
1126                g.drawLine(x+w-2, y, x+w-2, y);
1127            }
1128        }
1129    }
1130
1131    protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
1132                                               int selectedIndex,
1133                                               int x, int y, int w, int h) {
1134        Rectangle selRect = selectedIndex < 0? null :
1135                               getTabBounds(selectedIndex, calcRect);
1136
1137        g.setColor(lightHighlight);
1138
1139        // Draw unbroken line if tabs are not on LEFT, OR
1140
// selected tab is not in run adjacent to content, OR
1141
// selected tab is not visible (SCROLL_TAB_LAYOUT)
1142
//
1143
if (tabPlacement != LEFT || selectedIndex < 0 ||
1144            (selRect.x + selRect.width + 1 < x) ||
1145            (selRect.y < y || selRect.y > y + h)) {
1146            g.drawLine(x, y, x, y+h-2);
1147        } else {
1148            // Break line to show visual connection to selected tab
1149
g.drawLine(x, y, x, selRect.y - 1);
1150            if (selRect.y + selRect.height < y + h - 2) {
1151                g.drawLine(x, selRect.y + selRect.height,
1152                           x, y+h-2);
1153            }
1154        }
1155    }
1156
1157    protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
1158                                               int selectedIndex,
1159                                               int x, int y, int w, int h) {
1160        Rectangle selRect = selectedIndex < 0? null :
1161                               getTabBounds(selectedIndex, calcRect);
1162
1163        g.setColor(shadow);
1164
1165        // Draw unbroken line if tabs are not on BOTTOM, OR
1166
// selected tab is not in run adjacent to content, OR
1167
// selected tab is not visible (SCROLL_TAB_LAYOUT)
1168
//
1169
if (tabPlacement != BOTTOM || selectedIndex < 0 ||
1170             (selRect.y - 1 > h) ||
1171             (selRect.x < x || selRect.x > x + w)) {
1172            g.drawLine(x+1, y+h-2, x+w-2, y+h-2);
1173            g.setColor(darkShadow);
1174            g.drawLine(x, y+h-1, x+w-1, y+h-1);
1175        } else {
1176            // Break line to show visual connection to selected tab
1177
g.drawLine(x+1, y+h-2, selRect.x - 1, y+h-2);
1178            g.setColor(darkShadow);
1179            g.drawLine(x, y+h-1, selRect.x - 1, y+h-1);
1180            if (selRect.x + selRect.width < x + w - 2) {
1181                g.setColor(shadow);
1182                g.drawLine(selRect.x + selRect.width, y+h-2, x+w-2, y+h-2);
1183                g.setColor(darkShadow);
1184                g.drawLine(selRect.x + selRect.width, y+h-1, x+w-1, y+h-1);
1185            }
1186        }
1187
1188    }
1189
1190    protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
1191                                               int selectedIndex,
1192                                               int x, int y, int w, int h) {
1193        Rectangle selRect = selectedIndex < 0? null :
1194                               getTabBounds(selectedIndex, calcRect);
1195
1196        g.setColor(shadow);
1197
1198        // Draw unbroken line if tabs are not on RIGHT, OR
1199
// selected tab is not in run adjacent to content, OR
1200
// selected tab is not visible (SCROLL_TAB_LAYOUT)
1201
//
1202
if (tabPlacement != RIGHT || selectedIndex < 0 ||
1203             (selRect.x - 1 > w) ||
1204             (selRect.y < y || selRect.y > y + h)) {
1205            g.drawLine(x+w-2, y+1, x+w-2, y+h-3);
1206            g.setColor(darkShadow);
1207            g.drawLine(x+w-1, y, x+w-1, y+h-1);
1208        } else {
1209            // Break line to show visual connection to selected tab
1210
g.drawLine(x+w-2, y+1, x+w-2, selRect.y - 1);
1211            g.setColor(darkShadow);
1212            g.drawLine(x+w-1, y, x+w-1, selRect.y - 1);
1213
1214            if (selRect.y + selRect.height < y + h - 2) {
1215                g.setColor(shadow);
1216                g.drawLine(x+w-2, selRect.y + selRect.height,
1217                           x+w-2, y+h-2);
1218                g.setColor(darkShadow);
1219                g.drawLine(x+w-1, selRect.y + selRect.height,
1220                           x+w-1, y+h-2);
1221            }
1222        }
1223    }
1224
1225    private void ensureCurrentLayout() {
1226        if (!tabPane.isValid()) {
1227            tabPane.validate();
1228        }
1229        /* If tabPane doesn't have a peer yet, the validate() call will
1230         * silently fail. We handle that by forcing a layout if tabPane
1231         * is still invalid. See bug 4237677.
1232         */

1233        if (!tabPane.isValid()) {
1234            TabbedPaneLayout layout = (TabbedPaneLayout)tabPane.getLayout();
1235            layout.calculateLayoutInfo();
1236        }
1237    }
1238    
1239
1240// TabbedPaneUI methods
1241

1242    /**
1243     * Returns the bounds of the specified tab index. The bounds are
1244     * with respect to the JTabbedPane's coordinate space.
1245     */

1246    public Rectangle getTabBounds(JTabbedPane pane, int i) {
1247        ensureCurrentLayout();
1248        Rectangle tabRect = new Rectangle();
1249        return getTabBounds(i, tabRect);
1250    }
1251
1252    public int getTabRunCount(JTabbedPane pane) {
1253        ensureCurrentLayout();
1254        return runCount;
1255    }
1256
1257    /**
1258     * Returns the tab index which intersects the specified point
1259     * in the JTabbedPane's coordinate space.
1260     */

1261    public int tabForCoordinate(JTabbedPane pane, int x, int y) {
1262        return tabForCoordinate(pane, x, y, true);
1263    }
1264
1265    private int tabForCoordinate(JTabbedPane pane, int x, int y,
1266                                 boolean validateIfNecessary) {
1267        if (validateIfNecessary) {
1268            ensureCurrentLayout();
1269        }
1270        if (isRunsDirty) {
1271            // We didn't recalculate the layout, runs and tabCount may not
1272
// line up, bail.
1273
return -1;
1274        }
1275        Point p = new Point(x, y);
1276
1277        if (scrollableTabLayoutEnabled()) {
1278            translatePointToTabPanel(x, y, p);
1279            Rectangle viewRect = tabScroller.viewport.getViewRect();
1280            if (!viewRect.contains(p)) {
1281                return -1;
1282            }
1283        }
1284        int tabCount = tabPane.getTabCount();
1285        for (int i = 0; i < tabCount; i++) {
1286            if (rects[i].contains(p.x, p.y)) {
1287                return i;
1288            }
1289        }
1290        return -1;
1291    }
1292
1293    /**
1294     * Returns the bounds of the specified tab in the coordinate space
1295     * of the JTabbedPane component. This is required because the tab rects
1296     * are by default defined in the coordinate space of the component where
1297     * they are rendered, which could be the JTabbedPane
1298     * (for WRAP_TAB_LAYOUT) or a ScrollableTabPanel (SCROLL_TAB_LAYOUT).
1299     * This method should be used whenever the tab rectangle must be relative
1300     * to the JTabbedPane itself and the result should be placed in a
1301     * designated Rectangle object (rather than instantiating and returning
1302     * a new Rectangle each time). The tab index parameter must be a valid
1303     * tabbed pane tab index (0 to tab count - 1, inclusive). The destination
1304     * rectangle parameter must be a valid <code>Rectangle</code> instance.
1305     * The handling of invalid parameters is unspecified.
1306     *
1307     * @param tabIndex the index of the tab
1308     * @param dest the rectangle where the result should be placed
1309     * @return the resulting rectangle
1310     *
1311     * @since 1.4
1312     */

1313    protected Rectangle getTabBounds(int tabIndex, Rectangle dest) {
1314        dest.width = rects[tabIndex].width;
1315        dest.height = rects[tabIndex].height;
1316
1317        if (scrollableTabLayoutEnabled()) { // SCROLL_TAB_LAYOUT
1318
// Need to translate coordinates based on viewport location &
1319
// view position
1320
Point vpp = tabScroller.viewport.getLocation();
1321            Point viewp = tabScroller.viewport.getViewPosition();
1322            dest.x = rects[tabIndex].x + vpp.x - viewp.x;
1323            dest.y = rects[tabIndex].y + vpp.y - viewp.y;
1324
1325        } else { // WRAP_TAB_LAYOUT
1326
dest.x = rects[tabIndex].x;
1327            dest.y = rects[tabIndex].y;
1328        }
1329        return dest;
1330    }
1331
1332    /**
1333     * Returns the index of the tab closest to the passed in location, note
1334     * that the returned tab may not contain the location x,y.
1335     */

1336    private int getClosestTab(int x, int y) {
1337        int min = 0;
1338        int tabCount = Math.min(rects.length, tabPane.getTabCount());
1339        int max = tabCount;
1340        int tabPlacement = tabPane.getTabPlacement();
1341        boolean useX = (tabPlacement == TOP || tabPlacement == BOTTOM);
1342        int want = (useX) ? x : y;
1343
1344        while (min != max) {
1345            int current = (max + min) / 2;
1346            int minLoc;
1347            int maxLoc;
1348
1349            if (useX) {
1350                minLoc = rects[current].x;
1351                maxLoc = minLoc + rects[current].width;
1352            }
1353            else {
1354                minLoc = rects[current].y;
1355                maxLoc = minLoc + rects[current].height;
1356            }
1357            if (want < minLoc) {
1358                max = current;
1359                if (min == max) {
1360                    return Math.max(0, current - 1);
1361                }
1362            }
1363            else if (want >= maxLoc) {
1364                min = current;
1365                if (max - min <= 1) {
1366                    return Math.max(current + 1, tabCount - 1);
1367                }
1368            }
1369            else {
1370                return current;
1371            }
1372        }
1373        return min;
1374    }
1375
1376    /**
1377     * Returns a point which is translated from the specified point in the
1378     * JTabbedPane's coordinate space to the coordinate space of the
1379     * ScrollableTabPanel. This is used for SCROLL_TAB_LAYOUT ONLY.
1380     */

1381    private Point translatePointToTabPanel(int srcx, int srcy, Point dest) {
1382        Point vpp = tabScroller.viewport.getLocation();
1383        Point viewp = tabScroller.viewport.getViewPosition();
1384        dest.x = srcx - vpp.x + viewp.x;
1385        dest.y = srcy - vpp.y + viewp.y;
1386        return dest;
1387    }
1388
1389// BasicTabbedPaneUI methods
1390

1391    protected Component getVisibleComponent() {
1392        return visibleComponent;
1393    }
1394
1395    protected void setVisibleComponent(Component component) {
1396        if (visibleComponent != null && visibleComponent != component &&
1397                visibleComponent.getParent() == tabPane) {
1398            visibleComponent.setVisible(false);
1399        }
1400        if (component != null && !component.isVisible()) {
1401            component.setVisible(true);
1402        }
1403        visibleComponent = component;
1404    }
1405
1406    protected void assureRectsCreated(int tabCount) {
1407        int rectArrayLen = rects.length;
1408        if (tabCount != rectArrayLen ) {
1409            Rectangle[] tempRectArray = new Rectangle[tabCount];
1410            System.arraycopy(rects, 0, tempRectArray, 0,
1411                             Math.min(rectArrayLen, tabCount));
1412            rects = tempRectArray;
1413            for (int rectIndex = rectArrayLen; rectIndex < tabCount; rectIndex++) {
1414                rects[rectIndex] = new Rectangle();
1415            }
1416        }
1417
1418    }
1419
1420    protected void expandTabRunsArray() {
1421        int rectLen = tabRuns.length;
1422        int[] newArray = new int[rectLen+10];
1423        System.arraycopy(tabRuns, 0, newArray, 0, runCount);
1424        tabRuns = newArray;
1425    }
1426
1427    protected int getRunForTab(int tabCount, int tabIndex) {
1428        for (int i = 0; i < runCount; i++) {
1429            int first = tabRuns[i];
1430            int last = lastTabInRun(tabCount, i);
1431            if (tabIndex >= first && tabIndex <= last) {
1432                return i;
1433            }
1434        }
1435        return 0;
1436    }
1437
1438    protected int lastTabInRun(int tabCount, int run) {
1439        if (runCount == 1) {
1440            return tabCount - 1;
1441        }
1442        int nextRun = (run == runCount - 1? 0 : run + 1);
1443        if (tabRuns[nextRun] == 0) {
1444            return tabCount - 1;
1445        }
1446        return tabRuns[nextRun]-1;
1447    }
1448
1449    protected int getTabRunOverlay(int tabPlacement) {
1450        return tabRunOverlay;
1451    }
1452
1453    protected int getTabRunIndent(int tabPlacement, int run) {
1454        return 0;
1455    }
1456
1457    protected boolean shouldPadTabRun(int tabPlacement, int run) {
1458        return runCount > 1;
1459    }
1460
1461    protected boolean shouldRotateTabRuns(int tabPlacement) {
1462        return true;
1463    }
1464
1465    protected Icon getIconForTab(int tabIndex) {
1466        return (!tabPane.isEnabled() || !tabPane.isEnabledAt(tabIndex))?
1467                          tabPane.getDisabledIconAt(tabIndex) : tabPane.getIconAt(tabIndex);
1468    }
1469
1470    /**
1471     * Returns the text View object required to render stylized text (HTML) for
1472     * the specified tab or null if no specialized text rendering is needed
1473     * for this tab. This is provided to support html rendering inside tabs.
1474     *
1475     * @param tabIndex the index of the tab
1476     * @return the text view to render the tab's text or null if no
1477     * specialized rendering is required
1478     *
1479     * @since 1.4
1480     */

1481    protected View JavaDoc getTextViewForTab(int tabIndex) {
1482        if (htmlViews != null) {
1483            return (View JavaDoc)htmlViews.elementAt(tabIndex);
1484        }
1485        return null;
1486    }
1487
1488    protected int calculateTabHeight(int tabPlacement, int tabIndex, int fontHeight) {
1489        int height = 0;
1490        View JavaDoc v = getTextViewForTab(tabIndex);
1491        if (v != null) {
1492            // html
1493
height += (int)v.getPreferredSpan(View.Y_AXIS);
1494        } else {
1495            // plain text
1496
height += fontHeight;
1497        }
1498        Icon icon = getIconForTab(tabIndex);
1499        Insets tabInsets = getTabInsets(tabPlacement, tabIndex);
1500
1501        if (icon != null) {
1502            height = Math.max(height, icon.getIconHeight());
1503        }
1504        height += tabInsets.top + tabInsets.bottom + 2;
1505
1506        return height;
1507    }
1508
1509    protected int calculateMaxTabHeight(int tabPlacement) {
1510        FontMetrics metrics = getFontMetrics();
1511        int tabCount = tabPane.getTabCount();
1512        int result = 0;
1513        int fontHeight = metrics.getHeight();
1514        for(int i = 0; i < tabCount; i++) {
1515            result = Math.max(calculateTabHeight(tabPlacement, i, fontHeight), result);
1516        }
1517        return result;
1518    }
1519
1520    protected int calculateTabWidth(int tabPlacement, int tabIndex, FontMetrics metrics) {
1521        Icon icon = getIconForTab(tabIndex);
1522        Insets tabInsets = getTabInsets(tabPlacement, tabIndex);
1523        int width = tabInsets.left + tabInsets.right + 3;
1524
1525        if (icon != null) {
1526            width += icon.getIconWidth() + textIconGap;
1527        }
1528        View JavaDoc v = getTextViewForTab(tabIndex);
1529        if (v != null) {
1530            // html
1531
width += (int)v.getPreferredSpan(View.X_AXIS);
1532        } else {
1533            // plain text
1534
String JavaDoc title = tabPane.getTitleAt(tabIndex);
1535            width += SwingUtilities2.stringWidth(tabPane, metrics, title);
1536        }
1537        
1538        return width;
1539    }
1540    
1541    protected int calculateMaxTabWidth(int tabPlacement) {
1542        FontMetrics metrics = getFontMetrics();
1543        int tabCount = tabPane.getTabCount();
1544        int result = 0;
1545        for(int i = 0; i < tabCount; i++) {
1546            result = Math.max(calculateTabWidth(tabPlacement, i, metrics), result);
1547        }
1548        return result;
1549    }
1550
1551    protected int calculateTabAreaHeight(int tabPlacement, int horizRunCount, int maxTabHeight) {
1552        Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
1553        int tabRunOverlay = getTabRunOverlay(tabPlacement);
1554        return (horizRunCount > 0?
1555                horizRunCount * (maxTabHeight-tabRunOverlay) + tabRunOverlay +
1556                tabAreaInsets.top + tabAreaInsets.bottom :
1557                0);
1558    }
1559
1560    protected int calculateTabAreaWidth(int tabPlacement, int vertRunCount, int maxTabWidth) {
1561        Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
1562        int tabRunOverlay = getTabRunOverlay(tabPlacement);
1563        return (vertRunCount > 0?
1564                vertRunCount * (maxTabWidth-tabRunOverlay) + tabRunOverlay +
1565                tabAreaInsets.left + tabAreaInsets.right :
1566                0);
1567    }
1568
1569    protected Insets getTabInsets(int tabPlacement, int tabIndex) {
1570        return tabInsets;
1571    }
1572
1573    protected Insets getSelectedTabPadInsets(int tabPlacement) {
1574        rotateInsets(selectedTabPadInsets, currentPadInsets, tabPlacement);
1575        return currentPadInsets;
1576    }
1577
1578    protected Insets getTabAreaInsets(int tabPlacement) {
1579        rotateInsets(tabAreaInsets, currentTabAreaInsets, tabPlacement);
1580        return currentTabAreaInsets;
1581    }
1582
1583    protected Insets getContentBorderInsets(int tabPlacement) {
1584        return contentBorderInsets;
1585    }
1586    
1587    protected FontMetrics getFontMetrics() {
1588        Font font = tabPane.getFont();
1589        return tabPane.getFontMetrics(font);
1590    }
1591
1592
1593// Tab Navigation methods
1594

1595    protected void navigateSelectedTab(int direction) {
1596        int tabPlacement = tabPane.getTabPlacement();
1597        int current = DefaultLookup.getBoolean(tabPane, this,
1598                             "TabbedPane.selectionFollowsFocus", true) ?
1599                             tabPane.getSelectedIndex() : getFocusIndex();
1600        int tabCount = tabPane.getTabCount();
1601        boolean leftToRight = BasicGraphicsUtils.isLeftToRight(tabPane);
1602
1603        // If we have no tabs then don't navigate.
1604
if (tabCount <= 0) {
1605            return;
1606        }
1607
1608        int offset;
1609        switch(tabPlacement) {
1610          case LEFT:
1611          case RIGHT:
1612              switch(direction) {
1613                 case NEXT:
1614                     selectNextTab(current);
1615                     break;
1616                 case PREVIOUS:
1617                     selectPreviousTab(current);
1618                     break;
1619                case NORTH:
1620                    selectPreviousTabInRun(current);
1621                    break;
1622                case SOUTH:
1623                    selectNextTabInRun(current);
1624                    break;
1625                case WEST:
1626                    offset = getTabRunOffset(tabPlacement, tabCount, current, false);
1627                    selectAdjacentRunTab(tabPlacement, current, offset);
1628                    break;
1629                case EAST:
1630                    offset = getTabRunOffset(tabPlacement, tabCount, current, true);
1631                    selectAdjacentRunTab(tabPlacement, current, offset);
1632                    break;
1633                default:
1634              }
1635              break;
1636          case BOTTOM:
1637          case TOP:
1638          default:
1639              switch(direction) {
1640                case NEXT:
1641                    selectNextTab(current);
1642                    break;
1643                case PREVIOUS:
1644                    selectPreviousTab(current);
1645                    break;
1646                case NORTH:
1647                    offset = getTabRunOffset(tabPlacement, tabCount, current, false);
1648                    selectAdjacentRunTab(tabPlacement, current, offset);
1649                    break;
1650                case SOUTH:
1651                    offset = getTabRunOffset(tabPlacement, tabCount, current, true);
1652                    selectAdjacentRunTab(tabPlacement, current, offset);
1653                    break;
1654                case EAST:
1655                    if (leftToRight) {
1656                        selectNextTabInRun(current);
1657                    } else {
1658                        selectPreviousTabInRun(current);
1659                    }
1660                    break;
1661                case WEST:
1662                    if (leftToRight) {
1663                        selectPreviousTabInRun(current);
1664                    } else {
1665                        selectNextTabInRun(current);
1666                    }
1667                    break;
1668                default:
1669              }
1670        }
1671    }
1672
1673    protected void selectNextTabInRun(int current) {
1674        int tabCount = tabPane.getTabCount();
1675        int tabIndex = getNextTabIndexInRun(tabCount, current);
1676
1677        while(tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
1678            tabIndex = getNextTabIndexInRun(tabCount, tabIndex);
1679        }
1680        navigateTo(tabIndex);
1681    }
1682
1683    protected void selectPreviousTabInRun(int current) {
1684        int tabCount = tabPane.getTabCount();
1685        int tabIndex = getPreviousTabIndexInRun(tabCount, current);
1686
1687        while(tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
1688            tabIndex = getPreviousTabIndexInRun(tabCount, tabIndex);
1689        }
1690        navigateTo(tabIndex);
1691    }
1692
1693    protected void selectNextTab(int current) {
1694        int tabIndex = getNextTabIndex(current);
1695        
1696        while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
1697            tabIndex = getNextTabIndex(tabIndex);
1698        }
1699        navigateTo(tabIndex);
1700    }
1701
1702    protected void selectPreviousTab(int current) {
1703        int tabIndex = getPreviousTabIndex(current);
1704        
1705        while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
1706            tabIndex = getPreviousTabIndex(tabIndex);
1707        }
1708        navigateTo(tabIndex);
1709    }
1710
1711    protected void selectAdjacentRunTab(int tabPlacement,
1712                                        int tabIndex, int offset) {
1713        if ( runCount < 2 ) {
1714            return;
1715        }
1716        int newIndex;
1717        Rectangle r = rects[tabIndex];
1718        switch(tabPlacement) {
1719          case LEFT:
1720          case RIGHT:
1721              newIndex = tabForCoordinate(tabPane, r.x + r.width/2 + offset,
1722                                       r.y + r.height/2);
1723              break;
1724          case BOTTOM:
1725          case TOP:
1726          default:
1727              newIndex = tabForCoordinate(tabPane, r.x + r.width/2,
1728                                       r.y + r.height/2 + offset);
1729        }
1730        if (newIndex != -1) {
1731            while (!tabPane.isEnabledAt(newIndex) && newIndex != tabIndex) {
1732                newIndex = getNextTabIndex(newIndex);
1733            }
1734            navigateTo(newIndex);
1735        }
1736    }
1737
1738    private void navigateTo(int index) {
1739        if (DefaultLookup.getBoolean(tabPane, this,
1740                             "TabbedPane.selectionFollowsFocus", true)) {
1741            tabPane.setSelectedIndex(index);
1742        } else {
1743            // Just move focus (not selection)
1744
setFocusIndex(index, true);
1745        }
1746    }
1747
1748    void setFocusIndex(int index, boolean repaint) {
1749        if (repaint && !isRunsDirty) {
1750            repaintTab(focusIndex);
1751            focusIndex = index;
1752            repaintTab(focusIndex);
1753        }
1754        else {
1755            focusIndex = index;
1756        }
1757    }
1758
1759    /**
1760     * Repaints the specified tab.
1761     */

1762    private void repaintTab(int index) {
1763        // If we're not valid that means we will shortly be validated and
1764
// painted, which means we don't have to do anything here.
1765
if (!isRunsDirty && index >= 0 && index < tabPane.getTabCount()) {
1766            tabPane.repaint(getTabBounds(tabPane, index));
1767        }
1768    }
1769
1770    /**
1771     * Makes sure the focusIndex is valid.
1772     */

1773    private void validateFocusIndex() {
1774        if (focusIndex >= tabPane.getTabCount()) {
1775            setFocusIndex(tabPane.getSelectedIndex(), false);
1776        }
1777    }
1778
1779    /**
1780     * Returns the index of the tab that has focus.
1781     *
1782     * @return index of tab that has focus
1783     * @since 1.5
1784     */

1785    protected int getFocusIndex() {
1786        return focusIndex;
1787    }
1788
1789    protected int getTabRunOffset(int tabPlacement, int tabCount,
1790                                  int tabIndex, boolean forward) {
1791        int run = getRunForTab(tabCount, tabIndex);
1792        int offset;
1793        switch(tabPlacement) {
1794          case LEFT: {
1795              if (run == 0) {
1796                  offset = (forward?
1797                            -(calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth) :
1798                            -maxTabWidth);
1799
1800              } else if (run == runCount - 1) {
1801                  offset = (forward?
1802                            maxTabWidth :
1803                            calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth);
1804              } else {
1805                  offset = (forward? maxTabWidth : -maxTabWidth);
1806              }
1807              break;
1808          }
1809          case RIGHT: {
1810              if (run == 0) {
1811                  offset = (forward?
1812                            maxTabWidth :
1813                            calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth);
1814              } else if (run == runCount - 1) {
1815                  offset = (forward?
1816                            -(calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth) :
1817                            -maxTabWidth);
1818              } else {
1819                  offset = (forward? maxTabWidth : -maxTabWidth);
1820              }
1821              break;
1822          }
1823          case BOTTOM: {
1824              if (run == 0) {
1825                  offset = (forward?
1826                            maxTabHeight :
1827                            calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight);
1828              } else if (run == runCount - 1) {
1829                  offset = (forward?
1830                            -(calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight) :
1831                            -maxTabHeight);
1832              } else {
1833                  offset = (forward? maxTabHeight : -maxTabHeight);
1834              }
1835              break;
1836          }
1837          case TOP:
1838          default: {
1839              if (run == 0) {
1840                  offset = (forward?
1841                            -(calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight) :
1842                            -maxTabHeight);
1843              } else if (run == runCount - 1) {
1844                  offset = (forward?
1845                            maxTabHeight :
1846                            calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight);
1847              } else {
1848                  offset = (forward? maxTabHeight : -maxTabHeight);
1849              }
1850          }
1851        }
1852        return offset;
1853    }
1854
1855    protected int getPreviousTabIndex(int base) {
1856        int tabIndex = (base - 1 >= 0? base - 1 : tabPane.getTabCount() - 1);
1857        return (tabIndex >= 0? tabIndex : 0);
1858    }
1859
1860    protected int getNextTabIndex(int base) {
1861        return (base+1)%tabPane.getTabCount();
1862    }
1863
1864    protected int getNextTabIndexInRun(int tabCount, int base) {
1865        if (runCount < 2) {
1866            return getNextTabIndex(base);
1867        }
1868        int currentRun = getRunForTab(tabCount, base);
1869        int next = getNextTabIndex(base);
1870        if (next == tabRuns[getNextTabRun(currentRun)]) {
1871            return tabRuns[currentRun];
1872        }
1873        return next;
1874    }
1875
1876    protected int getPreviousTabIndexInRun(int tabCount, int base) {
1877        if (runCount < 2) {
1878            return getPreviousTabIndex(base);
1879        }
1880        int currentRun = getRunForTab(tabCount, base);
1881        if (base == tabRuns[currentRun]) {
1882            int previous = tabRuns[getNextTabRun(currentRun)]-1;
1883            return (previous != -1? previous : tabCount-1);
1884        }
1885        return getPreviousTabIndex(base);
1886    }
1887
1888    protected int getPreviousTabRun(int baseRun) {
1889        int runIndex = (baseRun - 1 >= 0? baseRun - 1 : runCount - 1);
1890        return (runIndex >= 0? runIndex : 0);
1891    }
1892
1893    protected int getNextTabRun(int baseRun) {
1894        return (baseRun+1)%runCount;
1895    }
1896
1897    protected static void rotateInsets(Insets topInsets, Insets targetInsets, int targetPlacement) {
1898        
1899        switch(targetPlacement) {
1900          case LEFT:
1901              targetInsets.top = topInsets.left;
1902              targetInsets.left = topInsets.top;
1903              targetInsets.bottom = topInsets.right;
1904              targetInsets.right = topInsets.bottom;
1905              break;
1906          case BOTTOM:
1907              targetInsets.top = topInsets.bottom;
1908              targetInsets.left = topInsets.left;
1909              targetInsets.bottom = topInsets.top;
1910              targetInsets.right = topInsets.right;
1911              break;
1912          case RIGHT:
1913              targetInsets.top = topInsets.left;
1914              targetInsets.left = topInsets.bottom;
1915              targetInsets.bottom = topInsets.right;
1916              targetInsets.right = topInsets.top;
1917              break;
1918          case TOP:
1919          default:
1920              targetInsets.top = topInsets.top;
1921              targetInsets.left = topInsets.left;
1922              targetInsets.bottom = topInsets.bottom;
1923              targetInsets.right = topInsets.right;
1924        }
1925    }
1926
1927   // REMIND(aim,7/29/98): This method should be made
1928
// protected in the next release where
1929
// API changes are allowed
1930
//
1931
boolean requestFocusForVisibleComponent() {
1932        Component visibleComponent = getVisibleComponent();
1933        if (visibleComponent != null && visibleComponent.isFocusTraversable()) {
1934       BasicLookAndFeel.compositeRequestFocus(visibleComponent);
1935             return true;
1936        } else if (visibleComponent instanceof JComponent) {
1937             if (((JComponent)visibleComponent).requestDefaultFocus()) {
1938                 return true;
1939             }
1940        }
1941        return false;
1942    }
1943
1944    private static class Actions extends UIAction {
1945        final static String JavaDoc NEXT = "navigateNext";
1946        final static String JavaDoc PREVIOUS = "navigatePrevious";
1947        final static String JavaDoc RIGHT = "navigateRight";
1948        final static String JavaDoc LEFT = "navigateLeft";
1949        final static String JavaDoc UP = "navigateUp";
1950        final static String JavaDoc DOWN = "navigateDown";
1951        final static String JavaDoc PAGE_UP = "navigatePageUp";
1952        final static String JavaDoc PAGE_DOWN = "navigatePageDown";
1953        final static String JavaDoc REQUEST_FOCUS = "requestFocus";
1954        final static String JavaDoc REQUEST_FOCUS_FOR_VISIBLE =
1955                                    "requestFocusForVisibleComponent";
1956        final static String JavaDoc SET_SELECTED = "setSelectedIndex";
1957        final static String JavaDoc SELECT_FOCUSED = "selectTabWithFocus";
1958        final static String JavaDoc SCROLL_FORWARD = "scrollTabsForwardAction";
1959        final static String JavaDoc SCROLL_BACKWARD = "scrollTabsBackwardAction";
1960
1961        Actions(String JavaDoc key) {
1962            super(key);
1963        }
1964
1965        public void actionPerformed(ActionEvent e) {
1966            String JavaDoc key = getName();
1967            JTabbedPane pane = (JTabbedPane)e.getSource();
1968            BasicTabbedPaneUI JavaDoc ui = (BasicTabbedPaneUI JavaDoc)BasicLookAndFeel.
1969                       getUIOfType(pane.getUI(), BasicTabbedPaneUI JavaDoc.class);
1970
1971            if (ui == null) {
1972                return;
1973            }
1974            if (key == NEXT) {
1975                ui.navigateSelectedTab(SwingConstants.NEXT);
1976            }
1977            else if (key == PREVIOUS) {
1978                ui.navigateSelectedTab(SwingConstants.PREVIOUS);
1979            }
1980            else if (key == RIGHT) {
1981                ui.navigateSelectedTab(SwingConstants.EAST);
1982            }
1983            else if (key == LEFT) {
1984                ui.navigateSelectedTab(SwingConstants.WEST);
1985            }
1986            else if (key == UP) {
1987                ui.navigateSelectedTab(SwingConstants.NORTH);
1988            }
1989            else if (key == DOWN) {
1990                ui.navigateSelectedTab(SwingConstants.SOUTH);
1991            }
1992            else if (key == PAGE_UP) {
1993                int tabPlacement = pane.getTabPlacement();
1994                if (tabPlacement == TOP|| tabPlacement == BOTTOM) {
1995                    ui.navigateSelectedTab(SwingConstants.WEST);
1996                } else {
1997                    ui.navigateSelectedTab(SwingConstants.NORTH);
1998                }
1999            }
2000            else if (key == PAGE_DOWN) {
2001                int tabPlacement = pane.getTabPlacement();
2002                if (tabPlacement == TOP || tabPlacement == BOTTOM) {
2003                    ui.navigateSelectedTab(SwingConstants.EAST);
2004                } else {
2005                    ui.navigateSelectedTab(SwingConstants.SOUTH);
2006                }
2007            }
2008            else if (key == REQUEST_FOCUS) {
2009                pane.requestFocus();
2010            }
2011            else if (key == REQUEST_FOCUS_FOR_VISIBLE) {
2012                ui.requestFocusForVisibleComponent();
2013            }
2014            else if (key == SET_SELECTED) {
2015                String JavaDoc command = e.getActionCommand();
2016
2017                if (command != null && command.length() > 0) {
2018                    int mnemonic = (int)e.getActionCommand().charAt(0);
2019                    if (mnemonic >= 'a' && mnemonic <='z') {
2020                        mnemonic -= ('a' - 'A');
2021                    }
2022                    Integer JavaDoc index = (Integer JavaDoc)ui.mnemonicToIndexMap.
2023                                 get(new Integer JavaDoc(mnemonic));
2024                    if (index != null && pane.isEnabledAt(index.intValue())) {
2025                        pane.setSelectedIndex(index.intValue());
2026                    }
2027                }
2028            }
2029            else if (key == SELECT_FOCUSED) {
2030                int focusIndex = ui.getFocusIndex();
2031                if (focusIndex != -1) {
2032                    pane.setSelectedIndex(focusIndex);
2033                }
2034            }
2035            else if (key == SCROLL_FORWARD) {
2036                if (ui.scrollableTabLayoutEnabled()) {
2037                    ui.tabScroller.scrollForward(pane.getTabPlacement());
2038                }
2039            }
2040            else if (key == SCROLL_BACKWARD) {
2041                if (ui.scrollableTabLayoutEnabled()) {
2042                    ui.tabScroller.scrollBackward(pane.getTabPlacement());
2043                }
2044            }
2045        }
2046    }
2047
2048    /**
2049     * This class should be treated as a &quot;protected&quot; inner class.
2050     * Instantiate it only within subclasses of BasicTabbedPaneUI.
2051     */

2052    public class TabbedPaneLayout implements LayoutManager {
2053
2054        public void addLayoutComponent(String JavaDoc name, Component comp) {}
2055    
2056        public void removeLayoutComponent(Component comp) {}
2057    
2058        public Dimension preferredLayoutSize(Container parent) {
2059            return calculateSize(false);
2060        }
2061
2062        public Dimension minimumLayoutSize(Container parent) {
2063            return calculateSize(true);
2064        }
2065
2066        protected Dimension calculateSize(boolean minimum) {
2067            int tabPlacement = tabPane.getTabPlacement();
2068            Insets insets = tabPane.getInsets();
2069            Insets contentInsets = getContentBorderInsets(tabPlacement);
2070            Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
2071
2072            Dimension zeroSize = new Dimension(0,0);
2073            int height = 0;
2074            int width = 0;
2075            int cWidth = 0;
2076            int cHeight = 0;
2077
2078            // Determine minimum size required to display largest
2079
// child in each dimension
2080
//
2081
for (int i = 0; i < tabPane.getTabCount(); i++) {
2082                Component component = tabPane.getComponentAt(i);
2083                if (component != null) {
2084                    Dimension size = zeroSize;
2085                    size = minimum? component.getMinimumSize() :
2086                                component.getPreferredSize();
2087                      
2088                    if (size != null) {
2089                        cHeight = Math.max(size.height, cHeight);
2090                        cWidth = Math.max(size.width, cWidth);
2091                    }
2092                }
2093            }
2094            // Add content border insets to minimum size
2095
width += cWidth;
2096            height += cHeight;
2097            int tabExtent = 0;
2098
2099            // Calculate how much space the tabs will need, based on the
2100
// minimum size required to display largest child + content border
2101
//
2102
switch(tabPlacement) {
2103              case LEFT:
2104              case RIGHT:
2105                  height = Math.max(height, calculateMaxTabHeight(tabPlacement));
2106                  tabExtent = preferredTabAreaWidth(tabPlacement, height - tabAreaInsets.top - tabAreaInsets.bottom);
2107                  width += tabExtent;
2108                  break;
2109              case TOP:
2110              case BOTTOM:
2111              default:
2112                  width = Math.max(width, calculateMaxTabWidth(tabPlacement));
2113                  tabExtent = preferredTabAreaHeight(tabPlacement, width - tabAreaInsets.left - tabAreaInsets.right);
2114                  height += tabExtent;
2115            }
2116            return new Dimension(width + insets.left + insets.right + contentInsets.left + contentInsets.right,
2117                             height + insets.bottom + insets.top + contentInsets.top + contentInsets.bottom);
2118
2119        }
2120
2121        protected int preferredTabAreaHeight(int tabPlacement, int width) {
2122            FontMetrics metrics = getFontMetrics();
2123            int tabCount = tabPane.getTabCount();
2124            int total = 0;
2125            if (tabCount > 0) {
2126                int rows = 1;
2127                int x = 0;
2128
2129                int maxTabHeight = calculateMaxTabHeight(tabPlacement);
2130        
2131                for (int i = 0; i < tabCount; i++) {
2132                    int tabWidth = calculateTabWidth(tabPlacement, i, metrics);
2133
2134                    if (x != 0 && x + tabWidth > width) {
2135                        rows++;
2136                        x = 0;
2137                    }
2138                    x += tabWidth;
2139                }
2140                total = calculateTabAreaHeight(tabPlacement, rows, maxTabHeight);
2141            }
2142            return total;
2143        }
2144
2145        protected int preferredTabAreaWidth(int tabPlacement, int height) {
2146            FontMetrics metrics = getFontMetrics();
2147            int tabCount = tabPane.getTabCount();
2148            int total = 0;
2149            if (tabCount > 0) {
2150                int columns = 1;
2151                int y = 0;
2152                int fontHeight = metrics.getHeight();
2153
2154                maxTabWidth = calculateMaxTabWidth(tabPlacement);
2155        
2156                for (int i = 0; i < tabCount; i++) {
2157                    int tabHeight = calculateTabHeight(tabPlacement, i, fontHeight);
2158
2159                    if (y != 0 && y + tabHeight > height) {
2160                        columns++;
2161                        y = 0;
2162                    }
2163                    y += tabHeight;
2164                }
2165                total = calculateTabAreaWidth(tabPlacement, columns, maxTabWidth);
2166            }
2167            return total;
2168        }
2169
2170        public void layoutContainer(Container parent) {
2171            setRolloverTab(-1);
2172
2173            int tabPlacement = tabPane.getTabPlacement();
2174            Insets insets = tabPane.getInsets();
2175            int selectedIndex = tabPane.getSelectedIndex();
2176            Component visibleComponent = getVisibleComponent();
2177
2178            calculateLayoutInfo();
2179
2180            if (selectedIndex < 0) {
2181                if (visibleComponent != null) {
2182                    // The last tab was removed, so remove the component
2183
setVisibleComponent(null);
2184                }
2185            } else {
2186                int cx, cy, cw, ch;
2187                int totalTabWidth = 0;
2188                int totalTabHeight = 0;
2189                Insets contentInsets = getContentBorderInsets(tabPlacement);
2190
2191                Component selectedComponent = tabPane.getComponentAt(selectedIndex);
2192                boolean shouldChangeFocus = false;
2193
2194                // In order to allow programs to use a single component
2195
// as the display for multiple tabs, we will not change
2196
// the visible compnent if the currently selected tab
2197
// has a null component. This is a bit dicey, as we don't
2198
// explicitly state we support this in the spec, but since
2199
// programs are now depending on this, we're making it work.
2200
//
2201
if (selectedComponent != null) {
2202                    if (selectedComponent != visibleComponent &&
2203                            visibleComponent != null) {
2204                        if (SwingUtilities.findFocusOwner(visibleComponent) != null) {
2205                           shouldChangeFocus = true;
2206                        }
2207                    }
2208                    setVisibleComponent(selectedComponent);
2209                }
2210
2211                Rectangle bounds = tabPane.getBounds();
2212                int numChildren = tabPane.getComponentCount();
2213
2214                if (numChildren > 0) {
2215
2216                    switch(tabPlacement) {
2217                      case LEFT:
2218                        totalTabWidth = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
2219                        cx = insets.left + totalTabWidth + contentInsets.left;
2220                        cy = insets.top + contentInsets.top;
2221                        break;
2222                      case RIGHT:
2223                        totalTabWidth = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
2224                        cx = insets.left + contentInsets.left;
2225                        cy = insets.top + contentInsets.top;
2226                        break;
2227                      case BOTTOM:
2228                        totalTabHeight = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
2229                        cx = insets.left + contentInsets.left;
2230                        cy = insets.top + contentInsets.top;
2231                        break;
2232                      case TOP:
2233                     default:
2234                       totalTabHeight = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
2235                       cx = insets.left + contentInsets.left;
2236                       cy = insets.top + totalTabHeight + contentInsets.top;
2237                    }
2238                
2239                    cw = bounds.width - totalTabWidth -
2240                                 insets.left - insets.right -
2241                                 contentInsets.left - contentInsets.right;
2242                    ch = bounds.height - totalTabHeight -
2243                                 insets.top - insets.bottom -
2244                                 contentInsets.top - contentInsets.bottom;
2245
2246                    for (int i=0; i < numChildren; i++) {
2247                        Component child = tabPane.getComponent(i);
2248                        child.setBounds(cx, cy, cw, ch);
2249                    }
2250                }
2251
2252                if (shouldChangeFocus) {
2253                    if (!requestFocusForVisibleComponent()) {
2254                       tabPane.requestFocus();
2255                    }
2256                }
2257            }
2258        }
2259
2260        public void calculateLayoutInfo() {
2261            int tabCount = tabPane.getTabCount();
2262            assureRectsCreated(tabCount);
2263            calculateTabRects(tabPane.getTabPlacement(), tabCount);
2264            isRunsDirty = false;
2265        }
2266
2267        protected void calculateTabRects(int tabPlacement, int tabCount) {
2268            FontMetrics metrics = getFontMetrics();
2269            Dimension size = tabPane.getSize();
2270            Insets insets = tabPane.getInsets();
2271            Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
2272            int fontHeight = metrics.getHeight();
2273            int selectedIndex = tabPane.getSelectedIndex();
2274            int tabRunOverlay;
2275            int i, j;
2276            int x, y;
2277            int returnAt;
2278            boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
2279            boolean leftToRight = BasicGraphicsUtils.isLeftToRight(tabPane);
2280
2281            //
2282
// Calculate bounds within which a tab run must fit
2283
//
2284
switch(tabPlacement) {
2285              case LEFT:
2286                  maxTabWidth = calculateMaxTabWidth(tabPlacement);
2287                  x = insets.left + tabAreaInsets.left;
2288                  y = insets.top + tabAreaInsets.top;
2289                  returnAt = size.height - (insets.bottom + tabAreaInsets.bottom);
2290                  break;
2291              case RIGHT:
2292                  maxTabWidth = calculateMaxTabWidth(tabPlacement);
2293                  x = size.width - insets.right - tabAreaInsets.right - maxTabWidth;
2294                  y = insets.top + tabAreaInsets.top;
2295                  returnAt = size.height - (insets.bottom + tabAreaInsets.bottom);
2296                  break;
2297              case BOTTOM:
2298                  maxTabHeight = calculateMaxTabHeight(tabPlacement);
2299                  x = insets.left + tabAreaInsets.left;
2300                  y = size.height - insets.bottom - tabAreaInsets.bottom - maxTabHeight;
2301                  returnAt = size.width - (insets.right + tabAreaInsets.right);
2302                  break;
2303              case TOP:
2304              default:
2305                  maxTabHeight = calculateMaxTabHeight(tabPlacement);
2306                  x = insets.left + tabAreaInsets.left;
2307                  y = insets.top + tabAreaInsets.top;
2308                  returnAt = size.width - (insets.right + tabAreaInsets.right);
2309                  break;
2310            }
2311
2312            tabRunOverlay = getTabRunOverlay(tabPlacement);
2313
2314            runCount = 0;
2315            selectedRun = -1;
2316
2317            if (tabCount == 0) {
2318                return;
2319            }
2320
2321            // Run through tabs and partition them into runs
2322
Rectangle rect;
2323            for (i = 0; i < tabCount; i++) {
2324                rect = rects[i];
2325
2326                if (!verticalTabRuns) {
2327                    // Tabs on TOP or BOTTOM....
2328
if (i > 0) {
2329                        rect.x = rects[i-1].x + rects[i-1].width;
2330                    } else {
2331                        tabRuns[0] = 0;
2332                        runCount = 1;
2333                        maxTabWidth = 0;
2334                        rect.x = x;
2335                    }
2336                    rect.width = calculateTabWidth(tabPlacement, i, metrics);
2337                    maxTabWidth = Math.max(maxTabWidth, rect.width);
2338
2339                    // Never move a TAB down a run if it is in the first column.
2340
// Even if there isn't enough room, moving it to a fresh
2341
// line won't help.
2342
if (rect.x != 2 + insets.left && rect.x + rect.width > returnAt) {
2343                        if (runCount > tabRuns.length - 1) {
2344                            expandTabRunsArray();
2345                        }
2346                        tabRuns[runCount] = i;
2347                        runCount++;
2348                        rect.x = x;
2349                    }
2350                    // Initialize y position in case there's just one run
2351
rect.y = y;
2352                    rect.height = maxTabHeight/* - 2*/;
2353
2354                } else {
2355                    // Tabs on LEFT or RIGHT...
2356
if (i > 0) {
2357                        rect.y = rects[i-1].y + rects[i-1].height;
2358                    } else {
2359                        tabRuns[0] = 0;
2360                        runCount = 1;
2361                        maxTabHeight = 0;
2362                        rect.y = y;
2363                    }
2364                    rect.height = calculateTabHeight(tabPlacement, i, fontHeight);
2365                    maxTabHeight = Math.max(maxTabHeight, rect.height);
2366
2367                    // Never move a TAB over a run if it is in the first run.
2368
// Even if there isn't enough room, moving it to a fresh
2369
// column won't help.
2370
if (rect.y != 2 + insets.top && rect.y + rect.height > returnAt) {
2371                        if (runCount > tabRuns.length - 1) {
2372                            expandTabRunsArray();
2373                        }
2374                        tabRuns[runCount] = i;
2375                        runCount++;
2376                        rect.y = y;
2377                    }
2378                    // Initialize x position in case there's just one column
2379
rect.x = x;
2380                    rect.width = maxTabWidth/* - 2*/;
2381
2382                }
2383                if (i == selectedIndex) {
2384                    selectedRun = runCount - 1;
2385                }
2386            }
2387
2388            if (runCount > 1) {
2389                // Re-distribute tabs in case last run has leftover space
2390
normalizeTabRuns(tabPlacement, tabCount, verticalTabRuns? y : x, returnAt);
2391
2392                selectedRun = getRunForTab(tabCount, selectedIndex);
2393
2394                // Rotate run array so that selected run is first
2395
if (shouldRotateTabRuns(tabPlacement)) {
2396                    rotateTabRuns(tabPlacement, selectedRun);
2397                }
2398            }
2399
2400            // Step through runs from back to front to calculate
2401
// tab y locations and to pad runs appropriately
2402
for (i = runCount - 1; i >= 0; i--) {
2403                int start = tabRuns[i];
2404                int next = tabRuns[i == (runCount - 1)? 0 : i + 1];
2405                int end = (next != 0? next - 1 : tabCount - 1);
2406                if (!verticalTabRuns) {
2407                    for (j = start; j <= end; j++) {
2408                        rect = rects[j];
2409                        rect.y = y;
2410                        rect.x += getTabRunIndent(tabPlacement, i);
2411                    }
2412                    if (shouldPadTabRun(tabPlacement, i)) {
2413                        padTabRun(tabPlacement, start, end, returnAt);
2414                    }
2415                    if (tabPlacement == BOTTOM) {
2416                        y -= (maxTabHeight - tabRunOverlay);
2417                    } else {
2418                        y += (maxTabHeight - tabRunOverlay);
2419                    }
2420                } else {
2421                    for (j = start; j <= end; j++) {
2422                        rect = rects[j];
2423                        rect.x = x;
2424                        rect.y += getTabRunIndent(tabPlacement, i);
2425                    }
2426                    if (shouldPadTabRun(tabPlacement, i)) {
2427                        padTabRun(tabPlacement, start, end, returnAt);
2428                    }
2429                    if (tabPlacement == RIGHT) {
2430                        x -= (maxTabWidth - tabRunOverlay);
2431                    } else {
2432                        x += (maxTabWidth - tabRunOverlay);
2433                    }
2434                }
2435            }
2436
2437            // Pad the selected tab so that it appears raised in front
2438
padSelectedTab(tabPlacement, selectedIndex);
2439
2440            // if right to left and tab placement on the top or
2441
// the bottom, flip x positions and adjust by widths
2442
if (!leftToRight && !verticalTabRuns) {
2443                int rightMargin = size.width
2444                                  - (insets.right + tabAreaInsets.right);
2445                for (i = 0; i < tabCount; i++) {
2446                    rects[i].x = rightMargin - rects[i].x - rects[i].width;
2447                }
2448            }
2449        }
2450
2451
2452       /*
2453       * Rotates the run-index array so that the selected run is run[0]
2454       */

2455        protected void rotateTabRuns(int tabPlacement, int selectedRun) {
2456            for (int i = 0; i < selectedRun; i++) {
2457                int save = tabRuns[0];
2458                for (int j = 1; j < runCount; j++) {
2459                    tabRuns[j - 1] = tabRuns[j];
2460                }
2461                tabRuns[runCount-1] = save;
2462            }
2463        }
2464
2465        protected void normalizeTabRuns(int tabPlacement, int tabCount,
2466                                     int start, int max) {
2467            boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
2468            int run = runCount - 1;
2469            boolean keepAdjusting = true;
2470            double weight = 1.25;
2471
2472            // At this point the tab runs are packed to fit as many
2473
// tabs as possible, which can leave the last run with a lot
2474
// of extra space (resulting in very fat tabs on the last run).
2475
// So we'll attempt to distribute this extra space more evenly
2476
// across the runs in order to make the runs look more consistent.
2477
//
2478
// Starting with the last run, determine whether the last tab in
2479
// the previous run would fit (generously) in this run; if so,
2480
// move tab to current run and shift tabs accordingly. Cycle
2481
// through remaining runs using the same algorithm.
2482
//
2483
while (keepAdjusting) {
2484                int last = lastTabInRun(tabCount, run);
2485                int prevLast = lastTabInRun(tabCount, run-1);
2486                int end;
2487                int prevLastLen;
2488
2489                if (!verticalTabRuns) {
2490                    end = rects[last].x + rects[last].width;
2491                    prevLastLen = (int)(maxTabWidth*weight);
2492                } else {
2493                    end = rects[last].y + rects[last].height;
2494                    prevLastLen = (int)(maxTabHeight*weight*2);
2495                }
2496 
2497                // Check if the run has enough extra space to fit the last tab
2498
// from the previous row...
2499
if (max - end > prevLastLen) {
2500
2501                    // Insert tab from previous row and shift rest over
2502
tabRuns[run] = prevLast;
2503                    if (!verticalTabRuns) {
2504                        rects[prevLast].x = start;
2505                    } else {
2506                        rects[prevLast].y = start;
2507                    }
2508                    for (int i = prevLast+1; i <= last; i++) {
2509                        if (!verticalTabRuns) {
2510                            rects[i].x = rects[i-1].x + rects[i-1].width;
2511                        } else {
2512                            rects[i].y = rects[i-1].y + rects[i-1].height;
2513                        }
2514                    }
2515 
2516                } else if (run == runCount - 1) {
2517                    // no more room left in last run, so we're done!
2518
keepAdjusting = false;
2519                }
2520                if (run - 1 > 0) {
2521                    // check previous run next...
2522
run -= 1;
2523                } else {
2524                    // check last run again...but require a higher ratio
2525
// of extraspace-to-tabsize because we don't want to
2526
// end up with too many tabs on the last run!
2527
run = runCount - 1;
2528                    weight += .25;
2529                }
2530            }
2531        }
2532
2533        protected void padTabRun(int tabPlacement, int start, int end, int max) {
2534            Rectangle lastRect = rects[end];
2535            if (tabPlacement == TOP || tabPlacement == BOTTOM) {
2536                int runWidth = (lastRect.x + lastRect.width) - rects[start].x;
2537                int deltaWidth = max - (lastRect.x + lastRect.width);
2538                float factor = (float)deltaWidth / (float)runWidth;
2539
2540                for (int j = start; j <= end; j++) {
2541                    Rectangle pastRect = rects[j];
2542                    if (j > start) {
2543                        pastRect.x = rects[j-1].x + rects[j-1].width;
2544                    }
2545                    pastRect.width += Math.round((float)pastRect.width * factor);
2546                }
2547                lastRect.width = max - lastRect.x;
2548            } else {
2549                int runHeight = (lastRect.y + lastRect.height) - rects[start].y;
2550                int deltaHeight = max - (lastRect.y + lastRect.height);
2551                float factor = (float)deltaHeight / (float)runHeight;
2552
2553                for (int j = start; j <= end; j++) {
2554                    Rectangle pastRect = rects[j];
2555                    if (j > start) {
2556                        pastRect.y = rects[j-1].y + rects[j-1].height;
2557                    }
2558                    pastRect.height += Math.round((float)pastRect.height * factor);
2559                }
2560                lastRect.height = max - lastRect.y;
2561            }
2562        }
2563
2564        protected void padSelectedTab(int tabPlacement, int selectedIndex) {
2565
2566           if (selectedIndex >= 0) {
2567                Rectangle selRect = rects[selectedIndex];
2568                Insets padInsets = getSelectedTabPadInsets(tabPlacement);
2569                selRect.x -= padInsets.left;
2570                selRect.width += (padInsets.left + padInsets.right);
2571                selRect.y -= padInsets.top;
2572                selRect.height += (padInsets.top + padInsets.bottom);
2573           }
2574        }
2575    }
2576
2577    private class TabbedPaneScrollLayout extends TabbedPaneLayout {
2578
2579        protected int preferredTabAreaHeight(int tabPlacement, int width) {
2580            return calculateMaxTabHeight(tabPlacement);
2581        }
2582
2583        protected int preferredTabAreaWidth(int tabPlacement, int height) {
2584            return calculateMaxTabWidth(tabPlacement);
2585        }
2586
2587        public void layoutContainer(Container parent) {
2588            setRolloverTab(-1);
2589
2590            int tabPlacement = tabPane.getTabPlacement();
2591            int tabCount = tabPane.getTabCount();
2592            Insets insets = tabPane.getInsets();
2593            int selectedIndex = tabPane.getSelectedIndex();
2594            Component visibleComponent = getVisibleComponent();
2595
2596            calculateLayoutInfo();
2597
2598            if (selectedIndex < 0) {
2599                if (visibleComponent != null) {
2600                    // The last tab was removed, so remove the component
2601
setVisibleComponent(null);
2602                }
2603            } else {
2604                Component selectedComponent = tabPane.getComponentAt(selectedIndex);
2605                boolean shouldChangeFocus = false;
2606
2607                // In order to allow programs to use a single component
2608
// as the display for multiple tabs, we will not change
2609
// the visible compnent if the currently selected tab
2610
// has a null component. This is a bit dicey, as we don't
2611
// explicitly state we support this in the spec, but since
2612
// programs are now depending on this, we're making it work.
2613
//
2614
if (selectedComponent != null) {
2615                    if (selectedComponent != visibleComponent &&
2616                            visibleComponent != null) {
2617                        if (SwingUtilities.findFocusOwner(visibleComponent) != null) {
2618                            shouldChangeFocus = true;
2619                        }
2620                    }
2621                    setVisibleComponent(selectedComponent);
2622                }
2623                int tx, ty, tw, th; // tab area bounds
2624
int cx, cy, cw, ch; // content area bounds
2625
Insets contentInsets = getContentBorderInsets(tabPlacement);
2626                Rectangle bounds = tabPane.getBounds();
2627                int numChildren = tabPane.getComponentCount();
2628
2629                if (numChildren > 0) {
2630                    switch(tabPlacement) {
2631                      case LEFT:
2632                        // calculate tab area bounds
2633
tw = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
2634                        th = bounds.height - insets.top - insets.bottom;
2635                        tx = insets.left;
2636                        ty = insets.top;
2637
2638                        // calculate content area bounds
2639
cx = tx + tw + contentInsets.left;
2640                        cy = ty + contentInsets.top;
2641                        cw = bounds.width - insets.left - insets.right - tw -
2642                                 contentInsets.left - contentInsets.right;
2643                        ch = bounds.height - insets.top - insets.bottom -
2644                                 contentInsets.top - contentInsets.bottom;
2645                        break;
2646                      case RIGHT:
2647                        // calculate tab area bounds
2648
tw = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
2649                        th = bounds.height - insets.top - insets.bottom;
2650                        tx = bounds.width - insets.right - tw;
2651                        ty = insets.top;
2652
2653                        // calculate content area bounds
2654
cx = insets.left + contentInsets.left;
2655                        cy = insets.top + contentInsets.top;
2656                        cw = bounds.width - insets.left - insets.right - tw -
2657                                 contentInsets.left - contentInsets.right;
2658                        ch = bounds.height - insets.top - insets.bottom -
2659                                 contentInsets.top - contentInsets.bottom;
2660                        break;
2661                      case BOTTOM:
2662                        // calculate tab area bounds
2663
tw = bounds.width - insets.left - insets.right;
2664                        th = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
2665                        tx = insets.left;
2666                        ty = bounds.height - insets.bottom - th;
2667
2668                        // calculate content area bounds
2669
cx = insets.left + contentInsets.left;
2670                        cy = insets.top + contentInsets.top;
2671                        cw = bounds.width - insets.left - insets.right -
2672                                 contentInsets.left - contentInsets.right;
2673                        ch = bounds.height - insets.top - insets.bottom - th -
2674                                 contentInsets.top - contentInsets.bottom;
2675                        break;
2676                      case TOP:
2677                      default:
2678                        // calculate tab area bounds
2679
tw = bounds.width - insets.left - insets.right;
2680                        th = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
2681                        tx = insets.left;
2682                        ty = insets.top;
2683
2684                        // calculate content area bounds
2685
cx = tx + contentInsets.left;
2686                        cy = ty + th + contentInsets.top;
2687                        cw = bounds.width - insets.left - insets.right -
2688                                 contentInsets.left - contentInsets.right;
2689                        ch = bounds.height - insets.top - insets.bottom - th -
2690                                 contentInsets.top - contentInsets.bottom;
2691                    }
2692
2693                    for (int i=0; i < numChildren; i++) {
2694                        Component child = tabPane.getComponent(i);
2695
2696                        if (tabScroller != null && child == tabScroller.viewport) {
2697                            JViewport viewport = (JViewport)child;
2698                            Rectangle viewRect = viewport.getViewRect();
2699                            int vw = tw;
2700                            int vh = th;
2701                            Dimension butSize = tabScroller.scrollForwardButton.getPreferredSize();
2702                            switch(tabPlacement) {
2703                              case LEFT:
2704                              case RIGHT:
2705                                int totalTabHeight = rects[tabCount-1].y + rects[tabCount-1].height;
2706                                if (totalTabHeight > th) {
2707                                    // Allow space for scrollbuttons
2708
vh = (th > 2*butSize.height) ? th - 2*butSize.height : 0;
2709                                    if (totalTabHeight - viewRect.y <= vh) {
2710                                          // Scrolled to the end, so ensure the viewport size is
2711
// such that the scroll offset aligns with a tab
2712
vh = totalTabHeight - viewRect.y;
2713                                     }
2714                                }
2715                                break;
2716                              case BOTTOM:
2717                              case TOP:
2718                              default:
2719                                int totalTabWidth = rects[tabCount-1].x + rects[tabCount-1].width;
2720                                if (totalTabWidth > tw) {
2721                                      // Need to allow space for scrollbuttons
2722
vw = (tw > 2*butSize.width) ? tw - 2*butSize.width : 0 ;
2723                                      if (totalTabWidth - viewRect.x <= vw) {
2724                                          // Scrolled to the end, so ensure the viewport size is
2725
// such that the scroll offset aligns with a tab
2726
vw = totalTabWidth - viewRect.x;
2727                                      }
2728                                }
2729                            }
2730                            child.setBounds(tx, ty, vw, vh);
2731                    
2732                        } else if (tabScroller != null &&
2733                                   (child == tabScroller.scrollForwardButton ||
2734                                    child == tabScroller.scrollBackwardButton)) {
2735                            Component scrollbutton = child;
2736                            Dimension bsize = scrollbutton.getPreferredSize();
2737                            int bx = 0;
2738                            int by = 0;
2739                            int bw = bsize.width;
2740                            int bh = bsize.height;
2741                            boolean visible = false;
2742                
2743                            switch(tabPlacement) {
2744                              case LEFT:
2745                              case RIGHT:
2746                                int totalTabHeight = rects[tabCount-1].y + rects[tabCount-1].height;
2747                                if (totalTabHeight > th) {
2748                                    visible = true;
2749                                    bx = (tabPlacement == LEFT? tx + tw - bsize.width : tx);
2750                                    by = (child == tabScroller.scrollForwardButton)?
2751                                        bounds.height - insets.bottom - bsize.height :
2752                                        bounds.height - insets.bottom - 2*bsize.height;
2753                                }
2754                                break;
2755
2756                              case BOTTOM:
2757                              case TOP:
2758                              default:
2759                                int totalTabWidth = rects[tabCount-1].x + rects[tabCount-1].width;
2760
2761                                if (totalTabWidth > tw) {
2762                                    visible = true;
2763                                    bx = (child == tabScroller.scrollForwardButton)?
2764                                        bounds.width - insets.left - bsize.width :
2765                                        bounds.width - insets.left - 2*bsize.width;
2766                                    by = (tabPlacement == TOP? ty + th - bsize.height : ty);
2767                                }
2768                            }
2769                            child.setVisible(visible);
2770                            if (visible) {
2771                                child.setBounds(bx, by, bw, bh);
2772                            }
2773
2774                        } else {
2775                            // All content children...
2776
child.setBounds(cx, cy, cw, ch);
2777                        }
2778                    }
2779                    if (shouldChangeFocus) {
2780                        if (!requestFocusForVisibleComponent()) {
2781                           tabPane.requestFocus();
2782                        }
2783                    }
2784                }
2785            }
2786        }
2787
2788        protected void calculateTabRects(int tabPlacement, int tabCount) {
2789            FontMetrics metrics = getFontMetrics();
2790            Dimension size = tabPane.getSize();
2791            Insets insets = tabPane.getInsets();
2792            Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
2793            int fontHeight = metrics.getHeight();
2794            int selectedIndex = tabPane.getSelectedIndex();
2795            int i, j;
2796            boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
2797            boolean leftToRight = BasicGraphicsUtils.isLeftToRight(tabPane);
2798            int x = tabAreaInsets.left;
2799            int y = tabAreaInsets.top;
2800            int totalWidth = 0;
2801            int totalHeight = 0;
2802
2803            //
2804
// Calculate bounds within which a tab run must fit
2805
//
2806
switch(tabPlacement) {
2807              case LEFT:
2808              case RIGHT:
2809                  maxTabWidth = calculateMaxTabWidth(tabPlacement);
2810                  break;
2811              case BOTTOM:
2812              case TOP:
2813              default:
2814                  maxTabHeight = calculateMaxTabHeight(tabPlacement);
2815            }
2816
2817            runCount = 0;
2818            selectedRun = -1;
2819
2820            if (tabCount == 0) {
2821                return;
2822            }
2823
2824            selectedRun = 0;
2825            runCount = 1;
2826
2827            // Run through tabs and lay them out in a single run
2828
Rectangle rect;
2829            for (i = 0; i < tabCount; i++) {
2830                rect = rects[i];
2831
2832                if (!verticalTabRuns) {
2833                    // Tabs on TOP or BOTTOM....
2834
if (i > 0) {
2835                        rect.x = rects[i-1].x + rects[i-1].width;
2836                    } else {
2837                        tabRuns[0] = 0;
2838                        maxTabWidth = 0;
2839                        totalHeight += maxTabHeight;
2840                        rect.x = x;
2841                    }
2842                    rect.width = calculateTabWidth(tabPlacement, i, metrics);
2843                    totalWidth = rect.x + rect.width;
2844                    maxTabWidth = Math.max(maxTabWidth, rect.width);
2845
2846                    rect.y = y;
2847                    rect.height = maxTabHeight/* - 2*/;
2848
2849                } else {
2850                    // Tabs on LEFT or RIGHT...
2851
if (i > 0) {
2852                        rect.y = rects[i-1].y + rects[i-1].height;
2853                    } else {
2854                        tabRuns[0] = 0;
2855                        maxTabHeight = 0;
2856                        totalWidth = maxTabWidth;
2857                        rect.y = y;
2858                    }
2859                    rect.height = calculateTabHeight(tabPlacement, i, fontHeight);
2860                    totalHeight = rect.y + rect.height;
2861                    maxTabHeight = Math.max(maxTabHeight, rect.height);
2862
2863                    rect.x = x;
2864                    rect.width = maxTabWidth/* - 2*/;
2865
2866                }
2867            }
2868
2869        if (tabsOverlapBorder) {
2870        // Pad the selected tab so that it appears raised in front
2871
padSelectedTab(tabPlacement, selectedIndex);
2872        }
2873
2874            // if right to left and tab placement on the top or
2875
// the bottom, flip x positions and adjust by widths
2876
if (!leftToRight && !verticalTabRuns) {
2877                int rightMargin = size.width
2878                                  - (insets.right + tabAreaInsets.right);
2879                for (i = 0; i < tabCount; i++) {
2880                    rects[i].x = rightMargin - rects[i].x - rects[i].width;
2881                }
2882            }
2883            //tabPanel.setSize(totalWidth, totalHeight);
2884
tabScroller.tabPanel.setPreferredSize(new Dimension(totalWidth, totalHeight));
2885        }
2886    }
2887
2888    private class ScrollableTabSupport implements ActionListener,
2889                            ChangeListener JavaDoc {
2890        public ScrollableTabViewport viewport;
2891        public ScrollableTabPanel tabPanel;
2892        public JButton scrollForwardButton;
2893        public JButton scrollBackwardButton;
2894        public int leadingTabIndex;
2895
2896        private Point tabViewPosition = new Point(0,0);
2897
2898        ScrollableTabSupport(int tabPlacement) {
2899            viewport = new ScrollableTabViewport();
2900            tabPanel = new ScrollableTabPanel();
2901            viewport.setView(tabPanel);
2902            viewport.addChangeListener(this);
2903            createButtons();
2904        }
2905
2906        /**
2907         * Recreates the scroll buttons and adds them to the TabbedPane.
2908         */

2909        void createButtons() {
2910            if (scrollForwardButton != null) {
2911                tabPane.remove(scrollForwardButton);
2912                scrollForwardButton.removeActionListener(this);
2913                tabPane.remove(scrollBackwardButton);
2914                scrollBackwardButton.removeActionListener(this);
2915            }
2916            int tabPlacement = tabPane.getTabPlacement();
2917            if (tabPlacement == TOP || tabPlacement == BOTTOM) {
2918                scrollForwardButton = createScrollButton(EAST);
2919                scrollBackwardButton = createScrollButton(WEST);
2920
2921            } else { // tabPlacement = LEFT || RIGHT
2922
scrollForwardButton = createScrollButton(SOUTH);
2923                scrollBackwardButton = createScrollButton(NORTH);
2924            }
2925            scrollForwardButton.addActionListener(this);
2926            scrollBackwardButton.addActionListener(this);
2927            tabPane.add(scrollForwardButton);
2928            tabPane.add(scrollBackwardButton);
2929        }
2930
2931        public void scrollForward(int tabPlacement) {
2932            Dimension viewSize = viewport.getViewSize();
2933            Rectangle viewRect = viewport.getViewRect();
2934
2935            if (tabPlacement == TOP || tabPlacement == BOTTOM) {
2936                if (viewRect.width >= viewSize.width - viewRect.x) {
2937                    return; // no room left to scroll
2938
}
2939            } else { // tabPlacement == LEFT || tabPlacement == RIGHT
2940
if (viewRect.height >= viewSize.height - viewRect.y) {
2941                    return;
2942                }
2943            }
2944            setLeadingTabIndex(tabPlacement, leadingTabIndex+1);
2945        }
2946
2947        public void scrollBackward(int tabPlacement) {
2948            if (leadingTabIndex == 0) {
2949                return; // no room left to scroll
2950
}
2951            setLeadingTabIndex(tabPlacement, leadingTabIndex-1);
2952        }
2953
2954        public void setLeadingTabIndex(int tabPlacement, int index) {
2955            leadingTabIndex = index;
2956            Dimension viewSize = viewport.getViewSize();
2957            Rectangle viewRect = viewport.getViewRect();
2958
2959            switch(tabPlacement) {
2960              case TOP:
2961              case BOTTOM:
2962                tabViewPosition.x = leadingTabIndex == 0? 0 : rects[leadingTabIndex].x;
2963
2964                if ((viewSize.width - tabViewPosition.x) < viewRect.width) {
2965                    // We've scrolled to the end, so adjust the viewport size
2966
// to ensure the view position remains aligned on a tab boundary
2967
Dimension extentSize = new Dimension(viewSize.width - tabViewPosition.x,
2968                                                         viewRect.height);
2969                    viewport.setExtentSize(extentSize);
2970                }
2971                break;
2972              case LEFT:
2973              case RIGHT:
2974                tabViewPosition.y = leadingTabIndex == 0? 0 : rects[leadingTabIndex].y;
2975
2976                if ((viewSize.height - tabViewPosition.y) < viewRect.height) {
2977                // We've scrolled to the end, so adjust the viewport size
2978
// to ensure the view position remains aligned on a tab boundary
2979
Dimension extentSize = new Dimension(viewRect.width,
2980                                                          viewSize.height - tabViewPosition.y);
2981                     viewport.setExtentSize(extentSize);
2982                }
2983            }
2984            viewport.setViewPosition(tabViewPosition);
2985        }
2986
2987        public void stateChanged(ChangeEvent JavaDoc e) {
2988            JViewport viewport = (JViewport)e.getSource();
2989            int tabPlacement = tabPane.getTabPlacement();
2990            int tabCount = tabPane.getTabCount();
2991            Rectangle vpRect = viewport.getBounds();
2992            Dimension viewSize = viewport.getViewSize();
2993            Rectangle viewRect = viewport.getViewRect();
2994
2995            leadingTabIndex = getClosestTab(viewRect.x, viewRect.y);
2996
2997            // If the tab isn't right aligned, adjust it.
2998
if (leadingTabIndex + 1 < tabCount) {
2999                switch (tabPlacement) {
3000                case TOP:
3001                case BOTTOM:
3002                    if (rects[leadingTabIndex].x < viewRect.x) {
3003                        leadingTabIndex++;
3004                    }
3005                    break;
3006                case LEFT:
3007                case RIGHT:
3008                    if (rects[leadingTabIndex].y < viewRect.y) {
3009                        leadingTabIndex++;
3010                    }
3011                    break;
3012                }
3013            }
3014            Insets contentInsets = getContentBorderInsets(tabPlacement);
3015            switch(tabPlacement) {
3016              case LEFT:
3017                  tabPane.repaint(vpRect.x+vpRect.width, vpRect.y,
3018                                  contentInsets.left, vpRect.height);
3019                  scrollBackwardButton.setEnabled(
3020                          viewRect.y > 0 && leadingTabIndex > 0);
3021                  scrollForwardButton.setEnabled(
3022                          leadingTabIndex < tabCount-1 &&
3023                          viewSize.height-viewRect.y > viewRect.height);
3024                  break;
3025              case RIGHT:
3026                  tabPane.repaint(vpRect.x-contentInsets.right, vpRect.y,
3027                                  contentInsets.right, vpRect.height);
3028                  scrollBackwardButton.setEnabled(
3029                          viewRect.y > 0 && leadingTabIndex > 0);
3030                  scrollForwardButton.setEnabled(
3031                          leadingTabIndex < tabCount-1 &&
3032                          viewSize.height-viewRect.y > viewRect.height);
3033                  break;
3034              case BOTTOM:
3035                  tabPane.repaint(vpRect.x, vpRect.y-contentInsets.bottom,
3036                                  vpRect.width, contentInsets.bottom);
3037                  scrollBackwardButton.setEnabled(
3038                          viewRect.x > 0 && leadingTabIndex > 0);
3039                  scrollForwardButton.setEnabled(
3040                          leadingTabIndex < tabCount-1 &&
3041                          viewSize.width-viewRect.x > viewRect.width);
3042                  break;
3043              case TOP:
3044              default:
3045                  tabPane.repaint(vpRect.x, vpRect.y+vpRect.height,
3046                                  vpRect.width, contentInsets.top);
3047                  scrollBackwardButton.setEnabled(
3048                          viewRect.x > 0 && leadingTabIndex > 0);
3049                  scrollForwardButton.setEnabled(
3050                          leadingTabIndex < tabCount-1 &&
3051                          viewSize.width-viewRect.x > viewRect.width);
3052            }
3053        }
3054
3055        /**
3056         * ActionListener for the scroll buttons.
3057         */

3058        public void actionPerformed(ActionEvent e) {
3059            ActionMap map = tabPane.getActionMap();
3060
3061            if (map != null) {
3062                String JavaDoc actionKey;
3063
3064                if (e.getSource() == scrollForwardButton) {
3065                    actionKey = "scrollTabsForwardAction";
3066                }
3067                else {
3068                    actionKey = "scrollTabsBackwardAction";
3069                }
3070                Action action = map.get(actionKey);
3071
3072                if (action != null && action.isEnabled()) {
3073                    action.actionPerformed(new ActionEvent(tabPane,
3074                        ActionEvent.ACTION_PERFORMED, null, e.getWhen(),
3075                        e.getModifiers()));
3076                }
3077            }
3078        }
3079
3080        public String JavaDoc toString() {
3081            return new String JavaDoc("viewport.viewSize="+viewport.getViewSize()+"\n"+
3082                              "viewport.viewRectangle="+viewport.getViewRect()+"\n"+
3083                              "leadingTabIndex="+leadingTabIndex+"\n"+
3084                              "tabViewPosition="+tabViewPosition);
3085        }
3086
3087    }
3088
3089    private class ScrollableTabViewport extends JViewport implements UIResource {
3090        public ScrollableTabViewport() {
3091            super();
3092            setName("TabbedPane.scrollableViewport");
3093            setScrollMode(SIMPLE_SCROLL_MODE);
3094            setOpaque(tabPane.isOpaque());
3095            Color bgColor = UIManager.getColor("TabbedPane.tabAreaBackground");
3096            if (bgColor == null) {
3097                bgColor = tabPane.getBackground();
3098            }
3099            setBackground(bgColor);
3100        }
3101    }
3102
3103    private class ScrollableTabPanel extends JPanel implements UIResource {
3104        public ScrollableTabPanel() {
3105            setLayout(null);
3106            setOpaque(tabPane.isOpaque());
3107            Color bgColor = UIManager.getColor("TabbedPane.tabAreaBackground");
3108            if (bgColor == null) {
3109                bgColor = tabPane.getBackground();
3110            }
3111            setBackground(bgColor);
3112        }
3113        public void paintComponent(Graphics g) {
3114            super.paintComponent(g);
3115            BasicTabbedPaneUI.this.paintTabArea(g, tabPane.getTabPlacement(),
3116                                                tabPane.getSelectedIndex());
3117            
3118        }
3119    }
3120
3121    private class ScrollableTabButton extends BasicArrowButton JavaDoc implements UIResource,
3122                                                                            SwingConstants {
3123        public ScrollableTabButton(int direction) {
3124            super(direction,
3125                  UIManager.getColor("TabbedPane.selected"),
3126                  UIManager.getColor("TabbedPane.shadow"),
3127                  UIManager.getColor("TabbedPane.darkShadow"),
3128                  UIManager.getColor("TabbedPane.highlight"));
3129        }
3130    }
3131        
3132
3133// Controller: event listeners
3134

3135    private class Handler implements ChangeListener JavaDoc, ContainerListener,
3136                  FocusListener, MouseListener, MouseMotionListener,
3137                  PropertyChangeListener JavaDoc {
3138        //
3139
// PropertyChangeListener
3140
//
3141
public void propertyChange(PropertyChangeEvent JavaDoc e) {
3142            JTabbedPane pane = (JTabbedPane)e.getSource();
3143            String JavaDoc name = e.getPropertyName();
3144            boolean isScrollLayout = scrollableTabLayoutEnabled();
3145            if (name == "mnemonicAt") {
3146                updateMnemonics();
3147                pane.repaint();
3148            }
3149            else if (name == "displayedMnemonicIndexAt") {
3150                pane.repaint();
3151            }
3152            else if (name =="indexForTitle") {
3153                int index = ((Integer JavaDoc)e.getNewValue()).intValue();
3154                String JavaDoc title = tabPane.getTitleAt(index);
3155                if (BasicHTML.isHTMLString(title)) {
3156                    if (htmlViews==null) { // Initialize vector
3157
htmlViews = createHTMLVector();
3158                    } else { // Vector already exists
3159
View JavaDoc v = BasicHTML.createHTMLView(tabPane, title);
3160                        htmlViews.setElementAt(v, index);
3161                    }
3162                } else {
3163                    if (htmlViews != null && htmlViews.elementAt(index) != null) {
3164                        htmlViews.setElementAt(null, index);
3165                    }
3166                }
3167                updateMnemonics();
3168            } else if (name == "tabLayoutPolicy") {
3169                BasicTabbedPaneUI.this.uninstallUI(pane);
3170                BasicTabbedPaneUI.this.installUI(pane);
3171            } else if (name == "tabPlacement") {
3172                if (scrollableTabLayoutEnabled()) {
3173                    tabScroller.createButtons();
3174                }
3175            } else if (name == "opaque" && isScrollLayout) {
3176                boolean newVal = ((Boolean JavaDoc)e.getNewValue()).booleanValue();
3177                tabScroller.tabPanel.setOpaque(newVal);
3178                tabScroller.viewport.setOpaque(newVal);
3179            } else if (name == "background" && isScrollLayout) {
3180                Color newVal = (Color)e.getNewValue();
3181                tabScroller.tabPanel.setBackground(newVal);
3182                tabScroller.viewport.setBackground(newVal);
3183                Color newColor = selectedColor == null ? newVal : selectedColor;
3184                tabScroller.scrollForwardButton.setBackground(newColor);
3185                tabScroller.scrollBackwardButton.setBackground(newColor);
3186            }
3187        }
3188
3189
3190        //
3191
// ChangeListener
3192
//
3193
public void stateChanged(ChangeEvent JavaDoc e) {
3194            JTabbedPane tabPane = (JTabbedPane)e.getSource();
3195            tabPane.revalidate();
3196            tabPane.repaint();
3197
3198            setFocusIndex(tabPane.getSelectedIndex(), false);
3199
3200            if (scrollableTabLayoutEnabled()) {
3201                int index = tabPane.getSelectedIndex();
3202                if (index < rects.length && index != -1) {
3203                    tabScroller.tabPanel.scrollRectToVisible(
3204                            (Rectangle)rects[index].clone());
3205                }
3206            }
3207        }
3208
3209        //
3210
// MouseListener
3211
//
3212
public void mouseClicked(MouseEvent e) {
3213        }
3214
3215        public void mouseReleased(MouseEvent e) {
3216        }
3217
3218        public void mouseEntered(MouseEvent e) {
3219            setRolloverTab(e.getX(), e.getY());
3220        }
3221
3222        public void mouseExited(MouseEvent e) {
3223            setRolloverTab(-1);
3224        }
3225
3226        public void mousePressed(MouseEvent e) {
3227            if (!tabPane.isEnabled()) {
3228                return;
3229            }
3230            int tabIndex = tabForCoordinate(tabPane, e.getX(), e.getY());
3231            if (tabIndex >= 0 && tabPane.isEnabledAt(tabIndex)) {
3232                if (tabIndex != tabPane.getSelectedIndex()) {
3233                    // Clicking on unselected tab, change selection, do NOT
3234
// request focus.
3235
// This will trigger the focusIndex to change by way
3236
// of stateChanged.
3237
tabPane.setSelectedIndex(tabIndex);
3238                }
3239                else if (tabPane.isRequestFocusEnabled()) {
3240                    // Clicking on selected tab, try and give the tabbedpane
3241
// focus. Repaint will occur in focusGained.
3242
tabPane.requestFocus();
3243                }
3244            }
3245        }
3246
3247        //
3248
// MouseMotionListener
3249
//
3250
public void mouseDragged(MouseEvent e) {
3251        }
3252
3253        public void mouseMoved(MouseEvent e) {
3254            setRolloverTab(e.getX(), e.getY());
3255        }
3256
3257        //
3258
// FocusListener
3259
//
3260
public void focusGained(FocusEvent e) {
3261           setFocusIndex(tabPane.getSelectedIndex(), true);
3262        }
3263        public void focusLost(FocusEvent e) {
3264            repaintTab(focusIndex);
3265        }
3266
3267
3268        //
3269
// ContainerListener
3270
//
3271
/* GES 2/3/99:
3272       The container listener code was added to support HTML
3273       rendering of tab titles.
3274       
3275       Ideally, we would be able to listen for property changes
3276       when a tab is added or its text modified. At the moment
3277       there are no such events because the Beans spec doesn't
3278       allow 'indexed' property changes (i.e. tab 2's text changed
3279       from A to B).
3280       
3281       In order to get around this, we listen for tabs to be added
3282       or removed by listening for the container events. we then
3283       queue up a runnable (so the component has a chance to complete
3284       the add) which checks the tab title of the new component to see
3285       if it requires HTML rendering.
3286       
3287       The Views (one per tab title requiring HTML rendering) are
3288       stored in the htmlViews Vector, which is only allocated after
3289       the first time we run into an HTML tab. Note that this vector
3290       is kept in step with the number of pages, and nulls are added
3291       for those pages whose tab title do not require HTML rendering.
3292       
3293       This makes it easy for the paint and layout code to tell
3294       whether to invoke the HTML engine without having to check
3295       the string during time-sensitive operations.
3296       
3297       When we have added a way to listen for tab additions and
3298       changes to tab text, this code should be removed and
3299       replaced by something which uses that. */

3300    
3301        public void componentAdded(ContainerEvent e) {
3302            JTabbedPane tp = (JTabbedPane)e.getContainer();
3303            Component child = e.getChild();
3304            if (child instanceof UIResource) {
3305                return;
3306            }
3307            int index = tp.indexOfComponent(child);
3308            String JavaDoc title = tp.getTitleAt(index);
3309            boolean isHTML = BasicHTML.isHTMLString(title);
3310            if (isHTML) {
3311                if (htmlViews==null) { // Initialize vector
3312
htmlViews = createHTMLVector();
3313                } else { // Vector already exists
3314
View JavaDoc v = BasicHTML.createHTMLView(tp, title);
3315                    htmlViews.insertElementAt(v, index);
3316                }
3317            } else { // Not HTML
3318
if (htmlViews!=null) { // Add placeholder
3319
htmlViews.insertElementAt(null, index);
3320                } // else nada!
3321
}
3322            isRunsDirty = true;
3323            updateMnemonics();
3324        }
3325        public void componentRemoved(ContainerEvent e) {
3326            JTabbedPane tp = (JTabbedPane)e.getContainer();
3327            Component child = e.getChild();
3328            if (child instanceof UIResource) {
3329                return;
3330            }
3331
3332            // NOTE 4/15/2002 (joutwate):
3333
// This fix is implemented using client properties since there is
3334
// currently no IndexPropertyChangeEvent. Once
3335
// IndexPropertyChangeEvents have been added this code should be
3336
// modified to use it.
3337
Integer JavaDoc indexObj =
3338                (Integer JavaDoc)tp.getClientProperty("__index_to_remove__");
3339            if (indexObj != null) {
3340                int index = indexObj.intValue();
3341                if (htmlViews != null && htmlViews.size() > index) {
3342                    htmlViews.removeElementAt(index);
3343                }
3344                tp.putClientProperty("__index_to_remove__", null);
3345            }
3346            isRunsDirty = true;
3347            updateMnemonics();
3348
3349            validateFocusIndex();
3350        }
3351    }
3352
3353    /**
3354     * This class should be treated as a &quot;protected&quot; inner class.
3355     * Instantiate it only within subclasses of BasicTabbedPaneUI.
3356     */

3357    public class PropertyChangeHandler implements PropertyChangeListener JavaDoc {
3358        // NOTE: This class exists only for backward compatability. All
3359
// its functionality has been moved into Handler. If you need to add
3360
// new functionality add it to the Handler, but make sure this
3361
// class calls into the Handler.
3362
public void propertyChange(PropertyChangeEvent JavaDoc e) {
3363            getHandler().propertyChange(e);
3364        }
3365    }
3366
3367    /**
3368     * This class should be treated as a &quot;protected&quot; inner class.
3369     * Instantiate it only within subclasses of BasicTabbedPaneUI.
3370     */

3371    public class TabSelectionHandler implements ChangeListener JavaDoc {
3372        // NOTE: This class exists only for backward compatability. All
3373
// its functionality has been moved into Handler. If you need to add
3374
// new functionality add it to the Handler, but make sure this
3375
// class calls into the Handler.
3376
public void stateChanged(ChangeEvent JavaDoc e) {
3377            getHandler().stateChanged(e);
3378        }
3379    }
3380
3381    /**
3382     * This class should be treated as a &quot;protected&quot; inner class.
3383     * Instantiate it only within subclasses of BasicTabbedPaneUI.
3384     */

3385    public class MouseHandler extends MouseAdapter {
3386        // NOTE: This class exists only for backward compatability. All
3387
// its functionality has been moved into Handler. If you need to add
3388
// new functionality add it to the Handler, but make sure this
3389
// class calls into the Handler.
3390
public void mousePressed(MouseEvent e) {
3391            getHandler().mousePressed(e);
3392        }
3393    }
3394
3395    /**
3396     * This class should be treated as a &quot;protected&quot; inner class.
3397     * Instantiate it only within subclasses of BasicTabbedPaneUI.
3398     */

3399    public class FocusHandler extends FocusAdapter {
3400        // NOTE: This class exists only for backward compatability. All
3401
// its functionality has been moved into Handler. If you need to add
3402
// new functionality add it to the Handler, but make sure this
3403
// class calls into the Handler.
3404
public void focusGained(FocusEvent e) {
3405            getHandler().focusGained(e);
3406        }
3407        public void focusLost(FocusEvent e) {
3408            getHandler().focusLost(e);
3409        }
3410    }
3411
3412    private Vector JavaDoc createHTMLVector() {
3413        Vector JavaDoc htmlViews = new Vector JavaDoc();
3414        int count = tabPane.getTabCount();
3415        if (count>0) {
3416            for (int i=0 ; i<count; i++) {
3417                String JavaDoc title = tabPane.getTitleAt(i);
3418                if (BasicHTML.isHTMLString(title)) {
3419                    htmlViews.addElement(BasicHTML.createHTMLView(tabPane, title));
3420                } else {
3421                    htmlViews.addElement(null);
3422                }
3423            }
3424        }
3425        return htmlViews;
3426    }
3427}
3428
Popular Tags