History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/plaf/basic/BasicTabbedPaneUI.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/swing/plaf/basic/BasicTabbedPaneUI.java+3933−0
@@ -0,0 +1,3933 @@
1+/*
2+ * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package javax.swing.plaf.basic;
27+
28+import sun.swing.SwingUtilities2;
29+
30+import javax.swing.*;
31+import javax.swing.event.*;
32+import javax.swing.plaf.*;
33+import javax.swing.text.View;
34+
35+import java.awt.*;
36+import java.awt.event.*;
37+import java.beans.PropertyChangeListener;
38+import java.beans.PropertyChangeEvent;
39+import java.util.Vector;
40+import java.util.Hashtable;
41+
42+import sun.swing.DefaultLookup;
43+import sun.swing.UIAction;
44+
45+/**
46+ * A Basic L&F implementation of TabbedPaneUI.
47+ *
48+ * @author Amy Fowler
49+ * @author Philip Milne
50+ * @author Steve Wilson
51+ * @author Tom Santos
52+ * @author Dave Moore
53+ */
54+public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants {
55+
56+
57+// Instance variables initialized at installation
58+
59+ protected JTabbedPane tabPane;
60+
61+ protected Color highlight;
62+ protected Color lightHighlight;
63+ protected Color shadow;
64+ protected Color darkShadow;
65+ protected Color focus;
66+ private Color selectedColor;
67+
68+ protected int textIconGap;
69+
70+ protected int tabRunOverlay;
71+
72+ protected Insets tabInsets;
73+ protected Insets selectedTabPadInsets;
74+ protected Insets tabAreaInsets;
75+ protected Insets contentBorderInsets;
76+ private boolean tabsOverlapBorder;
77+ private boolean tabsOpaque = true;
78+ private boolean contentOpaque = true;
79+
80+ /**
81+ * As of Java 2 platform v1.3 this previously undocumented field is no
82+ * longer used.
83+ * Key bindings are now defined by the LookAndFeel, please refer to
84+ * the key bindings specification for further details.
85+ *
86+ * @deprecated As of Java 2 platform v1.3.
87+ */
88+ @Deprecated
89+ protected KeyStroke upKey;
90+ /**
91+ * As of Java 2 platform v1.3 this previously undocumented field is no
92+ * longer used.
93+ * Key bindings are now defined by the LookAndFeel, please refer to
94+ * the key bindings specification for further details.
95+ *
96+ * @deprecated As of Java 2 platform v1.3.
97+ */
98+ @Deprecated
99+ protected KeyStroke downKey;
100+ /**
101+ * As of Java 2 platform v1.3 this previously undocumented field is no
102+ * longer used.
103+ * Key bindings are now defined by the LookAndFeel, please refer to
104+ * the key bindings specification for further details.
105+ *
106+ * @deprecated As of Java 2 platform v1.3.
107+ */
108+ @Deprecated
109+ protected KeyStroke leftKey;
110+ /**
111+ * As of Java 2 platform v1.3 this previously undocumented field is no
112+ * longer used.
113+ * Key bindings are now defined by the LookAndFeel, please refer to
114+ * the key bindings specification for further details.
115+ *
116+ * @deprecated As of Java 2 platform v1.3.
117+ */
118+ @Deprecated
119+ protected KeyStroke rightKey;
120+
121+
122+// Transient variables (recalculated each time TabbedPane is layed out)
123+
124+ protected int tabRuns[] = new int[10];
125+ protected int runCount = 0;
126+ protected int selectedRun = -1;
127+ protected Rectangle rects[] = new Rectangle[0];
128+ protected int maxTabHeight;
129+ protected int maxTabWidth;
130+
131+// Listeners
132+
133+ protected ChangeListener tabChangeListener;
134+ protected PropertyChangeListener propertyChangeListener;
135+ protected MouseListener mouseListener;
136+ protected FocusListener focusListener;
137+
138+// Private instance data
139+
140+ private Insets currentPadInsets = new Insets(0,0,0,0);
141+ private Insets currentTabAreaInsets = new Insets(0,0,0,0);
142+
143+ private Component visibleComponent;
144+ // PENDING(api): See comment for ContainerHandler
145+ private Vector<View> htmlViews;
146+
147+ private Hashtable<Integer, Integer> mnemonicToIndexMap;
148+
149+ /**
150+ * InputMap used for mnemonics. Only non-null if the JTabbedPane has
151+ * mnemonics associated with it. Lazily created in initMnemonics.
152+ */
153+ private InputMap mnemonicInputMap;
154+
155+ // For use when tabLayoutPolicy = SCROLL_TAB_LAYOUT
156+ private ScrollableTabSupport tabScroller;
157+
158+ private TabContainer tabContainer;
159+
160+ /**
161+ * A rectangle used for general layout calculations in order
162+ * to avoid constructing many new Rectangles on the fly.
163+ */
164+ protected transient Rectangle calcRect = new Rectangle(0,0,0,0);
165+
166+ /**
167+ * Tab that has focus.
168+ */
169+ private int focusIndex;
170+
171+ /**
172+ * Combined listeners.
173+ */
174+ private Handler handler;
175+
176+ /**
177+ * Index of the tab the mouse is over.
178+ */
179+ private int rolloverTabIndex;
180+
181+ /**
182+ * This is set to true when a component is added/removed from the tab
183+ * pane and set to false when layout happens. If true it indicates that
184+ * tabRuns is not valid and shouldn't be used.
185+ */
186+ private boolean isRunsDirty;
187+
188+ private boolean calculatedBaseline;
189+ private int baseline;
190+
191+// UI creation
192+
193+ public static ComponentUI createUI(JComponent c) {
194+ return new BasicTabbedPaneUI();
195+ }
196+
197+ static void loadActionMap(LazyActionMap map) {
198+ map.put(new Actions(Actions.NEXT));
199+ map.put(new Actions(Actions.PREVIOUS));
200+ map.put(new Actions(Actions.RIGHT));
201+ map.put(new Actions(Actions.LEFT));
202+ map.put(new Actions(Actions.UP));
203+ map.put(new Actions(Actions.DOWN));
204+ map.put(new Actions(Actions.PAGE_UP));
205+ map.put(new Actions(Actions.PAGE_DOWN));
206+ map.put(new Actions(Actions.REQUEST_FOCUS));
207+ map.put(new Actions(Actions.REQUEST_FOCUS_FOR_VISIBLE));
208+ map.put(new Actions(Actions.SET_SELECTED));
209+ map.put(new Actions(Actions.SELECT_FOCUSED));
210+ map.put(new Actions(Actions.SCROLL_FORWARD));
211+ map.put(new Actions(Actions.SCROLL_BACKWARD));
212+ }
213+
214+// UI Installation/De-installation
215+
216+ public void installUI(JComponent c) {
217+ this.tabPane = (JTabbedPane)c;
218+
219+ calculatedBaseline = false;
220+ rolloverTabIndex = -1;
221+ focusIndex = -1;
222+ c.setLayout(createLayoutManager());
223+ installComponents();
224+ installDefaults();
225+ installListeners();
226+ installKeyboardActions();
227+ }
228+
229+ public void uninstallUI(JComponent c) {
230+ uninstallKeyboardActions();
231+ uninstallListeners();
232+ uninstallDefaults();
233+ uninstallComponents();
234+ c.setLayout(null);
235+
236+ this.tabPane = null;
237+ }
238+
239+ /**
240+ * Invoked by <code>installUI</code> to create
241+ * a layout manager object to manage
242+ * the <code>JTabbedPane</code>.
243+ *
244+ * @return a layout manager object
245+ *
246+ * @see TabbedPaneLayout
247+ * @see javax.swing.JTabbedPane#getTabLayoutPolicy
248+ */
249+ protected LayoutManager createLayoutManager() {
250+ if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
251+ return new TabbedPaneScrollLayout();
252+ } else { /* WRAP_TAB_LAYOUT */
253+ return new TabbedPaneLayout();
254+ }
255+ }
256+
257+ /* In an attempt to preserve backward compatibility for programs
258+ * which have extended BasicTabbedPaneUI to do their own layout, the
259+ * UI uses the installed layoutManager (and not tabLayoutPolicy) to
260+ * determine if scrollTabLayout is enabled.
261+ */
262+ private boolean scrollableTabLayoutEnabled() {
263+ return (tabPane.getLayout() instanceof TabbedPaneScrollLayout);
264+ }
265+
266+ /**
267+ * Creates and installs any required subcomponents for the JTabbedPane.
268+ * Invoked by installUI.
269+ *
270+ * @since 1.4
271+ */
272+ protected void installComponents() {
273+ if (scrollableTabLayoutEnabled()) {
274+ if (tabScroller == null) {
275+ tabScroller = new ScrollableTabSupport(tabPane.getTabPlacement());
276+ tabPane.add(tabScroller.viewport);
277+ }
278+ }
279+ installTabContainer();
280+ }
281+
282+ private void installTabContainer() {
283+ for (int i = 0; i < tabPane.getTabCount(); i++) {
284+ Component tabComponent = tabPane.getTabComponentAt(i);
285+ if (tabComponent != null) {
286+ if(tabContainer == null) {
287+ tabContainer = new TabContainer();
288+ }
289+ tabContainer.add(tabComponent);
290+ }
291+ }
292+ if(tabContainer == null) {
293+ return;
294+ }
295+ if (scrollableTabLayoutEnabled()) {
296+ tabScroller.tabPanel.add(tabContainer);
297+ } else {
298+ tabPane.add(tabContainer);
299+ }
300+ }
301+
302+ /**
303+ * Creates and returns a JButton that will provide the user
304+ * with a way to scroll the tabs in a particular direction. The
305+ * returned JButton must be instance of UIResource.
306+ *
307+ * @param direction One of the SwingConstants constants:
308+ * SOUTH, NORTH, EAST or WEST
309+ * @return Widget for user to
310+ * @see javax.swing.JTabbedPane#setTabPlacement
311+ * @see javax.swing.SwingConstants
312+ * @throws IllegalArgumentException if direction is not one of
313+ * NORTH, SOUTH, EAST or WEST
314+ * @since 1.5
315+ */
316+ protected JButton createScrollButton(int direction) {
317+ if (direction != SOUTH && direction != NORTH && direction != EAST &&
318+ direction != WEST) {
319+ throw new IllegalArgumentException("Direction must be one of: " +
320+ "SOUTH, NORTH, EAST or WEST");
321+ }
322+ return new ScrollableTabButton(direction);
323+ }
324+
325+ /**
326+ * Removes any installed subcomponents from the JTabbedPane.
327+ * Invoked by uninstallUI.
328+ *
329+ * @since 1.4
330+ */
331+ protected void uninstallComponents() {
332+ uninstallTabContainer();
333+ if (scrollableTabLayoutEnabled()) {
334+ tabPane.remove(tabScroller.viewport);
335+ tabPane.remove(tabScroller.scrollForwardButton);
336+ tabPane.remove(tabScroller.scrollBackwardButton);
337+ tabScroller = null;
338+ }
339+ }
340+
341+ private void uninstallTabContainer() {
342+ if(tabContainer == null) {
343+ return;
344+ }
345+ // Remove all the tabComponents, making sure not to notify
346+ // the tabbedpane.
347+ tabContainer.notifyTabbedPane = false;
348+ tabContainer.removeAll();
349+ if(scrollableTabLayoutEnabled()) {
350+ tabContainer.remove(tabScroller.croppedEdge);
351+ tabScroller.tabPanel.remove(tabContainer);
352+ } else {
353+ tabPane.remove(tabContainer);
354+ }
355+ tabContainer = null;
356+ }
357+
358+ protected void installDefaults() {
359+ LookAndFeel.installColorsAndFont(tabPane, "TabbedPane.background",
360+ "TabbedPane.foreground", "TabbedPane.font");
361+ highlight = UIManager.getColor("TabbedPane.light");
362+ lightHighlight = UIManager.getColor("TabbedPane.highlight");
363+ shadow = UIManager.getColor("TabbedPane.shadow");
364+ darkShadow = UIManager.getColor("TabbedPane.darkShadow");
365+ focus = UIManager.getColor("TabbedPane.focus");
366+ selectedColor = UIManager.getColor("TabbedPane.selected");
367+
368+ textIconGap = UIManager.getInt("TabbedPane.textIconGap");
369+ tabInsets = UIManager.getInsets("TabbedPane.tabInsets");
370+ selectedTabPadInsets = UIManager.getInsets("TabbedPane.selectedTabPadInsets");
371+ tabAreaInsets = UIManager.getInsets("TabbedPane.tabAreaInsets");
372+ tabsOverlapBorder = UIManager.getBoolean("TabbedPane.tabsOverlapBorder");
373+ contentBorderInsets = UIManager.getInsets("TabbedPane.contentBorderInsets");
374+ tabRunOverlay = UIManager.getInt("TabbedPane.tabRunOverlay");
375+ tabsOpaque = UIManager.getBoolean("TabbedPane.tabsOpaque");
376+ contentOpaque = UIManager.getBoolean("TabbedPane.contentOpaque");
377+ Object opaque = UIManager.get("TabbedPane.opaque");
378+ if (opaque == null) {
379+ opaque = Boolean.FALSE;
380+ }
381+ LookAndFeel.installProperty(tabPane, "opaque", opaque);
382+
383+ // Fix for 6711145 BasicTabbedPanuUI should not throw a NPE if these
384+ // keys are missing. So we are setting them to there default values here
385+ // if the keys are missing.
386+ if (tabInsets == null) tabInsets = new Insets(0,4,1,4);
387+ if (selectedTabPadInsets == null) selectedTabPadInsets = new Insets(2,2,2,1);
388+ if (tabAreaInsets == null) tabAreaInsets = new Insets(3,2,0,2);
389+ if (contentBorderInsets == null) contentBorderInsets = new Insets(2,2,3,3);
390+ }
391+
392+ protected void uninstallDefaults() {
393+ highlight = null;
394+ lightHighlight = null;
395+ shadow = null;
396+ darkShadow = null;
397+ focus = null;
398+ tabInsets = null;
399+ selectedTabPadInsets = null;
400+ tabAreaInsets = null;
401+ contentBorderInsets = null;
402+ }
403+
404+ protected void installListeners() {
405+ if ((propertyChangeListener = createPropertyChangeListener()) != null) {
406+ tabPane.addPropertyChangeListener(propertyChangeListener);
407+ }
408+ if ((tabChangeListener = createChangeListener()) != null) {
409+ tabPane.addChangeListener(tabChangeListener);
410+ }
411+ if ((mouseListener = createMouseListener()) != null) {
412+ tabPane.addMouseListener(mouseListener);
413+ }
414+ tabPane.addMouseMotionListener(getHandler());
415+ if ((focusListener = createFocusListener()) != null) {
416+ tabPane.addFocusListener(focusListener);
417+ }
418+ tabPane.addContainerListener(getHandler());
419+ if (tabPane.getTabCount()>0) {
420+ htmlViews = createHTMLVector();
421+ }
422+ }
423+
424+ protected void uninstallListeners() {
425+ if (mouseListener != null) {
426+ tabPane.removeMouseListener(mouseListener);
427+ mouseListener = null;
428+ }
429+ tabPane.removeMouseMotionListener(getHandler());
430+ if (focusListener != null) {
431+ tabPane.removeFocusListener(focusListener);
432+ focusListener = null;
433+ }
434+
435+ tabPane.removeContainerListener(getHandler());
436+ if (htmlViews!=null) {
437+ htmlViews.removeAllElements();
438+ htmlViews = null;
439+ }
440+ if (tabChangeListener != null) {
441+ tabPane.removeChangeListener(tabChangeListener);
442+ tabChangeListener = null;
443+ }
444+ if (propertyChangeListener != null) {
445+ tabPane.removePropertyChangeListener(propertyChangeListener);
446+ propertyChangeListener = null;
447+ }
448+ handler = null;
449+ }
450+
451+ protected MouseListener createMouseListener() {
452+ return getHandler();
453+ }
454+
455+ protected FocusListener createFocusListener() {
456+ return getHandler();
457+ }
458+
459+ protected ChangeListener createChangeListener() {
460+ return getHandler();
461+ }
462+
463+ protected PropertyChangeListener createPropertyChangeListener() {
464+ return getHandler();
465+ }
466+
467+ private Handler getHandler() {
468+ if (handler == null) {
469+ handler = new Handler();
470+ }
471+ return handler;
472+ }
473+
474+ protected void installKeyboardActions() {
475+ InputMap km = getInputMap(JComponent.
476+ WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
477+
478+ SwingUtilities.replaceUIInputMap(tabPane, JComponent.
479+ WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
480+ km);
481+ km = getInputMap(JComponent.WHEN_FOCUSED);
482+ SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, km);
483+
484+ LazyActionMap.installLazyActionMap(tabPane, BasicTabbedPaneUI.class,
485+ "TabbedPane.actionMap");
486+ updateMnemonics();
487+ }
488+
489+ InputMap getInputMap(int condition) {
490+ if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
491+ return (InputMap)DefaultLookup.get(tabPane, this,
492+ "TabbedPane.ancestorInputMap");
493+ }
494+ else if (condition == JComponent.WHEN_FOCUSED) {
495+ return (InputMap)DefaultLookup.get(tabPane, this,
496+ "TabbedPane.focusInputMap");
497+ }
498+ return null;
499+ }
500+
501+ protected void uninstallKeyboardActions() {
502+ SwingUtilities.replaceUIActionMap(tabPane, null);
503+ SwingUtilities.replaceUIInputMap(tabPane, JComponent.
504+ WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
505+ null);
506+ SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED,
507+ null);
508+ SwingUtilities.replaceUIInputMap(tabPane,
509+ JComponent.WHEN_IN_FOCUSED_WINDOW,
510+ null);
511+ mnemonicToIndexMap = null;
512+ mnemonicInputMap = null;
513+ }
514+
515+ /**
516+ * Reloads the mnemonics. This should be invoked when a memonic changes,
517+ * when the title of a mnemonic changes, or when tabs are added/removed.
518+ */
519+ private void updateMnemonics() {
520+ resetMnemonics();
521+ for (int counter = tabPane.getTabCount() - 1; counter >= 0;
522+ counter--) {
523+ int mnemonic = tabPane.getMnemonicAt(counter);
524+
525+ if (mnemonic > 0) {
526+ addMnemonic(counter, mnemonic);
527+ }
528+ }
529+ }
530+
531+ /**
532+ * Resets the mnemonics bindings to an empty state.
533+ */
534+ private void resetMnemonics() {
535+ if (mnemonicToIndexMap != null) {
536+ mnemonicToIndexMap.clear();
537+ mnemonicInputMap.clear();
538+ }
539+ }
540+
541+ /**
542+ * Adds the specified mnemonic at the specified index.
543+ */
544+ private void addMnemonic(int index, int mnemonic) {
545+ if (mnemonicToIndexMap == null) {
546+ initMnemonics();
547+ }
548+ mnemonicInputMap.put(KeyStroke.getKeyStroke(mnemonic, Event.ALT_MASK),
549+ "setSelectedIndex");
550+ mnemonicToIndexMap.put(Integer.valueOf(mnemonic), Integer.valueOf(index));
551+ }
552+
553+ /**
554+ * Installs the state needed for mnemonics.
555+ */
556+ private void initMnemonics() {
557+ mnemonicToIndexMap = new Hashtable<Integer, Integer>();
558+ mnemonicInputMap = new ComponentInputMapUIResource(tabPane);
559+ mnemonicInputMap.setParent(SwingUtilities.getUIInputMap(tabPane,
560+ JComponent.WHEN_IN_FOCUSED_WINDOW));
561+ SwingUtilities.replaceUIInputMap(tabPane,
562+ JComponent.WHEN_IN_FOCUSED_WINDOW,
563+ mnemonicInputMap);
564+ }
565+
566+ /**
567+ * Sets the tab the mouse is over by location. This is a cover method
568+ * for <code>setRolloverTab(tabForCoordinate(x, y, false))</code>.
569+ */
570+ private void setRolloverTab(int x, int y) {
571+ // NOTE:
572+ // This calls in with false otherwise it could trigger a validate,
573+ // which should NOT happen if the user is only dragging the
574+ // mouse around.
575+ setRolloverTab(tabForCoordinate(tabPane, x, y, false));
576+ }
577+
578+ /**
579+ * Sets the tab the mouse is currently over to <code>index</code>.
580+ * <code>index</code> will be -1 if the mouse is no longer over any
581+ * tab. No checking is done to ensure the passed in index identifies a
582+ * valid tab.
583+ *
584+ * @param index Index of the tab the mouse is over.
585+ * @since 1.5
586+ */
587+ protected void setRolloverTab(int index) {
588+ rolloverTabIndex = index;
589+ }
590+
591+ /**
592+ * Returns the tab the mouse is currently over, or {@code -1} if the mouse is no
593+ * longer over any tab.
594+ *
595+ * @return the tab the mouse is currently over, or {@code -1} if the mouse is no
596+ * longer over any tab
597+ * @since 1.5
598+ */
599+ protected int getRolloverTab() {
600+ return rolloverTabIndex;
601+ }
602+
603+ public Dimension getMinimumSize(JComponent c) {
604+ // Default to LayoutManager's minimumLayoutSize
605+ return null;
606+ }
607+
608+ public Dimension getMaximumSize(JComponent c) {
609+ // Default to LayoutManager's maximumLayoutSize
610+ return null;
611+ }
612+
613+ /**
614+ * Returns the baseline.
615+ *
616+ * @throws NullPointerException {@inheritDoc}
617+ * @throws IllegalArgumentException {@inheritDoc}
618+ * @see javax.swing.JComponent#getBaseline(int, int)
619+ * @since 1.6
620+ */
621+ public int getBaseline(JComponent c, int width, int height) {
622+ super.getBaseline(c, width, height);
623+ int baseline = calculateBaselineIfNecessary();
624+ if (baseline != -1) {
625+ int placement = tabPane.getTabPlacement();
626+ Insets insets = tabPane.getInsets();
627+ Insets tabAreaInsets = getTabAreaInsets(placement);
628+ switch(placement) {
629+ case JTabbedPane.TOP:
630+ baseline += insets.top + tabAreaInsets.top;
631+ return baseline;
632+ case JTabbedPane.BOTTOM:
633+ baseline = height - insets.bottom -
634+ tabAreaInsets.bottom - maxTabHeight + baseline;
635+ return baseline;
636+ case JTabbedPane.LEFT:
637+ case JTabbedPane.RIGHT:
638+ baseline += insets.top + tabAreaInsets.top;
639+ return baseline;
640+ }
641+ }
642+ return -1;
643+ }
644+
645+ /**
646+ * Returns an enum indicating how the baseline of the component
647+ * changes as the size changes.
648+ *
649+ * @throws NullPointerException {@inheritDoc}
650+ * @see javax.swing.JComponent#getBaseline(int, int)
651+ * @since 1.6
652+ */
653+ public Component.BaselineResizeBehavior getBaselineResizeBehavior(
654+ JComponent c) {
655+ super.getBaselineResizeBehavior(c);
656+ switch(tabPane.getTabPlacement()) {
657+ case JTabbedPane.LEFT:
658+ case JTabbedPane.RIGHT:
659+ case JTabbedPane.TOP:
660+ return Component.BaselineResizeBehavior.CONSTANT_ASCENT;
661+ case JTabbedPane.BOTTOM:
662+ return Component.BaselineResizeBehavior.CONSTANT_DESCENT;
663+ }
664+ return Component.BaselineResizeBehavior.OTHER;
665+ }
666+
667+ /**
668+ * Returns the baseline for the specified tab.
669+ *
670+ * @param tab index of tab to get baseline for
671+ * @exception IndexOutOfBoundsException if index is out of range
672+ * (index < 0 || index >= tab count)
673+ * @return baseline or a value &lt; 0 indicating there is no reasonable
674+ * baseline
675+ * @since 1.6
676+ */
677+ protected int getBaseline(int tab) {
678+ if (tabPane.getTabComponentAt(tab) != null) {
679+ int offset = getBaselineOffset();
680+ if (offset != 0) {
681+ // The offset is not applied to the tab component, and so
682+ // in general we can't get good alignment like with components
683+ // in the tab.
684+ return -1;
685+ }
686+ Component c = tabPane.getTabComponentAt(tab);
687+ Dimension pref = c.getPreferredSize();
688+ Insets tabInsets = getTabInsets(tabPane.getTabPlacement(), tab);
689+ int cellHeight = maxTabHeight - tabInsets.top - tabInsets.bottom;
690+ return c.getBaseline(pref.width, pref.height) +
691+ (cellHeight - pref.height) / 2 + tabInsets.top;
692+ }
693+ else {
694+ View view = getTextViewForTab(tab);
695+ if (view != null) {
696+ int viewHeight = (int)view.getPreferredSpan(View.Y_AXIS);
697+ int baseline = BasicHTML.getHTMLBaseline(
698+ view, (int)view.getPreferredSpan(View.X_AXIS), viewHeight);
699+ if (baseline >= 0) {
700+ return maxTabHeight / 2 - viewHeight / 2 + baseline +
701+ getBaselineOffset();
702+ }
703+ return -1;
704+ }
705+ }
706+ FontMetrics metrics = getFontMetrics();
707+ int fontHeight = metrics.getHeight();
708+ int fontBaseline = metrics.getAscent();
709+ return maxTabHeight / 2 - fontHeight / 2 + fontBaseline +
710+ getBaselineOffset();
711+ }
712+
713+ /**
714+ * Returns the amount the baseline is offset by. This is typically
715+ * the same as <code>getTabLabelShiftY</code>.
716+ *
717+ * @return amount to offset the baseline by
718+ * @since 1.6
719+ */
720+ protected int getBaselineOffset() {
721+ switch(tabPane.getTabPlacement()) {
722+ case JTabbedPane.TOP:
723+ if (tabPane.getTabCount() > 1) {
724+ return 1;
725+ }
726+ else {
727+ return -1;
728+ }
729+ case JTabbedPane.BOTTOM:
730+ if (tabPane.getTabCount() > 1) {
731+ return -1;
732+ }
733+ else {
734+ return 1;
735+ }
736+ default: // RIGHT|LEFT
737+ return (maxTabHeight % 2);
738+ }
739+ }
740+
741+ private int calculateBaselineIfNecessary() {
742+ if (!calculatedBaseline) {
743+ calculatedBaseline = true;
744+ baseline = -1;
745+ if (tabPane.getTabCount() > 0) {
746+ calculateBaseline();
747+ }
748+ }
749+ return baseline;
750+ }
751+
752+ private void calculateBaseline() {
753+ int tabCount = tabPane.getTabCount();
754+ int tabPlacement = tabPane.getTabPlacement();
755+ maxTabHeight = calculateMaxTabHeight(tabPlacement);
756+ baseline = getBaseline(0);
757+ if (isHorizontalTabPlacement()) {
758+ for(int i = 1; i < tabCount; i++) {
759+ if (getBaseline(i) != baseline) {
760+ baseline = -1;
761+ break;
762+ }
763+ }
764+ }
765+ else {
766+ // left/right, tabs may be different sizes.
767+ FontMetrics fontMetrics = getFontMetrics();
768+ int fontHeight = fontMetrics.getHeight();
769+ int height = calculateTabHeight(tabPlacement, 0, fontHeight);
770+ for(int i = 1; i < tabCount; i++) {
771+ int newHeight = calculateTabHeight(tabPlacement, i,fontHeight);
772+ if (height != newHeight) {
773+ // assume different baseline
774+ baseline = -1;
775+ break;
776+ }
777+ }
778+ }
779+ }
780+
781+// UI Rendering
782+
783+ public void paint(Graphics g, JComponent c) {
784+ int selectedIndex = tabPane.getSelectedIndex();
785+ int tabPlacement = tabPane.getTabPlacement();
786+
787+ ensureCurrentLayout();
788+
789+ // Paint content border and tab area
790+ if (tabsOverlapBorder) {
791+ paintContentBorder(g, tabPlacement, selectedIndex);
792+ }
793+ // If scrollable tabs are enabled, the tab area will be
794+ // painted by the scrollable tab panel instead.
795+ //
796+ if (!scrollableTabLayoutEnabled()) { // WRAP_TAB_LAYOUT
797+ paintTabArea(g, tabPlacement, selectedIndex);
798+ }
799+ if (!tabsOverlapBorder) {
800+ paintContentBorder(g, tabPlacement, selectedIndex);
801+ }
802+ }
803+
804+ /**
805+ * Paints the tabs in the tab area.
806+ * Invoked by paint().
807+ * The graphics parameter must be a valid <code>Graphics</code>
808+ * object. Tab placement may be either:
809+ * <code>JTabbedPane.TOP</code>, <code>JTabbedPane.BOTTOM</code>,
810+ * <code>JTabbedPane.LEFT</code>, or <code>JTabbedPane.RIGHT</code>.
811+ * The selected index must be a valid tabbed pane tab index (0 to
812+ * tab count - 1, inclusive) or -1 if no tab is currently selected.
813+ * The handling of invalid parameters is unspecified.
814+ *
815+ * @param g the graphics object to use for rendering
816+ * @param tabPlacement the placement for the tabs within the JTabbedPane
817+ * @param selectedIndex the tab index of the selected component
818+ *
819+ * @since 1.4
820+ */
821+ protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex) {
822+ int tabCount = tabPane.getTabCount();
823+
824+ Rectangle iconRect = new Rectangle(),
825+ textRect = new Rectangle();
826+ Rectangle clipRect = g.getClipBounds();
827+
828+ // Paint tabRuns of tabs from back to front
829+ for (int i = runCount - 1; i >= 0; i--) {
830+ int start = tabRuns[i];
831+ int next = tabRuns[(i == runCount - 1)? 0 : i + 1];
832+ int end = (next != 0? next - 1: tabCount - 1);
833+ for (int j = start; j <= end; j++) {
834+ if (j != selectedIndex && rects[j].intersects(clipRect)) {
835+ paintTab(g, tabPlacement, rects, j, iconRect, textRect);
836+ }
837+ }
838+ }
839+
840+ // Paint selected tab if its in the front run
841+ // since it may overlap other tabs
842+ if (selectedIndex >= 0 && rects[selectedIndex].intersects(clipRect)) {
843+ paintTab(g, tabPlacement, rects, selectedIndex, iconRect, textRect);
844+ }
845+ }
846+
847+ protected void paintTab(Graphics g, int tabPlacement,
848+ Rectangle[] rects, int tabIndex,
849+ Rectangle iconRect, Rectangle textRect) {
850+ Rectangle tabRect = rects[tabIndex];
851+ int selectedIndex = tabPane.getSelectedIndex();
852+ boolean isSelected = selectedIndex == tabIndex;
853+
854+ if (tabsOpaque || tabPane.isOpaque()) {
855+ paintTabBackground(g, tabPlacement, tabIndex, tabRect.x, tabRect.y,
856+ tabRect.width, tabRect.height, isSelected);
857+ }
858+
859+ paintTabBorder(g, tabPlacement, tabIndex, tabRect.x, tabRect.y,
860+ tabRect.width, tabRect.height, isSelected);
861+
862+ String title = tabPane.getTitleAt(tabIndex);
863+ Font font = tabPane.getFont();
864+ FontMetrics metrics = SwingUtilities2.getFontMetrics(tabPane, g, font);
865+ Icon icon = getIconForTab(tabIndex);
866+
867+ layoutLabel(tabPlacement, metrics, tabIndex, title, icon,
868+ tabRect, iconRect, textRect, isSelected);
869+
870+ if (tabPane.getTabComponentAt(tabIndex) == null) {
871+ String clippedTitle = title;
872+
873+ if (scrollableTabLayoutEnabled() && tabScroller.croppedEdge.isParamsSet() &&
874+ tabScroller.croppedEdge.getTabIndex() == tabIndex && isHorizontalTabPlacement()) {
875+ int availTextWidth = tabScroller.croppedEdge.getCropline() -
876+ (textRect.x - tabRect.x) - tabScroller.croppedEdge.getCroppedSideWidth();
877+ clippedTitle = SwingUtilities2.clipStringIfNecessary(null, metrics, title, availTextWidth);
878+ }
879+
880+ paintText(g, tabPlacement, font, metrics,
881+ tabIndex, clippedTitle, textRect, isSelected);
882+
883+ paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected);
884+ }
885+ paintFocusIndicator(g, tabPlacement, rects, tabIndex,
886+ iconRect, textRect, isSelected);
887+ }
888+
889+ private boolean isHorizontalTabPlacement() {
890+ return tabPane.getTabPlacement() == TOP || tabPane.getTabPlacement() == BOTTOM;
891+ }
892+
893+ /* This method will create and return a polygon shape for the given tab rectangle
894+ * which has been cropped at the specified cropline with a torn edge visual.
895+ * e.g. A "File" tab which has cropped been cropped just after the "i":
896+ * -------------
897+ * | ..... |
898+ * | . |
899+ * | ... . |
900+ * | . . |
901+ * | . . |
902+ * | . . |
903+ * --------------
904+ *
905+ * The x, y arrays below define the pattern used to create a "torn" edge
906+ * segment which is repeated to fill the edge of the tab.
907+ * For tabs placed on TOP and BOTTOM, this righthand torn edge is created by
908+ * line segments which are defined by coordinates obtained by
909+ * subtracting xCropLen[i] from (tab.x + tab.width) and adding yCroplen[i]
910+ * to (tab.y).
911+ * For tabs placed on LEFT or RIGHT, the bottom torn edge is created by
912+ * subtracting xCropLen[i] from (tab.y + tab.height) and adding yCropLen[i]
913+ * to (tab.x).
914+ */
915+ private static int xCropLen[] = {1,1,0,0,1,1,2,2};
916+ private static int yCropLen[] = {0,3,3,6,6,9,9,12};
917+ private static final int CROP_SEGMENT = 12;
918+
919+ private static Polygon createCroppedTabShape(int tabPlacement, Rectangle tabRect, int cropline) {
920+ int rlen;
921+ int start;
922+ int end;
923+ int ostart;
924+
925+ switch(tabPlacement) {
926+ case LEFT:
927+ case RIGHT:
928+ rlen = tabRect.width;
929+ start = tabRect.x;
930+ end = tabRect.x + tabRect.width;
931+ ostart = tabRect.y + tabRect.height;
932+ break;
933+ case TOP:
934+ case BOTTOM:
935+ default:
936+ rlen = tabRect.height;
937+ start = tabRect.y;
938+ end = tabRect.y + tabRect.height;
939+ ostart = tabRect.x + tabRect.width;
940+ }
941+ int rcnt = rlen/CROP_SEGMENT;
942+ if (rlen%CROP_SEGMENT > 0) {
943+ rcnt++;
944+ }
945+ int npts = 2 + (rcnt*8);
946+ int xp[] = new int[npts];
947+ int yp[] = new int[npts];
948+ int pcnt = 0;
949+
950+ xp[pcnt] = ostart;
951+ yp[pcnt++] = end;
952+ xp[pcnt] = ostart;
953+ yp[pcnt++] = start;
954+ for(int i = 0; i < rcnt; i++) {
955+ for(int j = 0; j < xCropLen.length; j++) {
956+ xp[pcnt] = cropline - xCropLen[j];
957+ yp[pcnt] = start + (i*CROP_SEGMENT) + yCropLen[j];
958+ if (yp[pcnt] >= end) {
959+ yp[pcnt] = end;
960+ pcnt++;
961+ break;
962+ }
963+ pcnt++;
964+ }
965+ }
966+ if (tabPlacement == JTabbedPane.TOP || tabPlacement == JTabbedPane.BOTTOM) {
967+ return new Polygon(xp, yp, pcnt);
968+
969+ } else { // LEFT or RIGHT
970+ return new Polygon(yp, xp, pcnt);
971+ }
972+ }
973+
974+ /* If tabLayoutPolicy == SCROLL_TAB_LAYOUT, this method will paint an edge
975+ * indicating the tab is cropped in the viewport display
976+ */
977+ private void paintCroppedTabEdge(Graphics g) {
978+ int tabIndex = tabScroller.croppedEdge.getTabIndex();
979+ int cropline = tabScroller.croppedEdge.getCropline();
980+ int x,y;
981+ switch(tabPane.getTabPlacement()) {
982+ case LEFT:
983+ case RIGHT:
984+ x = rects[tabIndex].x;
985+ y = cropline;
986+ int xx = x;
987+ g.setColor(shadow);
988+ while(xx <= x+rects[tabIndex].width) {
989+ for (int i=0; i < xCropLen.length; i+=2) {
990+ g.drawLine(xx+yCropLen[i],y-xCropLen[i],
991+ xx+yCropLen[i+1]-1,y-xCropLen[i+1]);
992+ }
993+ xx+=CROP_SEGMENT;
994+ }
995+ break;
996+ case TOP:
997+ case BOTTOM:
998+ default:
999+ x = cropline;
1000+ y = rects[tabIndex].y;
1001+ int yy = y;
1002+ g.setColor(shadow);
1003+ while(yy <= y+rects[tabIndex].height) {
1004+ for (int i=0; i < xCropLen.length; i+=2) {
1005+ g.drawLine(x-xCropLen[i],yy+yCropLen[i],
1006+ x-xCropLen[i+1],yy+yCropLen[i+1]-1);
1007+ }
1008+ yy+=CROP_SEGMENT;
1009+ }
1010+ }
1011+ }
1012+
1013+ protected void layoutLabel(int tabPlacement,
1014+ FontMetrics metrics, int tabIndex,
1015+ String title, Icon icon,
1016+ Rectangle tabRect, Rectangle iconRect,
1017+ Rectangle textRect, boolean isSelected ) {
1018+ textRect.x = textRect.y = iconRect.x = iconRect.y = 0;
1019+
1020+ View v = getTextViewForTab(tabIndex);
1021+ if (v != null) {
1022+ tabPane.putClientProperty("html", v);
1023+ }
1024+
1025+ SwingUtilities.layoutCompoundLabel(tabPane,
1026+ metrics, title, icon,
1027+ SwingUtilities.CENTER,
1028+ SwingUtilities.CENTER,
1029+ SwingUtilities.CENTER,
1030+ SwingUtilities.TRAILING,
1031+ tabRect,
1032+ iconRect,
1033+ textRect,
1034+ textIconGap);
1035+
1036+ tabPane.putClientProperty("html", null);
1037+
1038+ int xNudge = getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
1039+ int yNudge = getTabLabelShiftY(tabPlacement, tabIndex, isSelected);
1040+ iconRect.x += xNudge;
1041+ iconRect.y += yNudge;
1042+ textRect.x += xNudge;
1043+ textRect.y += yNudge;
1044+ }
1045+
1046+ protected void paintIcon(Graphics g, int tabPlacement,
1047+ int tabIndex, Icon icon, Rectangle iconRect,
1048+ boolean isSelected ) {
1049+ if (icon != null) {
1050+ icon.paintIcon(tabPane, g, iconRect.x, iconRect.y);
1051+ }
1052+ }
1053+
1054+ protected void paintText(Graphics g, int tabPlacement,
1055+ Font font, FontMetrics metrics, int tabIndex,
1056+ String title, Rectangle textRect,
1057+ boolean isSelected) {
1058+
1059+ g.setFont(font);
1060+
1061+ View v = getTextViewForTab(tabIndex);
1062+ if (v != null) {
1063+ // html
1064+ v.paint(g, textRect);
1065+ } else {
1066+ // plain text
1067+ int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
1068+
1069+ if (tabPane.isEnabled() && tabPane.isEnabledAt(tabIndex)) {
1070+ Color fg = tabPane.getForegroundAt(tabIndex);
1071+ if (isSelected && (fg instanceof UIResource)) {
1072+ Color selectedFG = UIManager.getColor(
1073+ "TabbedPane.selectedForeground");
1074+ if (selectedFG != null) {
1075+ fg = selectedFG;
1076+ }
1077+ }
1078+ g.setColor(fg);
1079+ SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
1080+ title, mnemIndex,
1081+ textRect.x, textRect.y + metrics.getAscent());
1082+
1083+ } else { // tab disabled
1084+ g.setColor(tabPane.getBackgroundAt(tabIndex).brighter());
1085+ SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
1086+ title, mnemIndex,
1087+ textRect.x, textRect.y + metrics.getAscent());
1088+ g.setColor(tabPane.getBackgroundAt(tabIndex).darker());
1089+ SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
1090+ title, mnemIndex,
1091+ textRect.x - 1, textRect.y + metrics.getAscent() - 1);
1092+
1093+ }
1094+ }
1095+ }
1096+
1097+
1098+ protected int getTabLabelShiftX(int tabPlacement, int tabIndex, boolean isSelected) {
1099+ Rectangle tabRect = rects[tabIndex];
1100+ String propKey = (isSelected ? "selectedLabelShift" : "labelShift");
1101+ int nudge = DefaultLookup.getInt(
1102+ tabPane, this, "TabbedPane." + propKey, 1);
1103+
1104+ switch (tabPlacement) {
1105+ case LEFT:
1106+ return nudge;
1107+ case RIGHT:
1108+ return -nudge;
1109+ case BOTTOM:
1110+ case TOP:
1111+ default:
1112+ return tabRect.width % 2;
1113+ }
1114+ }
1115+
1116+ protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) {
1117+ Rectangle tabRect = rects[tabIndex];
1118+ String propKey = (isSelected ? "selectedLabelShift" : "labelShift");
1119+ int nudge = DefaultLookup.getInt(
1120+ tabPane, this, "TabbedPane." + propKey, 1);
1121+
1122+ switch (tabPlacement) {
1123+ case BOTTOM:
1124+ return -nudge;
1125+ case LEFT:
1126+ case RIGHT:
1127+ return tabRect.height % 2;
1128+ case TOP:
1129+ default:
1130+ return nudge;
1131+ }
1132+ }
1133+
1134+ protected void paintFocusIndicator(Graphics g, int tabPlacement,
1135+ Rectangle[] rects, int tabIndex,
1136+ Rectangle iconRect, Rectangle textRect,
1137+ boolean isSelected) {
1138+ Rectangle tabRect = rects[tabIndex];
1139+ if (tabPane.hasFocus() && isSelected) {
1140+ int x, y, w, h;
1141+ g.setColor(focus);
1142+ switch(tabPlacement) {
1143+ case LEFT:
1144+ x = tabRect.x + 3;
1145+ y = tabRect.y + 3;
1146+ w = tabRect.width - 5;
1147+ h = tabRect.height - 6;
1148+ break;
1149+ case RIGHT:
1150+ x = tabRect.x + 2;
1151+ y = tabRect.y + 3;
1152+ w = tabRect.width - 5;
1153+ h = tabRect.height - 6;
1154+ break;
1155+ case BOTTOM:
1156+ x = tabRect.x + 3;
1157+ y = tabRect.y + 2;
1158+ w = tabRect.width - 6;
1159+ h = tabRect.height - 5;
1160+ break;
1161+ case TOP:
1162+ default:
1163+ x = tabRect.x + 3;
1164+ y = tabRect.y + 3;
1165+ w = tabRect.width - 6;
1166+ h = tabRect.height - 5;
1167+ }
1168+ BasicGraphicsUtils.drawDashedRect(g, x, y, w, h);
1169+ }
1170+ }
1171+
1172+ /**
1173+ * this function draws the border around each tab
1174+ * note that this function does now draw the background of the tab.
1175+ * that is done elsewhere
1176+ */
1177+ protected void paintTabBorder(Graphics g, int tabPlacement,
1178+ int tabIndex,
1179+ int x, int y, int w, int h,
1180+ boolean isSelected ) {
1181+ g.setColor(lightHighlight);
1182+
1183+ switch (tabPlacement) {
1184+ case LEFT:
1185+ g.drawLine(x+1, y+h-2, x+1, y+h-2); // bottom-left highlight
1186+ g.drawLine(x, y+2, x, y+h-3); // left highlight
1187+ g.drawLine(x+1, y+1, x+1, y+1); // top-left highlight
1188+ g.drawLine(x+2, y, x+w-1, y); // top highlight
1189+
1190+ g.setColor(shadow);
1191+ g.drawLine(x+2, y+h-2, x+w-1, y+h-2); // bottom shadow
1192+
1193+ g.setColor(darkShadow);
1194+ g.drawLine(x+2, y+h-1, x+w-1, y+h-1); // bottom dark shadow
1195+ break;
1196+ case RIGHT:
1197+ g.drawLine(x, y, x+w-3, y); // top highlight
1198+
1199+ g.setColor(shadow);
1200+ g.drawLine(x, y+h-2, x+w-3, y+h-2); // bottom shadow
1201+ g.drawLine(x+w-2, y+2, x+w-2, y+h-3); // right shadow
1202+
1203+ g.setColor(darkShadow);
1204+ g.drawLine(x+w-2, y+1, x+w-2, y+1); // top-right dark shadow
1205+ g.drawLine(x+w-2, y+h-2, x+w-2, y+h-2); // bottom-right dark shadow
1206+ g.drawLine(x+w-1, y+2, x+w-1, y+h-3); // right dark shadow
1207+ g.drawLine(x, y+h-1, x+w-3, y+h-1); // bottom dark shadow
1208+ break;
1209+ case BOTTOM:
1210+ g.drawLine(x, y, x, y+h-3); // left highlight
1211+ g.drawLine(x+1, y+h-2, x+1, y+h-2); // bottom-left highlight
1212+
1213+ g.setColor(shadow);
1214+ g.drawLine(x+2, y+h-2, x+w-3, y+h-2); // bottom shadow
1215+ g.drawLine(x+w-2, y, x+w-2, y+h-3); // right shadow
1216+
1217+ g.setColor(darkShadow);
1218+ g.drawLine(x+2, y+h-1, x+w-3, y+h-1); // bottom dark shadow
1219+ g.drawLine(x+w-2, y+h-2, x+w-2, y+h-2); // bottom-right dark shadow
1220+ g.drawLine(x+w-1, y, x+w-1, y+h-3); // right dark shadow
1221+ break;
1222+ case TOP:
1223+ default:
1224+ g.drawLine(x, y+2, x, y+h-1); // left highlight
1225+ g.drawLine(x+1, y+1, x+1, y+1); // top-left highlight
1226+ g.drawLine(x+2, y, x+w-3, y); // top highlight
1227+
1228+ g.setColor(shadow);
1229+ g.drawLine(x+w-2, y+2, x+w-2, y+h-1); // right shadow
1230+
1231+ g.setColor(darkShadow);
1232+ g.drawLine(x+w-1, y+2, x+w-1, y+h-1); // right dark-shadow
1233+ g.drawLine(x+w-2, y+1, x+w-2, y+1); // top-right shadow
1234+ }
1235+ }
1236+
1237+ protected void paintTabBackground(Graphics g, int tabPlacement,
1238+ int tabIndex,
1239+ int x, int y, int w, int h,
1240+ boolean isSelected ) {
1241+ g.setColor(!isSelected || selectedColor == null?
1242+ tabPane.getBackgroundAt(tabIndex) : selectedColor);
1243+ switch(tabPlacement) {
1244+ case LEFT:
1245+ g.fillRect(x+1, y+1, w-1, h-3);
1246+ break;
1247+ case RIGHT:
1248+ g.fillRect(x, y+1, w-2, h-3);
1249+ break;
1250+ case BOTTOM:
1251+ g.fillRect(x+1, y, w-3, h-1);
1252+ break;
1253+ case TOP:
1254+ default:
1255+ g.fillRect(x+1, y+1, w-3, h-1);
1256+ }
1257+ }
1258+
1259+ protected void paintContentBorder(Graphics g, int tabPlacement, int selectedIndex) {
1260+ int width = tabPane.getWidth();
1261+ int height = tabPane.getHeight();
1262+ Insets insets = tabPane.getInsets();
1263+ Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
1264+
1265+ int x = insets.left;
1266+ int y = insets.top;
1267+ int w = width - insets.right - insets.left;
1268+ int h = height - insets.top - insets.bottom;
1269+
1270+ switch(tabPlacement) {
1271+ case LEFT:
1272+ x += calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
1273+ if (tabsOverlapBorder) {
1274+ x -= tabAreaInsets.right;
1275+ }
1276+ w -= (x - insets.left);
1277+ break;
1278+ case RIGHT:
1279+ w -= calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
1280+ if (tabsOverlapBorder) {
1281+ w += tabAreaInsets.left;
1282+ }
1283+ break;
1284+ case BOTTOM:
1285+ h -= calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
1286+ if (tabsOverlapBorder) {
1287+ h += tabAreaInsets.top;
1288+ }
1289+ break;
1290+ case TOP:
1291+ default:
1292+ y += calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
1293+ if (tabsOverlapBorder) {
1294+ y -= tabAreaInsets.bottom;
1295+ }
1296+ h -= (y - insets.top);
1297+ }
1298+
1299+ if ( tabPane.getTabCount() > 0 && (contentOpaque || tabPane.isOpaque()) ) {
1300+ // Fill region behind content area
1301+ Color color = UIManager.getColor("TabbedPane.contentAreaColor");
1302+ if (color != null) {
1303+ g.setColor(color);
1304+ }
1305+ else if ( selectedColor == null || selectedIndex == -1 ) {
1306+ g.setColor(tabPane.getBackground());
1307+ }
1308+ else {
1309+ g.setColor(selectedColor);
1310+ }
1311+ g.fillRect(x,y,w,h);
1312+ }
1313+
1314+ paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h);
1315+ paintContentBorderLeftEdge(g, tabPlacement, selectedIndex, x, y, w, h);
1316+ paintContentBorderBottomEdge(g, tabPlacement, selectedIndex, x, y, w, h);
1317+ paintContentBorderRightEdge(g, tabPlacement, selectedIndex, x, y, w, h);
1318+
1319+ }
1320+
1321+ protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
1322+ int selectedIndex,
1323+ int x, int y, int w, int h) {
1324+ Rectangle selRect = selectedIndex < 0? null :
1325+ getTabBounds(selectedIndex, calcRect);
1326+
1327+ g.setColor(lightHighlight);
1328+
1329+ // Draw unbroken line if tabs are not on TOP, OR
1330+ // selected tab is not in run adjacent to content, OR
1331+ // selected tab is not visible (SCROLL_TAB_LAYOUT)
1332+ //
1333+ if (tabPlacement != TOP || selectedIndex < 0 ||
1334+ (selRect.y + selRect.height + 1 < y) ||
1335+ (selRect.x < x || selRect.x > x + w)) {
1336+ g.drawLine(x, y, x+w-2, y);
1337+ } else {
1338+ // Break line to show visual connection to selected tab
1339+ g.drawLine(x, y, selRect.x - 1, y);
1340+ if (selRect.x + selRect.width < x + w - 2) {
1341+ g.drawLine(selRect.x + selRect.width, y,
1342+ x+w-2, y);
1343+ } else {
1344+ g.setColor(shadow);
1345+ g.drawLine(x+w-2, y, x+w-2, y);
1346+ }
1347+ }
1348+ }
1349+
1350+ protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
1351+ int selectedIndex,
1352+ int x, int y, int w, int h) {
1353+ Rectangle selRect = selectedIndex < 0? null :
1354+ getTabBounds(selectedIndex, calcRect);
1355+
1356+ g.setColor(lightHighlight);
1357+
1358+ // Draw unbroken line if tabs are not on LEFT, OR
1359+ // selected tab is not in run adjacent to content, OR
1360+ // selected tab is not visible (SCROLL_TAB_LAYOUT)
1361+ //
1362+ if (tabPlacement != LEFT || selectedIndex < 0 ||
1363+ (selRect.x + selRect.width + 1 < x) ||
1364+ (selRect.y < y || selRect.y > y + h)) {
1365+ g.drawLine(x, y, x, y+h-2);
1366+ } else {
1367+ // Break line to show visual connection to selected tab
1368+ g.drawLine(x, y, x, selRect.y - 1);
1369+ if (selRect.y + selRect.height < y + h - 2) {
1370+ g.drawLine(x, selRect.y + selRect.height,
1371+ x, y+h-2);
1372+ }
1373+ }
1374+ }
1375+
1376+ protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
1377+ int selectedIndex,
1378+ int x, int y, int w, int h) {
1379+ Rectangle selRect = selectedIndex < 0? null :
1380+ getTabBounds(selectedIndex, calcRect);
1381+
1382+ g.setColor(shadow);
1383+
1384+ // Draw unbroken line if tabs are not on BOTTOM, OR
1385+ // selected tab is not in run adjacent to content, OR
1386+ // selected tab is not visible (SCROLL_TAB_LAYOUT)
1387+ //
1388+ if (tabPlacement != BOTTOM || selectedIndex < 0 ||
1389+ (selRect.y - 1 > h) ||
1390+ (selRect.x < x || selRect.x > x + w)) {
1391+ g.drawLine(x+1, y+h-2, x+w-2, y+h-2);
1392+ g.setColor(darkShadow);
1393+ g.drawLine(x, y+h-1, x+w-1, y+h-1);
1394+ } else {
1395+ // Break line to show visual connection to selected tab
1396+ g.drawLine(x+1, y+h-2, selRect.x - 1, y+h-2);
1397+ g.setColor(darkShadow);
1398+ g.drawLine(x, y+h-1, selRect.x - 1, y+h-1);
1399+ if (selRect.x + selRect.width < x + w - 2) {
1400+ g.setColor(shadow);
1401+ g.drawLine(selRect.x + selRect.width, y+h-2, x+w-2, y+h-2);
1402+ g.setColor(darkShadow);
1403+ g.drawLine(selRect.x + selRect.width, y+h-1, x+w-1, y+h-1);
1404+ }
1405+ }
1406+
1407+ }
1408+
1409+ protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
1410+ int selectedIndex,
1411+ int x, int y, int w, int h) {
1412+ Rectangle selRect = selectedIndex < 0? null :
1413+ getTabBounds(selectedIndex, calcRect);
1414+
1415+ g.setColor(shadow);
1416+
1417+ // Draw unbroken line if tabs are not on RIGHT, OR
1418+ // selected tab is not in run adjacent to content, OR
1419+ // selected tab is not visible (SCROLL_TAB_LAYOUT)
1420+ //
1421+ if (tabPlacement != RIGHT || selectedIndex < 0 ||
1422+ (selRect.x - 1 > w) ||
1423+ (selRect.y < y || selRect.y > y + h)) {
1424+ g.drawLine(x+w-2, y+1, x+w-2, y+h-3);
1425+ g.setColor(darkShadow);
1426+ g.drawLine(x+w-1, y, x+w-1, y+h-1);
1427+ } else {
1428+ // Break line to show visual connection to selected tab
1429+ g.drawLine(x+w-2, y+1, x+w-2, selRect.y - 1);
1430+ g.setColor(darkShadow);
1431+ g.drawLine(x+w-1, y, x+w-1, selRect.y - 1);
1432+
1433+ if (selRect.y + selRect.height < y + h - 2) {
1434+ g.setColor(shadow);
1435+ g.drawLine(x+w-2, selRect.y + selRect.height,
1436+ x+w-2, y+h-2);
1437+ g.setColor(darkShadow);
1438+ g.drawLine(x+w-1, selRect.y + selRect.height,
1439+ x+w-1, y+h-2);
1440+ }
1441+ }
1442+ }
1443+
1444+ private void ensureCurrentLayout() {
1445+ if (!tabPane.isValid()) {
1446+ tabPane.validate();
1447+ }
1448+ /* If tabPane doesn't have a peer yet, the validate() call will
1449+ * silently fail. We handle that by forcing a layout if tabPane
1450+ * is still invalid. See bug 4237677.
1451+ */
1452+ if (!tabPane.isValid()) {
1453+ TabbedPaneLayout layout = (TabbedPaneLayout)tabPane.getLayout();
1454+ layout.calculateLayoutInfo();
1455+ }
1456+ }
1457+
1458+
1459+// TabbedPaneUI methods
1460+
1461+ /**
1462+ * Returns the bounds of the specified tab index. The bounds are
1463+ * with respect to the JTabbedPane's coordinate space.
1464+ */
1465+ public Rectangle getTabBounds(JTabbedPane pane, int i) {
1466+ ensureCurrentLayout();
1467+ Rectangle tabRect = new Rectangle();
1468+ return getTabBounds(i, tabRect);
1469+ }
1470+
1471+ public int getTabRunCount(JTabbedPane pane) {
1472+ ensureCurrentLayout();
1473+ return runCount;
1474+ }
1475+
1476+ /**
1477+ * Returns the tab index which intersects the specified point
1478+ * in the JTabbedPane's coordinate space.
1479+ */
1480+ public int tabForCoordinate(JTabbedPane pane, int x, int y) {
1481+ return tabForCoordinate(pane, x, y, true);
1482+ }
1483+
1484+ private int tabForCoordinate(JTabbedPane pane, int x, int y,
1485+ boolean validateIfNecessary) {
1486+ if (validateIfNecessary) {
1487+ ensureCurrentLayout();
1488+ }
1489+ if (isRunsDirty) {
1490+ // We didn't recalculate the layout, runs and tabCount may not
1491+ // line up, bail.
1492+ return -1;
1493+ }
1494+ Point p = new Point(x, y);
1495+
1496+ if (scrollableTabLayoutEnabled()) {
1497+ translatePointToTabPanel(x, y, p);
1498+ Rectangle viewRect = tabScroller.viewport.getViewRect();
1499+ if (!viewRect.contains(p)) {
1500+ return -1;
1501+ }
1502+ }
1503+ int tabCount = tabPane.getTabCount();
1504+ for (int i = 0; i < tabCount; i++) {
1505+ if (rects[i].contains(p.x, p.y)) {
1506+ return i;
1507+ }
1508+ }
1509+ return -1;
1510+ }
1511+
1512+ /**
1513+ * Returns the bounds of the specified tab in the coordinate space
1514+ * of the JTabbedPane component. This is required because the tab rects
1515+ * are by default defined in the coordinate space of the component where
1516+ * they are rendered, which could be the JTabbedPane
1517+ * (for WRAP_TAB_LAYOUT) or a ScrollableTabPanel (SCROLL_TAB_LAYOUT).
1518+ * This method should be used whenever the tab rectangle must be relative
1519+ * to the JTabbedPane itself and the result should be placed in a
1520+ * designated Rectangle object (rather than instantiating and returning
1521+ * a new Rectangle each time). The tab index parameter must be a valid
1522+ * tabbed pane tab index (0 to tab count - 1, inclusive). The destination
1523+ * rectangle parameter must be a valid <code>Rectangle</code> instance.
1524+ * The handling of invalid parameters is unspecified.
1525+ *
1526+ * @param tabIndex the index of the tab
1527+ * @param dest the rectangle where the result should be placed
1528+ * @return the resulting rectangle
1529+ *
1530+ * @since 1.4
1531+ */
1532+ protected Rectangle getTabBounds(int tabIndex, Rectangle dest) {
1533+ dest.width = rects[tabIndex].width;
1534+ dest.height = rects[tabIndex].height;
1535+
1536+ if (scrollableTabLayoutEnabled()) { // SCROLL_TAB_LAYOUT
1537+ // Need to translate coordinates based on viewport location &
1538+ // view position
1539+ Point vpp = tabScroller.viewport.getLocation();
1540+ Point viewp = tabScroller.viewport.getViewPosition();
1541+ dest.x = rects[tabIndex].x + vpp.x - viewp.x;
1542+ dest.y = rects[tabIndex].y + vpp.y - viewp.y;
1543+
1544+ } else { // WRAP_TAB_LAYOUT
1545+ dest.x = rects[tabIndex].x;
1546+ dest.y = rects[tabIndex].y;
1547+ }
1548+ return dest;
1549+ }
1550+
1551+ /**
1552+ * Returns the index of the tab closest to the passed in location, note
1553+ * that the returned tab may not contain the location x,y.
1554+ */
1555+ private int getClosestTab(int x, int y) {
1556+ int min = 0;
1557+ int tabCount = Math.min(rects.length, tabPane.getTabCount());
1558+ int max = tabCount;
1559+ int tabPlacement = tabPane.getTabPlacement();
1560+ boolean useX = (tabPlacement == TOP || tabPlacement == BOTTOM);
1561+ int want = (useX) ? x : y;
1562+
1563+ while (min != max) {
1564+ int current = (max + min) / 2;
1565+ int minLoc;
1566+ int maxLoc;
1567+
1568+ if (useX) {
1569+ minLoc = rects[current].x;
1570+ maxLoc = minLoc + rects[current].width;
1571+ }
1572+ else {
1573+ minLoc = rects[current].y;
1574+ maxLoc = minLoc + rects[current].height;
1575+ }
1576+ if (want < minLoc) {
1577+ max = current;
1578+ if (min == max) {
1579+ return Math.max(0, current - 1);
1580+ }
1581+ }
1582+ else if (want >= maxLoc) {
1583+ min = current;
1584+ if (max - min <= 1) {
1585+ return Math.max(current + 1, tabCount - 1);
1586+ }
1587+ }
1588+ else {
1589+ return current;
1590+ }
1591+ }
1592+ return min;
1593+ }
1594+
1595+ /**
1596+ * Returns a point which is translated from the specified point in the
1597+ * JTabbedPane's coordinate space to the coordinate space of the
1598+ * ScrollableTabPanel. This is used for SCROLL_TAB_LAYOUT ONLY.
1599+ */
1600+ private Point translatePointToTabPanel(int srcx, int srcy, Point dest) {
1601+ Point vpp = tabScroller.viewport.getLocation();
1602+ Point viewp = tabScroller.viewport.getViewPosition();
1603+ dest.x = srcx - vpp.x + viewp.x;
1604+ dest.y = srcy - vpp.y + viewp.y;
1605+ return dest;
1606+ }
1607+
1608+// BasicTabbedPaneUI methods
1609+
1610+ protected Component getVisibleComponent() {
1611+ return visibleComponent;
1612+ }
1613+
1614+ protected void setVisibleComponent(Component component) {
1615+ if (visibleComponent != null
1616+ && visibleComponent != component
1617+ && visibleComponent.getParent() == tabPane
1618+ && visibleComponent.isVisible()) {
1619+
1620+ visibleComponent.setVisible(false);
1621+ }
1622+ if (component != null && !component.isVisible()) {
1623+ component.setVisible(true);
1624+ }
1625+ visibleComponent = component;
1626+ }
1627+
1628+ protected void assureRectsCreated(int tabCount) {
1629+ int rectArrayLen = rects.length;
1630+ if (tabCount != rectArrayLen ) {
1631+ Rectangle[] tempRectArray = new Rectangle[tabCount];
1632+ System.arraycopy(rects, 0, tempRectArray, 0,
1633+ Math.min(rectArrayLen, tabCount));
1634+ rects = tempRectArray;
1635+ for (int rectIndex = rectArrayLen; rectIndex < tabCount; rectIndex++) {
1636+ rects[rectIndex] = new Rectangle();
1637+ }
1638+ }
1639+
1640+ }
1641+
1642+ protected void expandTabRunsArray() {
1643+ int rectLen = tabRuns.length;
1644+ int[] newArray = new int[rectLen+10];
1645+ System.arraycopy(tabRuns, 0, newArray, 0, runCount);
1646+ tabRuns = newArray;
1647+ }
1648+
1649+ protected int getRunForTab(int tabCount, int tabIndex) {
1650+ for (int i = 0; i < runCount; i++) {
1651+ int first = tabRuns[i];
1652+ int last = lastTabInRun(tabCount, i);
1653+ if (tabIndex >= first && tabIndex <= last) {
1654+ return i;
1655+ }
1656+ }
1657+ return 0;
1658+ }
1659+
1660+ protected int lastTabInRun(int tabCount, int run) {
1661+ if (runCount == 1) {
1662+ return tabCount - 1;
1663+ }
1664+ int nextRun = (run == runCount - 1? 0 : run + 1);
1665+ if (tabRuns[nextRun] == 0) {
1666+ return tabCount - 1;
1667+ }
1668+ return tabRuns[nextRun]-1;
1669+ }
1670+
1671+ protected int getTabRunOverlay(int tabPlacement) {
1672+ return tabRunOverlay;
1673+ }
1674+
1675+ protected int getTabRunIndent(int tabPlacement, int run) {
1676+ return 0;
1677+ }
1678+
1679+ protected boolean shouldPadTabRun(int tabPlacement, int run) {
1680+ return runCount > 1;
1681+ }
1682+
1683+ protected boolean shouldRotateTabRuns(int tabPlacement) {
1684+ return true;
1685+ }
1686+
1687+ protected Icon getIconForTab(int tabIndex) {
1688+ return (!tabPane.isEnabled() || !tabPane.isEnabledAt(tabIndex))?
1689+ tabPane.getDisabledIconAt(tabIndex) : tabPane.getIconAt(tabIndex);
1690+ }
1691+
1692+ /**
1693+ * Returns the text View object required to render stylized text (HTML) for
1694+ * the specified tab or null if no specialized text rendering is needed
1695+ * for this tab. This is provided to support html rendering inside tabs.
1696+ *
1697+ * @param tabIndex the index of the tab
1698+ * @return the text view to render the tab's text or null if no
1699+ * specialized rendering is required
1700+ *
1701+ * @since 1.4
1702+ */
1703+ protected View getTextViewForTab(int tabIndex) {
1704+ if (htmlViews != null) {
1705+ return htmlViews.elementAt(tabIndex);
1706+ }
1707+ return null;
1708+ }
1709+
1710+ protected int calculateTabHeight(int tabPlacement, int tabIndex, int fontHeight) {
1711+ int height = 0;
1712+ Component c = tabPane.getTabComponentAt(tabIndex);
1713+ if (c != null) {
1714+ height = c.getPreferredSize().height;
1715+ } else {
1716+ View v = getTextViewForTab(tabIndex);
1717+ if (v != null) {
1718+ // html
1719+ height += (int) v.getPreferredSpan(View.Y_AXIS);
1720+ } else {
1721+ // plain text
1722+ height += fontHeight;
1723+ }
1724+ Icon icon = getIconForTab(tabIndex);
1725+
1726+ if (icon != null) {
1727+ height = Math.max(height, icon.getIconHeight());
1728+ }
1729+ }
1730+ Insets tabInsets = getTabInsets(tabPlacement, tabIndex);
1731+ height += tabInsets.top + tabInsets.bottom + 2;
1732+ return height;
1733+ }
1734+
1735+ protected int calculateMaxTabHeight(int tabPlacement) {
1736+ FontMetrics metrics = getFontMetrics();
1737+ int tabCount = tabPane.getTabCount();
1738+ int result = 0;
1739+ int fontHeight = metrics.getHeight();
1740+ for(int i = 0; i < tabCount; i++) {
1741+ result = Math.max(calculateTabHeight(tabPlacement, i, fontHeight), result);
1742+ }
1743+ return result;
1744+ }
1745+
1746+ protected int calculateTabWidth(int tabPlacement, int tabIndex, FontMetrics metrics) {
1747+ Insets tabInsets = getTabInsets(tabPlacement, tabIndex);
1748+ int width = tabInsets.left + tabInsets.right + 3;
1749+ Component tabComponent = tabPane.getTabComponentAt(tabIndex);
1750+ if (tabComponent != null) {
1751+ width += tabComponent.getPreferredSize().width;
1752+ } else {
1753+ Icon icon = getIconForTab(tabIndex);
1754+ if (icon != null) {
1755+ width += icon.getIconWidth() + textIconGap;
1756+ }
1757+ View v = getTextViewForTab(tabIndex);
1758+ if (v != null) {
1759+ // html
1760+ width += (int) v.getPreferredSpan(View.X_AXIS);
1761+ } else {
1762+ // plain text
1763+ String title = tabPane.getTitleAt(tabIndex);
1764+ width += SwingUtilities2.stringWidth(tabPane, metrics, title);
1765+ }
1766+ }
1767+ return width;
1768+ }
1769+
1770+ protected int calculateMaxTabWidth(int tabPlacement) {
1771+ FontMetrics metrics = getFontMetrics();
1772+ int tabCount = tabPane.getTabCount();
1773+ int result = 0;
1774+ for(int i = 0; i < tabCount; i++) {
1775+ result = Math.max(calculateTabWidth(tabPlacement, i, metrics), result);
1776+ }
1777+ return result;
1778+ }
1779+
1780+ protected int calculateTabAreaHeight(int tabPlacement, int horizRunCount, int maxTabHeight) {
1781+ Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
1782+ int tabRunOverlay = getTabRunOverlay(tabPlacement);
1783+ return (horizRunCount > 0?
1784+ horizRunCount * (maxTabHeight-tabRunOverlay) + tabRunOverlay +
1785+ tabAreaInsets.top + tabAreaInsets.bottom :
1786+ 0);
1787+ }
1788+
1789+ protected int calculateTabAreaWidth(int tabPlacement, int vertRunCount, int maxTabWidth) {
1790+ Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
1791+ int tabRunOverlay = getTabRunOverlay(tabPlacement);
1792+ return (vertRunCount > 0?
1793+ vertRunCount * (maxTabWidth-tabRunOverlay) + tabRunOverlay +
1794+ tabAreaInsets.left + tabAreaInsets.right :
1795+ 0);
1796+ }
1797+
1798+ protected Insets getTabInsets(int tabPlacement, int tabIndex) {
1799+ return tabInsets;
1800+ }
1801+
1802+ protected Insets getSelectedTabPadInsets(int tabPlacement) {
1803+ rotateInsets(selectedTabPadInsets, currentPadInsets, tabPlacement);
1804+ return currentPadInsets;
1805+ }
1806+
1807+ protected Insets getTabAreaInsets(int tabPlacement) {
1808+ rotateInsets(tabAreaInsets, currentTabAreaInsets, tabPlacement);
1809+ return currentTabAreaInsets;
1810+ }
1811+
1812+ protected Insets getContentBorderInsets(int tabPlacement) {
1813+ return contentBorderInsets;
1814+ }
1815+
1816+ protected FontMetrics getFontMetrics() {
1817+ Font font = tabPane.getFont();
1818+ return tabPane.getFontMetrics(font);
1819+ }
1820+
1821+
1822+// Tab Navigation methods
1823+
1824+ protected void navigateSelectedTab(int direction) {
1825+ int tabPlacement = tabPane.getTabPlacement();
1826+ int current = DefaultLookup.getBoolean(tabPane, this,
1827+ "TabbedPane.selectionFollowsFocus", true) ?
1828+ tabPane.getSelectedIndex() : getFocusIndex();
1829+ int tabCount = tabPane.getTabCount();
1830+ boolean leftToRight = BasicGraphicsUtils.isLeftToRight(tabPane);
1831+
1832+ // If we have no tabs then don't navigate.
1833+ if (tabCount <= 0) {
1834+ return;
1835+ }
1836+
1837+ int offset;
1838+ switch(tabPlacement) {
1839+ case LEFT:
1840+ case RIGHT:
1841+ switch(direction) {
1842+ case NEXT:
1843+ selectNextTab(current);
1844+ break;
1845+ case PREVIOUS:
1846+ selectPreviousTab(current);
1847+ break;
1848+ case NORTH:
1849+ selectPreviousTabInRun(current);
1850+ break;
1851+ case SOUTH:
1852+ selectNextTabInRun(current);
1853+ break;
1854+ case WEST:
1855+ offset = getTabRunOffset(tabPlacement, tabCount, current, false);
1856+ selectAdjacentRunTab(tabPlacement, current, offset);
1857+ break;
1858+ case EAST:
1859+ offset = getTabRunOffset(tabPlacement, tabCount, current, true);
1860+ selectAdjacentRunTab(tabPlacement, current, offset);
1861+ break;
1862+ default:
1863+ }
1864+ break;
1865+ case BOTTOM:
1866+ case TOP:
1867+ default:
1868+ switch(direction) {
1869+ case NEXT:
1870+ selectNextTab(current);
1871+ break;
1872+ case PREVIOUS:
1873+ selectPreviousTab(current);
1874+ break;
1875+ case NORTH:
1876+ offset = getTabRunOffset(tabPlacement, tabCount, current, false);
1877+ selectAdjacentRunTab(tabPlacement, current, offset);
1878+ break;
1879+ case SOUTH:
1880+ offset = getTabRunOffset(tabPlacement, tabCount, current, true);
1881+ selectAdjacentRunTab(tabPlacement, current, offset);
1882+ break;
1883+ case EAST:
1884+ if (leftToRight) {
1885+ selectNextTabInRun(current);
1886+ } else {
1887+ selectPreviousTabInRun(current);
1888+ }
1889+ break;
1890+ case WEST:
1891+ if (leftToRight) {
1892+ selectPreviousTabInRun(current);
1893+ } else {
1894+ selectNextTabInRun(current);
1895+ }
1896+ break;
1897+ default:
1898+ }
1899+ }
1900+ }
1901+
1902+ protected void selectNextTabInRun(int current) {
1903+ int tabCount = tabPane.getTabCount();
1904+ int tabIndex = getNextTabIndexInRun(tabCount, current);
1905+
1906+ while(tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
1907+ tabIndex = getNextTabIndexInRun(tabCount, tabIndex);
1908+ }
1909+ navigateTo(tabIndex);
1910+ }
1911+
1912+ protected void selectPreviousTabInRun(int current) {
1913+ int tabCount = tabPane.getTabCount();
1914+ int tabIndex = getPreviousTabIndexInRun(tabCount, current);
1915+
1916+ while(tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
1917+ tabIndex = getPreviousTabIndexInRun(tabCount, tabIndex);
1918+ }
1919+ navigateTo(tabIndex);
1920+ }
1921+
1922+ protected void selectNextTab(int current) {
1923+ int tabIndex = getNextTabIndex(current);
1924+
1925+ while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
1926+ tabIndex = getNextTabIndex(tabIndex);
1927+ }
1928+ navigateTo(tabIndex);
1929+ }
1930+
1931+ protected void selectPreviousTab(int current) {
1932+ int tabIndex = getPreviousTabIndex(current);
1933+
1934+ while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
1935+ tabIndex = getPreviousTabIndex(tabIndex);
1936+ }
1937+ navigateTo(tabIndex);
1938+ }
1939+
1940+ protected void selectAdjacentRunTab(int tabPlacement,
1941+ int tabIndex, int offset) {
1942+ if ( runCount < 2 ) {
1943+ return;
1944+ }
1945+ int newIndex;
1946+ Rectangle r = rects[tabIndex];
1947+ switch(tabPlacement) {
1948+ case LEFT:
1949+ case RIGHT:
1950+ newIndex = tabForCoordinate(tabPane, r.x + r.width/2 + offset,
1951+ r.y + r.height/2);
1952+ break;
1953+ case BOTTOM:
1954+ case TOP:
1955+ default:
1956+ newIndex = tabForCoordinate(tabPane, r.x + r.width/2,
1957+ r.y + r.height/2 + offset);
1958+ }
1959+ if (newIndex != -1) {
1960+ while (!tabPane.isEnabledAt(newIndex) && newIndex != tabIndex) {
1961+ newIndex = getNextTabIndex(newIndex);
1962+ }
1963+ navigateTo(newIndex);
1964+ }
1965+ }
1966+
1967+ private void navigateTo(int index) {
1968+ if (DefaultLookup.getBoolean(tabPane, this,
1969+ "TabbedPane.selectionFollowsFocus", true)) {
1970+ tabPane.setSelectedIndex(index);
1971+ } else {
1972+ // Just move focus (not selection)
1973+ setFocusIndex(index, true);
1974+ }
1975+ }
1976+
1977+ void setFocusIndex(int index, boolean repaint) {
1978+ if (repaint && !isRunsDirty) {
1979+ repaintTab(focusIndex);
1980+ focusIndex = index;
1981+ repaintTab(focusIndex);
1982+ }
1983+ else {
1984+ focusIndex = index;
1985+ }
1986+ }
1987+
1988+ /**
1989+ * Repaints the specified tab.
1990+ */
1991+ private void repaintTab(int index) {
1992+ // If we're not valid that means we will shortly be validated and
1993+ // painted, which means we don't have to do anything here.
1994+ if (!isRunsDirty && index >= 0 && index < tabPane.getTabCount()) {
1995+ tabPane.repaint(getTabBounds(tabPane, index));
1996+ }
1997+ }
1998+
1999+ /**
2000+ * Makes sure the focusIndex is valid.
2001+ */
2002+ private void validateFocusIndex() {
2003+ if (focusIndex >= tabPane.getTabCount()) {
2004+ setFocusIndex(tabPane.getSelectedIndex(), false);
2005+ }
2006+ }
2007+
2008+ /**
2009+ * Returns the index of the tab that has focus.
2010+ *
2011+ * @return index of tab that has focus
2012+ * @since 1.5
2013+ */
2014+ protected int getFocusIndex() {
2015+ return focusIndex;
2016+ }
2017+
2018+ protected int getTabRunOffset(int tabPlacement, int tabCount,
2019+ int tabIndex, boolean forward) {
2020+ int run = getRunForTab(tabCount, tabIndex);
2021+ int offset;
2022+ switch(tabPlacement) {
2023+ case LEFT: {
2024+ if (run == 0) {
2025+ offset = (forward?
2026+ -(calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth) :
2027+ -maxTabWidth);
2028+
2029+ } else if (run == runCount - 1) {
2030+ offset = (forward?
2031+ maxTabWidth :
2032+ calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth);
2033+ } else {
2034+ offset = (forward? maxTabWidth : -maxTabWidth);
2035+ }
2036+ break;
2037+ }
2038+ case RIGHT: {
2039+ if (run == 0) {
2040+ offset = (forward?
2041+ maxTabWidth :
2042+ calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth);
2043+ } else if (run == runCount - 1) {
2044+ offset = (forward?
2045+ -(calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth)-maxTabWidth) :
2046+ -maxTabWidth);
2047+ } else {
2048+ offset = (forward? maxTabWidth : -maxTabWidth);
2049+ }
2050+ break;
2051+ }
2052+ case BOTTOM: {
2053+ if (run == 0) {
2054+ offset = (forward?
2055+ maxTabHeight :
2056+ calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight);
2057+ } else if (run == runCount - 1) {
2058+ offset = (forward?
2059+ -(calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight) :
2060+ -maxTabHeight);
2061+ } else {
2062+ offset = (forward? maxTabHeight : -maxTabHeight);
2063+ }
2064+ break;
2065+ }
2066+ case TOP:
2067+ default: {
2068+ if (run == 0) {
2069+ offset = (forward?
2070+ -(calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight) :
2071+ -maxTabHeight);
2072+ } else if (run == runCount - 1) {
2073+ offset = (forward?
2074+ maxTabHeight :
2075+ calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)-maxTabHeight);
2076+ } else {
2077+ offset = (forward? maxTabHeight : -maxTabHeight);
2078+ }
2079+ }
2080+ }
2081+ return offset;
2082+ }
2083+
2084+ protected int getPreviousTabIndex(int base) {
2085+ int tabIndex = (base - 1 >= 0? base - 1 : tabPane.getTabCount() - 1);
2086+ return (tabIndex >= 0? tabIndex : 0);
2087+ }
2088+
2089+ protected int getNextTabIndex(int base) {
2090+ return (base+1)%tabPane.getTabCount();
2091+ }
2092+
2093+ protected int getNextTabIndexInRun(int tabCount, int base) {
2094+ if (runCount < 2) {
2095+ return getNextTabIndex(base);
2096+ }
2097+ int currentRun = getRunForTab(tabCount, base);
2098+ int next = getNextTabIndex(base);
2099+ if (next == tabRuns[getNextTabRun(currentRun)]) {
2100+ return tabRuns[currentRun];
2101+ }
2102+ return next;
2103+ }
2104+
2105+ protected int getPreviousTabIndexInRun(int tabCount, int base) {
2106+ if (runCount < 2) {
2107+ return getPreviousTabIndex(base);
2108+ }
2109+ int currentRun = getRunForTab(tabCount, base);
2110+ if (base == tabRuns[currentRun]) {
2111+ int previous = tabRuns[getNextTabRun(currentRun)]-1;
2112+ return (previous != -1? previous : tabCount-1);
2113+ }
2114+ return getPreviousTabIndex(base);
2115+ }
2116+
2117+ protected int getPreviousTabRun(int baseRun) {
2118+ int runIndex = (baseRun - 1 >= 0? baseRun - 1 : runCount - 1);
2119+ return (runIndex >= 0? runIndex : 0);
2120+ }
2121+
2122+ protected int getNextTabRun(int baseRun) {
2123+ return (baseRun+1)%runCount;
2124+ }
2125+
2126+ protected static void rotateInsets(Insets topInsets, Insets targetInsets, int targetPlacement) {
2127+
2128+ switch(targetPlacement) {
2129+ case LEFT:
2130+ targetInsets.top = topInsets.left;
2131+ targetInsets.left = topInsets.top;
2132+ targetInsets.bottom = topInsets.right;
2133+ targetInsets.right = topInsets.bottom;
2134+ break;
2135+ case BOTTOM:
2136+ targetInsets.top = topInsets.bottom;
2137+ targetInsets.left = topInsets.left;
2138+ targetInsets.bottom = topInsets.top;
2139+ targetInsets.right = topInsets.right;
2140+ break;
2141+ case RIGHT:
2142+ targetInsets.top = topInsets.left;
2143+ targetInsets.left = topInsets.bottom;
2144+ targetInsets.bottom = topInsets.right;
2145+ targetInsets.right = topInsets.top;
2146+ break;
2147+ case TOP:
2148+ default:
2149+ targetInsets.top = topInsets.top;
2150+ targetInsets.left = topInsets.left;
2151+ targetInsets.bottom = topInsets.bottom;
2152+ targetInsets.right = topInsets.right;
2153+ }
2154+ }
2155+
2156+ // REMIND(aim,7/29/98): This method should be made
2157+ // protected in the next release where
2158+ // API changes are allowed
2159+ boolean requestFocusForVisibleComponent() {
2160+ return SwingUtilities2.tabbedPaneChangeFocusTo(getVisibleComponent());
2161+ }
2162+
2163+ private static class Actions extends UIAction {
2164+ final static String NEXT = "navigateNext";
2165+ final static String PREVIOUS = "navigatePrevious";
2166+ final static String RIGHT = "navigateRight";
2167+ final static String LEFT = "navigateLeft";
2168+ final static String UP = "navigateUp";
2169+ final static String DOWN = "navigateDown";
2170+ final static String PAGE_UP = "navigatePageUp";
2171+ final static String PAGE_DOWN = "navigatePageDown";
2172+ final static String REQUEST_FOCUS = "requestFocus";
2173+ final static String REQUEST_FOCUS_FOR_VISIBLE =
2174+ "requestFocusForVisibleComponent";
2175+ final static String SET_SELECTED = "setSelectedIndex";
2176+ final static String SELECT_FOCUSED = "selectTabWithFocus";
2177+ final static String SCROLL_FORWARD = "scrollTabsForwardAction";
2178+ final static String SCROLL_BACKWARD = "scrollTabsBackwardAction";
2179+
2180+ Actions(String key) {
2181+ super(key);
2182+ }
2183+
2184+ public void actionPerformed(ActionEvent e) {
2185+ String key = getName();
2186+ JTabbedPane pane = (JTabbedPane)e.getSource();
2187+ BasicTabbedPaneUI ui = (BasicTabbedPaneUI)BasicLookAndFeel.
2188+ getUIOfType(pane.getUI(), BasicTabbedPaneUI.class);
2189+
2190+ if (ui == null) {
2191+ return;
2192+ }
2193+ if (key == NEXT) {
2194+ ui.navigateSelectedTab(SwingConstants.NEXT);
2195+ }
2196+ else if (key == PREVIOUS) {
2197+ ui.navigateSelectedTab(SwingConstants.PREVIOUS);
2198+ }
2199+ else if (key == RIGHT) {
2200+ ui.navigateSelectedTab(SwingConstants.EAST);
2201+ }
2202+ else if (key == LEFT) {
2203+ ui.navigateSelectedTab(SwingConstants.WEST);
2204+ }
2205+ else if (key == UP) {
2206+ ui.navigateSelectedTab(SwingConstants.NORTH);
2207+ }
2208+ else if (key == DOWN) {
2209+ ui.navigateSelectedTab(SwingConstants.SOUTH);
2210+ }
2211+ else if (key == PAGE_UP) {
2212+ int tabPlacement = pane.getTabPlacement();
2213+ if (tabPlacement == TOP|| tabPlacement == BOTTOM) {
2214+ ui.navigateSelectedTab(SwingConstants.WEST);
2215+ } else {
2216+ ui.navigateSelectedTab(SwingConstants.NORTH);
2217+ }
2218+ }
2219+ else if (key == PAGE_DOWN) {
2220+ int tabPlacement = pane.getTabPlacement();
2221+ if (tabPlacement == TOP || tabPlacement == BOTTOM) {
2222+ ui.navigateSelectedTab(SwingConstants.EAST);
2223+ } else {
2224+ ui.navigateSelectedTab(SwingConstants.SOUTH);
2225+ }
2226+ }
2227+ else if (key == REQUEST_FOCUS) {
2228+ pane.requestFocus();
2229+ }
2230+ else if (key == REQUEST_FOCUS_FOR_VISIBLE) {
2231+ ui.requestFocusForVisibleComponent();
2232+ }
2233+ else if (key == SET_SELECTED) {
2234+ String command = e.getActionCommand();
2235+
2236+ if (command != null && command.length() > 0) {
2237+ int mnemonic = (int)e.getActionCommand().charAt(0);
2238+ if (mnemonic >= 'a' && mnemonic <='z') {
2239+ mnemonic -= ('a' - 'A');
2240+ }
2241+ Integer index = ui.mnemonicToIndexMap.get(Integer.valueOf(mnemonic));
2242+ if (index != null && pane.isEnabledAt(index.intValue())) {
2243+ pane.setSelectedIndex(index.intValue());
2244+ }
2245+ }
2246+ }
2247+ else if (key == SELECT_FOCUSED) {
2248+ int focusIndex = ui.getFocusIndex();
2249+ if (focusIndex != -1) {
2250+ pane.setSelectedIndex(focusIndex);
2251+ }
2252+ }
2253+ else if (key == SCROLL_FORWARD) {
2254+ if (ui.scrollableTabLayoutEnabled()) {
2255+ ui.tabScroller.scrollForward(pane.getTabPlacement());
2256+ }
2257+ }
2258+ else if (key == SCROLL_BACKWARD) {
2259+ if (ui.scrollableTabLayoutEnabled()) {
2260+ ui.tabScroller.scrollBackward(pane.getTabPlacement());
2261+ }
2262+ }
2263+ }
2264+ }
2265+
2266+ /**
2267+ * This class should be treated as a &quot;protected&quot; inner class.
2268+ * Instantiate it only within subclasses of BasicTabbedPaneUI.
2269+ */
2270+ public class TabbedPaneLayout implements LayoutManager {
2271+
2272+ public void addLayoutComponent(String name, Component comp) {}
2273+
2274+ public void removeLayoutComponent(Component comp) {}
2275+
2276+ public Dimension preferredLayoutSize(Container parent) {
2277+ return calculateSize(false);
2278+ }
2279+
2280+ public Dimension minimumLayoutSize(Container parent) {
2281+ return calculateSize(true);
2282+ }
2283+
2284+ protected Dimension calculateSize(boolean minimum) {
2285+ int tabPlacement = tabPane.getTabPlacement();
2286+ Insets insets = tabPane.getInsets();
2287+ Insets contentInsets = getContentBorderInsets(tabPlacement);
2288+ Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
2289+
2290+ Dimension zeroSize = new Dimension(0,0);
2291+ int height = 0;
2292+ int width = 0;
2293+ int cWidth = 0;
2294+ int cHeight = 0;
2295+
2296+ // Determine minimum size required to display largest
2297+ // child in each dimension
2298+ //
2299+ for (int i = 0; i < tabPane.getTabCount(); i++) {
2300+ Component component = tabPane.getComponentAt(i);
2301+ if (component != null) {
2302+ Dimension size = minimum ? component.getMinimumSize() :
2303+ component.getPreferredSize();
2304+
2305+ if (size != null) {
2306+ cHeight = Math.max(size.height, cHeight);
2307+ cWidth = Math.max(size.width, cWidth);
2308+ }
2309+ }
2310+ }
2311+ // Add content border insets to minimum size
2312+ width += cWidth;
2313+ height += cHeight;
2314+ int tabExtent;
2315+
2316+ // Calculate how much space the tabs will need, based on the
2317+ // minimum size required to display largest child + content border
2318+ //
2319+ switch(tabPlacement) {
2320+ case LEFT:
2321+ case RIGHT:
2322+ height = Math.max(height, calculateMaxTabHeight(tabPlacement));
2323+ tabExtent = preferredTabAreaWidth(tabPlacement, height - tabAreaInsets.top - tabAreaInsets.bottom);
2324+ width += tabExtent;
2325+ break;
2326+ case TOP:
2327+ case BOTTOM:
2328+ default:
2329+ width = Math.max(width, calculateMaxTabWidth(tabPlacement));
2330+ tabExtent = preferredTabAreaHeight(tabPlacement, width - tabAreaInsets.left - tabAreaInsets.right);
2331+ height += tabExtent;
2332+ }
2333+ return new Dimension(width + insets.left + insets.right + contentInsets.left + contentInsets.right,
2334+ height + insets.bottom + insets.top + contentInsets.top + contentInsets.bottom);
2335+
2336+ }
2337+
2338+ protected int preferredTabAreaHeight(int tabPlacement, int width) {
2339+ FontMetrics metrics = getFontMetrics();
2340+ int tabCount = tabPane.getTabCount();
2341+ int total = 0;
2342+ if (tabCount > 0) {
2343+ int rows = 1;
2344+ int x = 0;
2345+
2346+ int maxTabHeight = calculateMaxTabHeight(tabPlacement);
2347+
2348+ for (int i = 0; i < tabCount; i++) {
2349+ int tabWidth = calculateTabWidth(tabPlacement, i, metrics);
2350+
2351+ if (x != 0 && x + tabWidth > width) {
2352+ rows++;
2353+ x = 0;
2354+ }
2355+ x += tabWidth;
2356+ }
2357+ total = calculateTabAreaHeight(tabPlacement, rows, maxTabHeight);
2358+ }
2359+ return total;
2360+ }
2361+
2362+ protected int preferredTabAreaWidth(int tabPlacement, int height) {
2363+ FontMetrics metrics = getFontMetrics();
2364+ int tabCount = tabPane.getTabCount();
2365+ int total = 0;
2366+ if (tabCount > 0) {
2367+ int columns = 1;
2368+ int y = 0;
2369+ int fontHeight = metrics.getHeight();
2370+
2371+ maxTabWidth = calculateMaxTabWidth(tabPlacement);
2372+
2373+ for (int i = 0; i < tabCount; i++) {
2374+ int tabHeight = calculateTabHeight(tabPlacement, i, fontHeight);
2375+
2376+ if (y != 0 && y + tabHeight > height) {
2377+ columns++;
2378+ y = 0;
2379+ }
2380+ y += tabHeight;
2381+ }
2382+ total = calculateTabAreaWidth(tabPlacement, columns, maxTabWidth);
2383+ }
2384+ return total;
2385+ }
2386+
2387+ public void layoutContainer(Container parent) {
2388+ /* Some of the code in this method deals with changing the
2389+ * visibility of components to hide and show the contents for the
2390+ * selected tab. This is older code that has since been duplicated
2391+ * in JTabbedPane.fireStateChanged(), so as to allow visibility
2392+ * changes to happen sooner (see the note there). This code remains
2393+ * for backward compatibility as there are some cases, such as
2394+ * subclasses that don't fireStateChanged() where it may be used.
2395+ * Any changes here need to be kept in synch with
2396+ * JTabbedPane.fireStateChanged().
2397+ */
2398+
2399+ setRolloverTab(-1);
2400+
2401+ int tabPlacement = tabPane.getTabPlacement();
2402+ Insets insets = tabPane.getInsets();
2403+ int selectedIndex = tabPane.getSelectedIndex();
2404+ Component visibleComponent = getVisibleComponent();
2405+
2406+ calculateLayoutInfo();
2407+
2408+ Component selectedComponent = null;
2409+ if (selectedIndex < 0) {
2410+ if (visibleComponent != null) {
2411+ // The last tab was removed, so remove the component
2412+ setVisibleComponent(null);
2413+ }
2414+ } else {
2415+ selectedComponent = tabPane.getComponentAt(selectedIndex);
2416+ }
2417+ int cx, cy, cw, ch;
2418+ int totalTabWidth = 0;
2419+ int totalTabHeight = 0;
2420+ Insets contentInsets = getContentBorderInsets(tabPlacement);
2421+
2422+ boolean shouldChangeFocus = false;
2423+
2424+ // In order to allow programs to use a single component
2425+ // as the display for multiple tabs, we will not change
2426+ // the visible compnent if the currently selected tab
2427+ // has a null component. This is a bit dicey, as we don't
2428+ // explicitly state we support this in the spec, but since
2429+ // programs are now depending on this, we're making it work.
2430+ //
2431+ if(selectedComponent != null) {
2432+ if(selectedComponent != visibleComponent &&
2433+ visibleComponent != null) {
2434+ if(SwingUtilities.findFocusOwner(visibleComponent) != null) {
2435+ shouldChangeFocus = true;
2436+ }
2437+ }
2438+ setVisibleComponent(selectedComponent);
2439+ }
2440+
2441+ Rectangle bounds = tabPane.getBounds();
2442+ int numChildren = tabPane.getComponentCount();
2443+
2444+ if(numChildren > 0) {
2445+
2446+ switch(tabPlacement) {
2447+ case LEFT:
2448+ totalTabWidth = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
2449+ cx = insets.left + totalTabWidth + contentInsets.left;
2450+ cy = insets.top + contentInsets.top;
2451+ break;
2452+ case RIGHT:
2453+ totalTabWidth = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
2454+ cx = insets.left + contentInsets.left;
2455+ cy = insets.top + contentInsets.top;
2456+ break;
2457+ case BOTTOM:
2458+ totalTabHeight = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
2459+ cx = insets.left + contentInsets.left;
2460+ cy = insets.top + contentInsets.top;
2461+ break;
2462+ case TOP:
2463+ default:
2464+ totalTabHeight = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
2465+ cx = insets.left + contentInsets.left;
2466+ cy = insets.top + totalTabHeight + contentInsets.top;
2467+ }
2468+
2469+ cw = bounds.width - totalTabWidth -
2470+ insets.left - insets.right -
2471+ contentInsets.left - contentInsets.right;
2472+ ch = bounds.height - totalTabHeight -
2473+ insets.top - insets.bottom -
2474+ contentInsets.top - contentInsets.bottom;
2475+
2476+ for(int i = 0; i < numChildren; i++) {
2477+ Component child = tabPane.getComponent(i);
2478+ if(child == tabContainer) {
2479+
2480+ int tabContainerWidth = totalTabWidth == 0 ? bounds.width :
2481+ totalTabWidth + insets.left + insets.right +
2482+ contentInsets.left + contentInsets.right;
2483+ int tabContainerHeight = totalTabHeight == 0 ? bounds.height :
2484+ totalTabHeight + insets.top + insets.bottom +
2485+ contentInsets.top + contentInsets.bottom;
2486+
2487+ int tabContainerX = 0;
2488+ int tabContainerY = 0;
2489+ if(tabPlacement == BOTTOM) {
2490+ tabContainerY = bounds.height - tabContainerHeight;
2491+ } else if(tabPlacement == RIGHT) {
2492+ tabContainerX = bounds.width - tabContainerWidth;
2493+ }
2494+ child.setBounds(tabContainerX, tabContainerY, tabContainerWidth, tabContainerHeight);
2495+ } else {
2496+ child.setBounds(cx, cy, cw, ch);
2497+ }
2498+ }
2499+ }
2500+ layoutTabComponents();
2501+ if(shouldChangeFocus) {
2502+ if(!requestFocusForVisibleComponent()) {
2503+ tabPane.requestFocus();
2504+ }
2505+ }
2506+ }
2507+
2508+ public void calculateLayoutInfo() {
2509+ int tabCount = tabPane.getTabCount();
2510+ assureRectsCreated(tabCount);
2511+ calculateTabRects(tabPane.getTabPlacement(), tabCount);
2512+ isRunsDirty = false;
2513+ }
2514+
2515+ private void layoutTabComponents() {
2516+ if (tabContainer == null) {
2517+ return;
2518+ }
2519+ Rectangle rect = new Rectangle();
2520+ Point delta = new Point(-tabContainer.getX(), -tabContainer.getY());
2521+ if (scrollableTabLayoutEnabled()) {
2522+ translatePointToTabPanel(0, 0, delta);
2523+ }
2524+ for (int i = 0; i < tabPane.getTabCount(); i++) {
2525+ Component c = tabPane.getTabComponentAt(i);
2526+ if (c == null) {
2527+ continue;
2528+ }
2529+ getTabBounds(i, rect);
2530+ Dimension preferredSize = c.getPreferredSize();
2531+ Insets insets = getTabInsets(tabPane.getTabPlacement(), i);
2532+ int outerX = rect.x + insets.left + delta.x;
2533+ int outerY = rect.y + insets.top + delta.y;
2534+ int outerWidth = rect.width - insets.left - insets.right;
2535+ int outerHeight = rect.height - insets.top - insets.bottom;
2536+ //centralize component
2537+ int x = outerX + (outerWidth - preferredSize.width) / 2;
2538+ int y = outerY + (outerHeight - preferredSize.height) / 2;
2539+ int tabPlacement = tabPane.getTabPlacement();
2540+ boolean isSeleceted = i == tabPane.getSelectedIndex();
2541+ c.setBounds(x + getTabLabelShiftX(tabPlacement, i, isSeleceted),
2542+ y + getTabLabelShiftY(tabPlacement, i, isSeleceted),
2543+ preferredSize.width, preferredSize.height);
2544+ }
2545+ }
2546+
2547+ protected void calculateTabRects(int tabPlacement, int tabCount) {
2548+ FontMetrics metrics = getFontMetrics();
2549+ Dimension size = tabPane.getSize();
2550+ Insets insets = tabPane.getInsets();
2551+ Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
2552+ int fontHeight = metrics.getHeight();
2553+ int selectedIndex = tabPane.getSelectedIndex();
2554+ int tabRunOverlay;
2555+ int i, j;
2556+ int x, y;
2557+ int returnAt;
2558+ boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
2559+ boolean leftToRight = BasicGraphicsUtils.isLeftToRight(tabPane);
2560+
2561+ //
2562+ // Calculate bounds within which a tab run must fit
2563+ //
2564+ switch(tabPlacement) {
2565+ case LEFT:
2566+ maxTabWidth = calculateMaxTabWidth(tabPlacement);
2567+ x = insets.left + tabAreaInsets.left;
2568+ y = insets.top + tabAreaInsets.top;
2569+ returnAt = size.height - (insets.bottom + tabAreaInsets.bottom);
2570+ break;
2571+ case RIGHT:
2572+ maxTabWidth = calculateMaxTabWidth(tabPlacement);
2573+ x = size.width - insets.right - tabAreaInsets.right - maxTabWidth;
2574+ y = insets.top + tabAreaInsets.top;
2575+ returnAt = size.height - (insets.bottom + tabAreaInsets.bottom);
2576+ break;
2577+ case BOTTOM:
2578+ maxTabHeight = calculateMaxTabHeight(tabPlacement);
2579+ x = insets.left + tabAreaInsets.left;
2580+ y = size.height - insets.bottom - tabAreaInsets.bottom - maxTabHeight;
2581+ returnAt = size.width - (insets.right + tabAreaInsets.right);
2582+ break;
2583+ case TOP:
2584+ default:
2585+ maxTabHeight = calculateMaxTabHeight(tabPlacement);
2586+ x = insets.left + tabAreaInsets.left;
2587+ y = insets.top + tabAreaInsets.top;
2588+ returnAt = size.width - (insets.right + tabAreaInsets.right);
2589+ break;
2590+ }
2591+
2592+ tabRunOverlay = getTabRunOverlay(tabPlacement);
2593+
2594+ runCount = 0;
2595+ selectedRun = -1;
2596+
2597+ if (tabCount == 0) {
2598+ return;
2599+ }
2600+
2601+ // Run through tabs and partition them into runs
2602+ Rectangle rect;
2603+ for (i = 0; i < tabCount; i++) {
2604+ rect = rects[i];
2605+
2606+ if (!verticalTabRuns) {
2607+ // Tabs on TOP or BOTTOM....
2608+ if (i > 0) {
2609+ rect.x = rects[i-1].x + rects[i-1].width;
2610+ } else {
2611+ tabRuns[0] = 0;
2612+ runCount = 1;
2613+ maxTabWidth = 0;
2614+ rect.x = x;
2615+ }
2616+ rect.width = calculateTabWidth(tabPlacement, i, metrics);
2617+ maxTabWidth = Math.max(maxTabWidth, rect.width);
2618+
2619+ // Never move a TAB down a run if it is in the first column.
2620+ // Even if there isn't enough room, moving it to a fresh
2621+ // line won't help.
2622+ if (rect.x != 2 + insets.left && rect.x + rect.width > returnAt) {
2623+ if (runCount > tabRuns.length - 1) {
2624+ expandTabRunsArray();
2625+ }
2626+ tabRuns[runCount] = i;
2627+ runCount++;
2628+ rect.x = x;
2629+ }
2630+ // Initialize y position in case there's just one run
2631+ rect.y = y;
2632+ rect.height = maxTabHeight/* - 2*/;
2633+
2634+ } else {
2635+ // Tabs on LEFT or RIGHT...
2636+ if (i > 0) {
2637+ rect.y = rects[i-1].y + rects[i-1].height;
2638+ } else {
2639+ tabRuns[0] = 0;
2640+ runCount = 1;
2641+ maxTabHeight = 0;
2642+ rect.y = y;
2643+ }
2644+ rect.height = calculateTabHeight(tabPlacement, i, fontHeight);
2645+ maxTabHeight = Math.max(maxTabHeight, rect.height);
2646+
2647+ // Never move a TAB over a run if it is in the first run.
2648+ // Even if there isn't enough room, moving it to a fresh
2649+ // column won't help.
2650+ if (rect.y != 2 + insets.top && rect.y + rect.height > returnAt) {
2651+ if (runCount > tabRuns.length - 1) {
2652+ expandTabRunsArray();
2653+ }
2654+ tabRuns[runCount] = i;
2655+ runCount++;
2656+ rect.y = y;
2657+ }
2658+ // Initialize x position in case there's just one column
2659+ rect.x = x;
2660+ rect.width = maxTabWidth/* - 2*/;
2661+
2662+ }
2663+ if (i == selectedIndex) {
2664+ selectedRun = runCount - 1;
2665+ }
2666+ }
2667+
2668+ if (runCount > 1) {
2669+ // Re-distribute tabs in case last run has leftover space
2670+ normalizeTabRuns(tabPlacement, tabCount, verticalTabRuns? y : x, returnAt);
2671+
2672+ selectedRun = getRunForTab(tabCount, selectedIndex);
2673+
2674+ // Rotate run array so that selected run is first
2675+ if (shouldRotateTabRuns(tabPlacement)) {
2676+ rotateTabRuns(tabPlacement, selectedRun);
2677+ }
2678+ }
2679+
2680+ // Step through runs from back to front to calculate
2681+ // tab y locations and to pad runs appropriately
2682+ for (i = runCount - 1; i >= 0; i--) {
2683+ int start = tabRuns[i];
2684+ int next = tabRuns[i == (runCount - 1)? 0 : i + 1];
2685+ int end = (next != 0? next - 1 : tabCount - 1);
2686+ if (!verticalTabRuns) {
2687+ for (j = start; j <= end; j++) {
2688+ rect = rects[j];
2689+ rect.y = y;
2690+ rect.x += getTabRunIndent(tabPlacement, i);
2691+ }
2692+ if (shouldPadTabRun(tabPlacement, i)) {
2693+ padTabRun(tabPlacement, start, end, returnAt);
2694+ }
2695+ if (tabPlacement == BOTTOM) {
2696+ y -= (maxTabHeight - tabRunOverlay);
2697+ } else {
2698+ y += (maxTabHeight - tabRunOverlay);
2699+ }
2700+ } else {
2701+ for (j = start; j <= end; j++) {
2702+ rect = rects[j];
2703+ rect.x = x;
2704+ rect.y += getTabRunIndent(tabPlacement, i);
2705+ }
2706+ if (shouldPadTabRun(tabPlacement, i)) {
2707+ padTabRun(tabPlacement, start, end, returnAt);
2708+ }
2709+ if (tabPlacement == RIGHT) {
2710+ x -= (maxTabWidth - tabRunOverlay);
2711+ } else {
2712+ x += (maxTabWidth - tabRunOverlay);
2713+ }
2714+ }
2715+ }
2716+
2717+ // Pad the selected tab so that it appears raised in front
2718+ padSelectedTab(tabPlacement, selectedIndex);
2719+
2720+ // if right to left and tab placement on the top or
2721+ // the bottom, flip x positions and adjust by widths
2722+ if (!leftToRight && !verticalTabRuns) {
2723+ int rightMargin = size.width
2724+ - (insets.right + tabAreaInsets.right);
2725+ for (i = 0; i < tabCount; i++) {
2726+ rects[i].x = rightMargin - rects[i].x - rects[i].width;
2727+ }
2728+ }
2729+ }
2730+
2731+
2732+ /*
2733+ * Rotates the run-index array so that the selected run is run[0]
2734+ */
2735+ protected void rotateTabRuns(int tabPlacement, int selectedRun) {
2736+ for (int i = 0; i < selectedRun; i++) {
2737+ int save = tabRuns[0];
2738+ for (int j = 1; j < runCount; j++) {
2739+ tabRuns[j - 1] = tabRuns[j];
2740+ }
2741+ tabRuns[runCount-1] = save;
2742+ }
2743+ }
2744+
2745+ protected void normalizeTabRuns(int tabPlacement, int tabCount,
2746+ int start, int max) {
2747+ boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
2748+ int run = runCount - 1;
2749+ boolean keepAdjusting = true;
2750+ double weight = 1.25;
2751+
2752+ // At this point the tab runs are packed to fit as many
2753+ // tabs as possible, which can leave the last run with a lot
2754+ // of extra space (resulting in very fat tabs on the last run).
2755+ // So we'll attempt to distribute this extra space more evenly
2756+ // across the runs in order to make the runs look more consistent.
2757+ //
2758+ // Starting with the last run, determine whether the last tab in
2759+ // the previous run would fit (generously) in this run; if so,
2760+ // move tab to current run and shift tabs accordingly. Cycle
2761+ // through remaining runs using the same algorithm.
2762+ //
2763+ while (keepAdjusting) {
2764+ int last = lastTabInRun(tabCount, run);
2765+ int prevLast = lastTabInRun(tabCount, run-1);
2766+ int end;
2767+ int prevLastLen;
2768+
2769+ if (!verticalTabRuns) {
2770+ end = rects[last].x + rects[last].width;
2771+ prevLastLen = (int)(maxTabWidth*weight);
2772+ } else {
2773+ end = rects[last].y + rects[last].height;
2774+ prevLastLen = (int)(maxTabHeight*weight*2);
2775+ }
2776+
2777+ // Check if the run has enough extra space to fit the last tab
2778+ // from the previous row...
2779+ if (max - end > prevLastLen) {
2780+
2781+ // Insert tab from previous row and shift rest over
2782+ tabRuns[run] = prevLast;
2783+ if (!verticalTabRuns) {
2784+ rects[prevLast].x = start;
2785+ } else {
2786+ rects[prevLast].y = start;
2787+ }
2788+ for (int i = prevLast+1; i <= last; i++) {
2789+ if (!verticalTabRuns) {
2790+ rects[i].x = rects[i-1].x + rects[i-1].width;
2791+ } else {
2792+ rects[i].y = rects[i-1].y + rects[i-1].height;
2793+ }
2794+ }
2795+
2796+ } else if (run == runCount - 1) {
2797+ // no more room left in last run, so we're done!
2798+ keepAdjusting = false;
2799+ }
2800+ if (run - 1 > 0) {
2801+ // check previous run next...
2802+ run -= 1;
2803+ } else {
2804+ // check last run again...but require a higher ratio
2805+ // of extraspace-to-tabsize because we don't want to
2806+ // end up with too many tabs on the last run!
2807+ run = runCount - 1;
2808+ weight += .25;
2809+ }
2810+ }
2811+ }
2812+
2813+ protected void padTabRun(int tabPlacement, int start, int end, int max) {
2814+ Rectangle lastRect = rects[end];
2815+ if (tabPlacement == TOP || tabPlacement == BOTTOM) {
2816+ int runWidth = (lastRect.x + lastRect.width) - rects[start].x;
2817+ int deltaWidth = max - (lastRect.x + lastRect.width);
2818+ float factor = (float)deltaWidth / (float)runWidth;
2819+
2820+ for (int j = start; j <= end; j++) {
2821+ Rectangle pastRect = rects[j];
2822+ if (j > start) {
2823+ pastRect.x = rects[j-1].x + rects[j-1].width;
2824+ }
2825+ pastRect.width += Math.round((float)pastRect.width * factor);
2826+ }
2827+ lastRect.width = max - lastRect.x;
2828+ } else {
2829+ int runHeight = (lastRect.y + lastRect.height) - rects[start].y;
2830+ int deltaHeight = max - (lastRect.y + lastRect.height);
2831+ float factor = (float)deltaHeight / (float)runHeight;
2832+
2833+ for (int j = start; j <= end; j++) {
2834+ Rectangle pastRect = rects[j];
2835+ if (j > start) {
2836+ pastRect.y = rects[j-1].y + rects[j-1].height;
2837+ }
2838+ pastRect.height += Math.round((float)pastRect.height * factor);
2839+ }
2840+ lastRect.height = max - lastRect.y;
2841+ }
2842+ }
2843+
2844+ protected void padSelectedTab(int tabPlacement, int selectedIndex) {
2845+
2846+ if (selectedIndex >= 0) {
2847+ Rectangle selRect = rects[selectedIndex];
2848+ Insets padInsets = getSelectedTabPadInsets(tabPlacement);
2849+ selRect.x -= padInsets.left;
2850+ selRect.width += (padInsets.left + padInsets.right);
2851+ selRect.y -= padInsets.top;
2852+ selRect.height += (padInsets.top + padInsets.bottom);
2853+
2854+ if (!scrollableTabLayoutEnabled()) { // WRAP_TAB_LAYOUT
2855+ // do not expand selected tab more then necessary
2856+ Dimension size = tabPane.getSize();
2857+ Insets insets = tabPane.getInsets();
2858+
2859+ if ((tabPlacement == LEFT) || (tabPlacement == RIGHT)) {
2860+ int top = insets.top - selRect.y;
2861+ if (top > 0) {
2862+ selRect.y += top;
2863+ selRect.height -= top;
2864+ }
2865+ int bottom = (selRect.y + selRect.height) + insets.bottom - size.height;
2866+ if (bottom > 0) {
2867+ selRect.height -= bottom;
2868+ }
2869+ } else {
2870+ int left = insets.left - selRect.x;
2871+ if (left > 0) {
2872+ selRect.x += left;
2873+ selRect.width -= left;
2874+ }
2875+ int right = (selRect.x + selRect.width) + insets.right - size.width;
2876+ if (right > 0) {
2877+ selRect.width -= right;
2878+ }
2879+ }
2880+ }
2881+ }
2882+ }
2883+ }
2884+
2885+ private class TabbedPaneScrollLayout extends TabbedPaneLayout {
2886+
2887+ protected int preferredTabAreaHeight(int tabPlacement, int width) {
2888+ return calculateMaxTabHeight(tabPlacement);
2889+ }
2890+
2891+ protected int preferredTabAreaWidth(int tabPlacement, int height) {
2892+ return calculateMaxTabWidth(tabPlacement);
2893+ }
2894+
2895+ public void layoutContainer(Container parent) {
2896+ /* Some of the code in this method deals with changing the
2897+ * visibility of components to hide and show the contents for the
2898+ * selected tab. This is older code that has since been duplicated
2899+ * in JTabbedPane.fireStateChanged(), so as to allow visibility
2900+ * changes to happen sooner (see the note there). This code remains
2901+ * for backward compatibility as there are some cases, such as
2902+ * subclasses that don't fireStateChanged() where it may be used.
2903+ * Any changes here need to be kept in synch with
2904+ * JTabbedPane.fireStateChanged().
2905+ */
2906+
2907+ setRolloverTab(-1);
2908+
2909+ int tabPlacement = tabPane.getTabPlacement();
2910+ int tabCount = tabPane.getTabCount();
2911+ Insets insets = tabPane.getInsets();
2912+ int selectedIndex = tabPane.getSelectedIndex();
2913+ Component visibleComponent = getVisibleComponent();
2914+
2915+ calculateLayoutInfo();
2916+
2917+ Component selectedComponent = null;
2918+ if (selectedIndex < 0) {
2919+ if (visibleComponent != null) {
2920+ // The last tab was removed, so remove the component
2921+ setVisibleComponent(null);
2922+ }
2923+ } else {
2924+ selectedComponent = tabPane.getComponentAt(selectedIndex);
2925+ }
2926+
2927+ if (tabPane.getTabCount() == 0) {
2928+ tabScroller.croppedEdge.resetParams();
2929+ tabScroller.scrollForwardButton.setVisible(false);
2930+ tabScroller.scrollBackwardButton.setVisible(false);
2931+ return;
2932+ }
2933+
2934+ boolean shouldChangeFocus = false;
2935+
2936+ // In order to allow programs to use a single component
2937+ // as the display for multiple tabs, we will not change
2938+ // the visible compnent if the currently selected tab
2939+ // has a null component. This is a bit dicey, as we don't
2940+ // explicitly state we support this in the spec, but since
2941+ // programs are now depending on this, we're making it work.
2942+ //
2943+ if(selectedComponent != null) {
2944+ if(selectedComponent != visibleComponent &&
2945+ visibleComponent != null) {
2946+ if(SwingUtilities.findFocusOwner(visibleComponent) != null) {
2947+ shouldChangeFocus = true;
2948+ }
2949+ }
2950+ setVisibleComponent(selectedComponent);
2951+ }
2952+ int tx, ty, tw, th; // tab area bounds
2953+ int cx, cy, cw, ch; // content area bounds
2954+ Insets contentInsets = getContentBorderInsets(tabPlacement);
2955+ Rectangle bounds = tabPane.getBounds();
2956+ int numChildren = tabPane.getComponentCount();
2957+
2958+ if(numChildren > 0) {
2959+ switch(tabPlacement) {
2960+ case LEFT:
2961+ // calculate tab area bounds
2962+ tw = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
2963+ th = bounds.height - insets.top - insets.bottom;
2964+ tx = insets.left;
2965+ ty = insets.top;
2966+
2967+ // calculate content area bounds
2968+ cx = tx + tw + contentInsets.left;
2969+ cy = ty + contentInsets.top;
2970+ cw = bounds.width - insets.left - insets.right - tw -
2971+ contentInsets.left - contentInsets.right;
2972+ ch = bounds.height - insets.top - insets.bottom -
2973+ contentInsets.top - contentInsets.bottom;
2974+ break;
2975+ case RIGHT:
2976+ // calculate tab area bounds
2977+ tw = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
2978+ th = bounds.height - insets.top - insets.bottom;
2979+ tx = bounds.width - insets.right - tw;
2980+ ty = insets.top;
2981+
2982+ // calculate content area bounds
2983+ cx = insets.left + contentInsets.left;
2984+ cy = insets.top + contentInsets.top;
2985+ cw = bounds.width - insets.left - insets.right - tw -
2986+ contentInsets.left - contentInsets.right;
2987+ ch = bounds.height - insets.top - insets.bottom -
2988+ contentInsets.top - contentInsets.bottom;
2989+ break;
2990+ case BOTTOM:
2991+ // calculate tab area bounds
2992+ tw = bounds.width - insets.left - insets.right;
2993+ th = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
2994+ tx = insets.left;
2995+ ty = bounds.height - insets.bottom - th;
2996+
2997+ // calculate content area bounds
2998+ cx = insets.left + contentInsets.left;
2999+ cy = insets.top + contentInsets.top;
3000+ cw = bounds.width - insets.left - insets.right -
3001+ contentInsets.left - contentInsets.right;
3002+ ch = bounds.height - insets.top - insets.bottom - th -
3003+ contentInsets.top - contentInsets.bottom;
3004+ break;
3005+ case TOP:
3006+ default:
3007+ // calculate tab area bounds
3008+ tw = bounds.width - insets.left - insets.right;
3009+ th = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
3010+ tx = insets.left;
3011+ ty = insets.top;
3012+
3013+ // calculate content area bounds
3014+ cx = tx + contentInsets.left;
3015+ cy = ty + th + contentInsets.top;
3016+ cw = bounds.width - insets.left - insets.right -
3017+ contentInsets.left - contentInsets.right;
3018+ ch = bounds.height - insets.top - insets.bottom - th -
3019+ contentInsets.top - contentInsets.bottom;
3020+ }
3021+
3022+ for(int i = 0; i < numChildren; i++) {
3023+ Component child = tabPane.getComponent(i);
3024+
3025+ if(tabScroller != null && child == tabScroller.viewport) {
3026+ JViewport viewport = (JViewport) child;
3027+ Rectangle viewRect = viewport.getViewRect();
3028+ int vw = tw;
3029+ int vh = th;
3030+ Dimension butSize = tabScroller.scrollForwardButton.getPreferredSize();
3031+ switch(tabPlacement) {
3032+ case LEFT:
3033+ case RIGHT:
3034+ int totalTabHeight = rects[tabCount - 1].y + rects[tabCount - 1].height;
3035+ if(totalTabHeight > th) {
3036+ // Allow space for scrollbuttons
3037+ vh = (th > 2 * butSize.height) ? th - 2 * butSize.height : 0;
3038+ if(totalTabHeight - viewRect.y <= vh) {
3039+ // Scrolled to the end, so ensure the viewport size is
3040+ // such that the scroll offset aligns with a tab
3041+ vh = totalTabHeight - viewRect.y;
3042+ }
3043+ }
3044+ break;
3045+ case BOTTOM:
3046+ case TOP:
3047+ default:
3048+ int totalTabWidth = rects[tabCount - 1].x + rects[tabCount - 1].width;
3049+ if(totalTabWidth > tw) {
3050+ // Need to allow space for scrollbuttons
3051+ vw = (tw > 2 * butSize.width) ? tw - 2 * butSize.width : 0;
3052+ if(totalTabWidth - viewRect.x <= vw) {
3053+ // Scrolled to the end, so ensure the viewport size is
3054+ // such that the scroll offset aligns with a tab
3055+ vw = totalTabWidth - viewRect.x;
3056+ }
3057+ }
3058+ }
3059+ child.setBounds(tx, ty, vw, vh);
3060+
3061+ } else if(tabScroller != null &&
3062+ (child == tabScroller.scrollForwardButton ||
3063+ child == tabScroller.scrollBackwardButton)) {
3064+ Component scrollbutton = child;
3065+ Dimension bsize = scrollbutton.getPreferredSize();
3066+ int bx = 0;
3067+ int by = 0;
3068+ int bw = bsize.width;
3069+ int bh = bsize.height;
3070+ boolean visible = false;
3071+
3072+ switch(tabPlacement) {
3073+ case LEFT:
3074+ case RIGHT:
3075+ int totalTabHeight = rects[tabCount - 1].y + rects[tabCount - 1].height;
3076+ if(totalTabHeight > th) {
3077+ visible = true;
3078+ bx = (tabPlacement == LEFT ? tx + tw - bsize.width : tx);
3079+ by = (child == tabScroller.scrollForwardButton) ?
3080+ bounds.height - insets.bottom - bsize.height :
3081+ bounds.height - insets.bottom - 2 * bsize.height;
3082+ }
3083+ break;
3084+
3085+ case BOTTOM:
3086+ case TOP:
3087+ default:
3088+ int totalTabWidth = rects[tabCount - 1].x + rects[tabCount - 1].width;
3089+
3090+ if(totalTabWidth > tw) {
3091+ visible = true;
3092+ bx = (child == tabScroller.scrollForwardButton) ?
3093+ bounds.width - insets.left - bsize.width :
3094+ bounds.width - insets.left - 2 * bsize.width;
3095+ by = (tabPlacement == TOP ? ty + th - bsize.height : ty);
3096+ }
3097+ }
3098+ child.setVisible(visible);
3099+ if(visible) {
3100+ child.setBounds(bx, by, bw, bh);
3101+ }
3102+
3103+ } else {
3104+ // All content children...
3105+ child.setBounds(cx, cy, cw, ch);
3106+ }
3107+ }
3108+ super.layoutTabComponents();
3109+ layoutCroppedEdge();
3110+ if(shouldChangeFocus) {
3111+ if(!requestFocusForVisibleComponent()) {
3112+ tabPane.requestFocus();
3113+ }
3114+ }
3115+ }
3116+ }
3117+
3118+ private void layoutCroppedEdge() {
3119+ tabScroller.croppedEdge.resetParams();
3120+ Rectangle viewRect = tabScroller.viewport.getViewRect();
3121+ int cropline;
3122+ for (int i = 0; i < rects.length; i++) {
3123+ Rectangle tabRect = rects[i];
3124+ switch (tabPane.getTabPlacement()) {
3125+ case LEFT:
3126+ case RIGHT:
3127+ cropline = viewRect.y + viewRect.height;
3128+ if ((tabRect.y < cropline) && (tabRect.y + tabRect.height > cropline)) {
3129+ tabScroller.croppedEdge.setParams(i, cropline - tabRect.y - 1,
3130+ -currentTabAreaInsets.left, 0);
3131+ }
3132+ break;
3133+ case TOP:
3134+ case BOTTOM:
3135+ default:
3136+ cropline = viewRect.x + viewRect.width;
3137+ if ((tabRect.x < cropline - 1) && (tabRect.x + tabRect.width > cropline)) {
3138+ tabScroller.croppedEdge.setParams(i, cropline - tabRect.x - 1,
3139+ 0, -currentTabAreaInsets.top);
3140+ }
3141+ }
3142+ }
3143+ }
3144+
3145+ protected void calculateTabRects(int tabPlacement, int tabCount) {
3146+ FontMetrics metrics = getFontMetrics();
3147+ Dimension size = tabPane.getSize();
3148+ Insets insets = tabPane.getInsets();
3149+ Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
3150+ int fontHeight = metrics.getHeight();
3151+ int selectedIndex = tabPane.getSelectedIndex();
3152+ int i;
3153+ boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
3154+ boolean leftToRight = BasicGraphicsUtils.isLeftToRight(tabPane);
3155+ int x = tabAreaInsets.left;
3156+ int y = tabAreaInsets.top;
3157+ int totalWidth = 0;
3158+ int totalHeight = 0;
3159+
3160+ //
3161+ // Calculate bounds within which a tab run must fit
3162+ //
3163+ switch(tabPlacement) {
3164+ case LEFT:
3165+ case RIGHT:
3166+ maxTabWidth = calculateMaxTabWidth(tabPlacement);
3167+ break;
3168+ case BOTTOM:
3169+ case TOP:
3170+ default:
3171+ maxTabHeight = calculateMaxTabHeight(tabPlacement);
3172+ }
3173+
3174+ runCount = 0;
3175+ selectedRun = -1;
3176+
3177+ if (tabCount == 0) {
3178+ return;
3179+ }
3180+
3181+ selectedRun = 0;
3182+ runCount = 1;
3183+
3184+ // Run through tabs and lay them out in a single run
3185+ Rectangle rect;
3186+ for (i = 0; i < tabCount; i++) {
3187+ rect = rects[i];
3188+
3189+ if (!verticalTabRuns) {
3190+ // Tabs on TOP or BOTTOM....
3191+ if (i > 0) {
3192+ rect.x = rects[i-1].x + rects[i-1].width;
3193+ } else {
3194+ tabRuns[0] = 0;
3195+ maxTabWidth = 0;
3196+ totalHeight += maxTabHeight;
3197+ rect.x = x;
3198+ }
3199+ rect.width = calculateTabWidth(tabPlacement, i, metrics);
3200+ totalWidth = rect.x + rect.width;
3201+ maxTabWidth = Math.max(maxTabWidth, rect.width);
3202+
3203+ rect.y = y;
3204+ rect.height = maxTabHeight/* - 2*/;
3205+
3206+ } else {
3207+ // Tabs on LEFT or RIGHT...
3208+ if (i > 0) {
3209+ rect.y = rects[i-1].y + rects[i-1].height;
3210+ } else {
3211+ tabRuns[0] = 0;
3212+ maxTabHeight = 0;
3213+ totalWidth = maxTabWidth;
3214+ rect.y = y;
3215+ }
3216+ rect.height = calculateTabHeight(tabPlacement, i, fontHeight);
3217+ totalHeight = rect.y + rect.height;
3218+ maxTabHeight = Math.max(maxTabHeight, rect.height);
3219+
3220+ rect.x = x;
3221+ rect.width = maxTabWidth/* - 2*/;
3222+
3223+ }
3224+ }
3225+
3226+ if (tabsOverlapBorder) {
3227+ // Pad the selected tab so that it appears raised in front
3228+ padSelectedTab(tabPlacement, selectedIndex);
3229+ }
3230+
3231+ // if right to left and tab placement on the top or
3232+ // the bottom, flip x positions and adjust by widths
3233+ if (!leftToRight && !verticalTabRuns) {
3234+ int rightMargin = size.width
3235+ - (insets.right + tabAreaInsets.right);
3236+ for (i = 0; i < tabCount; i++) {
3237+ rects[i].x = rightMargin - rects[i].x - rects[i].width;
3238+ }
3239+ }
3240+ tabScroller.tabPanel.setPreferredSize(new Dimension(totalWidth, totalHeight));
3241+ }
3242+ }
3243+
3244+ private class ScrollableTabSupport implements ActionListener,
3245+ ChangeListener {
3246+ public ScrollableTabViewport viewport;
3247+ public ScrollableTabPanel tabPanel;
3248+ public JButton scrollForwardButton;
3249+ public JButton scrollBackwardButton;
3250+ public CroppedEdge croppedEdge;
3251+ public int leadingTabIndex;
3252+
3253+ private Point tabViewPosition = new Point(0,0);
3254+
3255+ ScrollableTabSupport(int tabPlacement) {
3256+ viewport = new ScrollableTabViewport();
3257+ tabPanel = new ScrollableTabPanel();
3258+ viewport.setView(tabPanel);
3259+ viewport.addChangeListener(this);
3260+ croppedEdge = new CroppedEdge();
3261+ createButtons();
3262+ }
3263+
3264+ /**
3265+ * Recreates the scroll buttons and adds them to the TabbedPane.
3266+ */
3267+ void createButtons() {
3268+ if (scrollForwardButton != null) {
3269+ tabPane.remove(scrollForwardButton);
3270+ scrollForwardButton.removeActionListener(this);
3271+ tabPane.remove(scrollBackwardButton);
3272+ scrollBackwardButton.removeActionListener(this);
3273+ }
3274+ int tabPlacement = tabPane.getTabPlacement();
3275+ if (tabPlacement == TOP || tabPlacement == BOTTOM) {
3276+ scrollForwardButton = createScrollButton(EAST);
3277+ scrollBackwardButton = createScrollButton(WEST);
3278+
3279+ } else { // tabPlacement = LEFT || RIGHT
3280+ scrollForwardButton = createScrollButton(SOUTH);
3281+ scrollBackwardButton = createScrollButton(NORTH);
3282+ }
3283+ scrollForwardButton.addActionListener(this);
3284+ scrollBackwardButton.addActionListener(this);
3285+ tabPane.add(scrollForwardButton);
3286+ tabPane.add(scrollBackwardButton);
3287+ }
3288+
3289+ public void scrollForward(int tabPlacement) {
3290+ Dimension viewSize = viewport.getViewSize();
3291+ Rectangle viewRect = viewport.getViewRect();
3292+
3293+ if (tabPlacement == TOP || tabPlacement == BOTTOM) {
3294+ if (viewRect.width >= viewSize.width - viewRect.x) {
3295+ return; // no room left to scroll
3296+ }
3297+ } else { // tabPlacement == LEFT || tabPlacement == RIGHT
3298+ if (viewRect.height >= viewSize.height - viewRect.y) {
3299+ return;
3300+ }
3301+ }
3302+ setLeadingTabIndex(tabPlacement, leadingTabIndex+1);
3303+ }
3304+
3305+ public void scrollBackward(int tabPlacement) {
3306+ if (leadingTabIndex == 0) {
3307+ return; // no room left to scroll
3308+ }
3309+ setLeadingTabIndex(tabPlacement, leadingTabIndex-1);
3310+ }
3311+
3312+ public void setLeadingTabIndex(int tabPlacement, int index) {
3313+ leadingTabIndex = index;
3314+ Dimension viewSize = viewport.getViewSize();
3315+ Rectangle viewRect = viewport.getViewRect();
3316+
3317+ switch(tabPlacement) {
3318+ case TOP:
3319+ case BOTTOM:
3320+ tabViewPosition.x = leadingTabIndex == 0? 0 : rects[leadingTabIndex].x;
3321+
3322+ if ((viewSize.width - tabViewPosition.x) < viewRect.width) {
3323+ // We've scrolled to the end, so adjust the viewport size
3324+ // to ensure the view position remains aligned on a tab boundary
3325+ Dimension extentSize = new Dimension(viewSize.width - tabViewPosition.x,
3326+ viewRect.height);
3327+ viewport.setExtentSize(extentSize);
3328+ }
3329+ break;
3330+ case LEFT:
3331+ case RIGHT:
3332+ tabViewPosition.y = leadingTabIndex == 0? 0 : rects[leadingTabIndex].y;
3333+
3334+ if ((viewSize.height - tabViewPosition.y) < viewRect.height) {
3335+ // We've scrolled to the end, so adjust the viewport size
3336+ // to ensure the view position remains aligned on a tab boundary
3337+ Dimension extentSize = new Dimension(viewRect.width,
3338+ viewSize.height - tabViewPosition.y);
3339+ viewport.setExtentSize(extentSize);
3340+ }
3341+ }
3342+ viewport.setViewPosition(tabViewPosition);
3343+ }
3344+
3345+ public void stateChanged(ChangeEvent e) {
3346+ updateView();
3347+ }
3348+
3349+ private void updateView() {
3350+ int tabPlacement = tabPane.getTabPlacement();
3351+ int tabCount = tabPane.getTabCount();
3352+ Rectangle vpRect = viewport.getBounds();
3353+ Dimension viewSize = viewport.getViewSize();
3354+ Rectangle viewRect = viewport.getViewRect();
3355+
3356+ leadingTabIndex = getClosestTab(viewRect.x, viewRect.y);
3357+
3358+ // If the tab isn't right aligned, adjust it.
3359+ if (leadingTabIndex + 1 < tabCount) {
3360+ switch (tabPlacement) {
3361+ case TOP:
3362+ case BOTTOM:
3363+ if (rects[leadingTabIndex].x < viewRect.x) {
3364+ leadingTabIndex++;
3365+ }
3366+ break;
3367+ case LEFT:
3368+ case RIGHT:
3369+ if (rects[leadingTabIndex].y < viewRect.y) {
3370+ leadingTabIndex++;
3371+ }
3372+ break;
3373+ }
3374+ }
3375+ Insets contentInsets = getContentBorderInsets(tabPlacement);
3376+ switch(tabPlacement) {
3377+ case LEFT:
3378+ tabPane.repaint(vpRect.x+vpRect.width, vpRect.y,
3379+ contentInsets.left, vpRect.height);
3380+ scrollBackwardButton.setEnabled(
3381+ viewRect.y > 0 && leadingTabIndex > 0);
3382+ scrollForwardButton.setEnabled(
3383+ leadingTabIndex < tabCount-1 &&
3384+ viewSize.height-viewRect.y > viewRect.height);
3385+ break;
3386+ case RIGHT:
3387+ tabPane.repaint(vpRect.x-contentInsets.right, vpRect.y,
3388+ contentInsets.right, vpRect.height);
3389+ scrollBackwardButton.setEnabled(
3390+ viewRect.y > 0 && leadingTabIndex > 0);
3391+ scrollForwardButton.setEnabled(
3392+ leadingTabIndex < tabCount-1 &&
3393+ viewSize.height-viewRect.y > viewRect.height);
3394+ break;
3395+ case BOTTOM:
3396+ tabPane.repaint(vpRect.x, vpRect.y-contentInsets.bottom,
3397+ vpRect.width, contentInsets.bottom);
3398+ scrollBackwardButton.setEnabled(
3399+ viewRect.x > 0 && leadingTabIndex > 0);
3400+ scrollForwardButton.setEnabled(
3401+ leadingTabIndex < tabCount-1 &&
3402+ viewSize.width-viewRect.x > viewRect.width);
3403+ break;
3404+ case TOP:
3405+ default:
3406+ tabPane.repaint(vpRect.x, vpRect.y+vpRect.height,
3407+ vpRect.width, contentInsets.top);
3408+ scrollBackwardButton.setEnabled(
3409+ viewRect.x > 0 && leadingTabIndex > 0);
3410+ scrollForwardButton.setEnabled(
3411+ leadingTabIndex < tabCount-1 &&
3412+ viewSize.width-viewRect.x > viewRect.width);
3413+ }
3414+ }
3415+
3416+ /**
3417+ * ActionListener for the scroll buttons.
3418+ */
3419+ public void actionPerformed(ActionEvent e) {
3420+ ActionMap map = tabPane.getActionMap();
3421+
3422+ if (map != null) {
3423+ String actionKey;
3424+
3425+ if (e.getSource() == scrollForwardButton) {
3426+ actionKey = "scrollTabsForwardAction";
3427+ }
3428+ else {
3429+ actionKey = "scrollTabsBackwardAction";
3430+ }
3431+ Action action = map.get(actionKey);
3432+
3433+ if (action != null && action.isEnabled()) {
3434+ action.actionPerformed(new ActionEvent(tabPane,
3435+ ActionEvent.ACTION_PERFORMED, null, e.getWhen(),
3436+ e.getModifiers()));
3437+ }
3438+ }
3439+ }
3440+
3441+ public String toString() {
3442+ return "viewport.viewSize=" + viewport.getViewSize() + "\n" +
3443+ "viewport.viewRectangle="+viewport.getViewRect()+"\n"+
3444+ "leadingTabIndex="+leadingTabIndex+"\n"+
3445+ "tabViewPosition=" + tabViewPosition;
3446+ }
3447+
3448+ }
3449+
3450+ private class ScrollableTabViewport extends JViewport implements UIResource {
3451+ public ScrollableTabViewport() {
3452+ super();
3453+ setName("TabbedPane.scrollableViewport");
3454+ setScrollMode(SIMPLE_SCROLL_MODE);
3455+ setOpaque(tabPane.isOpaque());
3456+ Color bgColor = UIManager.getColor("TabbedPane.tabAreaBackground");
3457+ if (bgColor == null) {
3458+ bgColor = tabPane.getBackground();
3459+ }
3460+ setBackground(bgColor);
3461+ }
3462+ }
3463+
3464+ private class ScrollableTabPanel extends JPanel implements UIResource {
3465+ public ScrollableTabPanel() {
3466+ super(null);
3467+ setOpaque(tabPane.isOpaque());
3468+ Color bgColor = UIManager.getColor("TabbedPane.tabAreaBackground");
3469+ if (bgColor == null) {
3470+ bgColor = tabPane.getBackground();
3471+ }
3472+ setBackground(bgColor);
3473+ }
3474+ public void paintComponent(Graphics g) {
3475+ super.paintComponent(g);
3476+ BasicTabbedPaneUI.this.paintTabArea(g, tabPane.getTabPlacement(),
3477+ tabPane.getSelectedIndex());
3478+ if (tabScroller.croppedEdge.isParamsSet() && tabContainer == null) {
3479+ Rectangle croppedRect = rects[tabScroller.croppedEdge.getTabIndex()];
3480+ g.translate(croppedRect.x, croppedRect.y);
3481+ tabScroller.croppedEdge.paintComponent(g);
3482+ g.translate(-croppedRect.x, -croppedRect.y);
3483+ }
3484+ }
3485+
3486+ public void doLayout() {
3487+ if (getComponentCount() > 0) {
3488+ Component child = getComponent(0);
3489+ child.setBounds(0, 0, getWidth(), getHeight());
3490+ }
3491+ }
3492+ }
3493+
3494+ private class ScrollableTabButton extends BasicArrowButton implements UIResource,
3495+ SwingConstants {
3496+ public ScrollableTabButton(int direction) {
3497+ super(direction,
3498+ UIManager.getColor("TabbedPane.selected"),
3499+ UIManager.getColor("TabbedPane.shadow"),
3500+ UIManager.getColor("TabbedPane.darkShadow"),
3501+ UIManager.getColor("TabbedPane.highlight"));
3502+ }
3503+ }
3504+
3505+
3506+// Controller: event listeners
3507+
3508+ private class Handler implements ChangeListener, ContainerListener,
3509+ FocusListener, MouseListener, MouseMotionListener,
3510+ PropertyChangeListener {
3511+ //
3512+ // PropertyChangeListener
3513+ //
3514+ public void propertyChange(PropertyChangeEvent e) {
3515+ JTabbedPane pane = (JTabbedPane)e.getSource();
3516+ String name = e.getPropertyName();
3517+ boolean isScrollLayout = scrollableTabLayoutEnabled();
3518+ if (name == "mnemonicAt") {
3519+ updateMnemonics();
3520+ pane.repaint();
3521+ }
3522+ else if (name == "displayedMnemonicIndexAt") {
3523+ pane.repaint();
3524+ }
3525+ else if (name =="indexForTitle") {
3526+ calculatedBaseline = false;
3527+ Integer index = (Integer) e.getNewValue();
3528+ // remove the current index
3529+ // to let updateHtmlViews() insert the correct one
3530+ if (htmlViews != null) {
3531+ htmlViews.removeElementAt(index);
3532+ }
3533+ updateHtmlViews(index);
3534+ } else if (name == "tabLayoutPolicy") {
3535+ BasicTabbedPaneUI.this.uninstallUI(pane);
3536+ BasicTabbedPaneUI.this.installUI(pane);
3537+ calculatedBaseline = false;
3538+ } else if (name == "tabPlacement") {
3539+ if (scrollableTabLayoutEnabled()) {
3540+ tabScroller.createButtons();
3541+ }
3542+ calculatedBaseline = false;
3543+ } else if (name == "opaque" && isScrollLayout) {
3544+ boolean newVal = ((Boolean)e.getNewValue()).booleanValue();
3545+ tabScroller.tabPanel.setOpaque(newVal);
3546+ tabScroller.viewport.setOpaque(newVal);
3547+ } else if (name == "background" && isScrollLayout) {
3548+ Color newVal = (Color)e.getNewValue();
3549+ tabScroller.tabPanel.setBackground(newVal);
3550+ tabScroller.viewport.setBackground(newVal);
3551+ Color newColor = selectedColor == null ? newVal : selectedColor;
3552+ tabScroller.scrollForwardButton.setBackground(newColor);
3553+ tabScroller.scrollBackwardButton.setBackground(newColor);
3554+ } else if (name == "indexForTabComponent") {
3555+ if (tabContainer != null) {
3556+ tabContainer.removeUnusedTabComponents();
3557+ }
3558+ Component c = tabPane.getTabComponentAt(
3559+ (Integer)e.getNewValue());
3560+ if (c != null) {
3561+ if (tabContainer == null) {
3562+ installTabContainer();
3563+ } else {
3564+ tabContainer.add(c);
3565+ }
3566+ }
3567+ tabPane.revalidate();
3568+ tabPane.repaint();
3569+ calculatedBaseline = false;
3570+ } else if (name == "indexForNullComponent") {
3571+ isRunsDirty = true;
3572+ updateHtmlViews((Integer)e.getNewValue());
3573+ } else if (name == "font") {
3574+ calculatedBaseline = false;
3575+ }
3576+ }
3577+
3578+ private void updateHtmlViews(int index) {
3579+ String title = tabPane.getTitleAt(index);
3580+ boolean isHTML = BasicHTML.isHTMLString(title);
3581+ if (isHTML) {
3582+ if (htmlViews==null) { // Initialize vector
3583+ htmlViews = createHTMLVector();
3584+ } else { // Vector already exists
3585+ View v = BasicHTML.createHTMLView(tabPane, title);
3586+ htmlViews.insertElementAt(v, index);
3587+ }
3588+ } else { // Not HTML
3589+ if (htmlViews!=null) { // Add placeholder
3590+ htmlViews.insertElementAt(null, index);
3591+ } // else nada!
3592+ }
3593+ updateMnemonics();
3594+ }
3595+
3596+ //
3597+ // ChangeListener
3598+ //
3599+ public void stateChanged(ChangeEvent e) {
3600+ JTabbedPane tabPane = (JTabbedPane)e.getSource();
3601+ tabPane.revalidate();
3602+ tabPane.repaint();
3603+
3604+ setFocusIndex(tabPane.getSelectedIndex(), false);
3605+
3606+ if (scrollableTabLayoutEnabled()) {
3607+ int index = tabPane.getSelectedIndex();
3608+ if (index < rects.length && index != -1) {
3609+ tabScroller.tabPanel.scrollRectToVisible(
3610+ (Rectangle)rects[index].clone());
3611+ }
3612+ }
3613+ }
3614+
3615+ //
3616+ // MouseListener
3617+ //
3618+ public void mouseClicked(MouseEvent e) {
3619+ }
3620+
3621+ public void mouseReleased(MouseEvent e) {
3622+ }
3623+
3624+ public void mouseEntered(MouseEvent e) {
3625+ setRolloverTab(e.getX(), e.getY());
3626+ }
3627+
3628+ public void mouseExited(MouseEvent e) {
3629+ setRolloverTab(-1);
3630+ }
3631+
3632+ public void mousePressed(MouseEvent e) {
3633+ if (!tabPane.isEnabled()) {
3634+ return;
3635+ }
3636+ int tabIndex = tabForCoordinate(tabPane, e.getX(), e.getY());
3637+ if (tabIndex >= 0 && tabPane.isEnabledAt(tabIndex)) {
3638+ if (tabIndex != tabPane.getSelectedIndex()) {
3639+ // Clicking on unselected tab, change selection, do NOT
3640+ // request focus.
3641+ // This will trigger the focusIndex to change by way
3642+ // of stateChanged.
3643+ tabPane.setSelectedIndex(tabIndex);
3644+ }
3645+ else if (tabPane.isRequestFocusEnabled()) {
3646+ // Clicking on selected tab, try and give the tabbedpane
3647+ // focus. Repaint will occur in focusGained.
3648+ tabPane.requestFocus();
3649+ }
3650+ }
3651+ }
3652+
3653+ //
3654+ // MouseMotionListener
3655+ //
3656+ public void mouseDragged(MouseEvent e) {
3657+ }
3658+
3659+ public void mouseMoved(MouseEvent e) {
3660+ setRolloverTab(e.getX(), e.getY());
3661+ }
3662+
3663+ //
3664+ // FocusListener
3665+ //
3666+ public void focusGained(FocusEvent e) {
3667+ setFocusIndex(tabPane.getSelectedIndex(), true);
3668+ }
3669+ public void focusLost(FocusEvent e) {
3670+ repaintTab(focusIndex);
3671+ }
3672+
3673+
3674+ //
3675+ // ContainerListener
3676+ //
3677+ /* GES 2/3/99:
3678+ The container listener code was added to support HTML
3679+ rendering of tab titles.
3680+
3681+ Ideally, we would be able to listen for property changes
3682+ when a tab is added or its text modified. At the moment
3683+ there are no such events because the Beans spec doesn't
3684+ allow 'indexed' property changes (i.e. tab 2's text changed
3685+ from A to B).
3686+
3687+ In order to get around this, we listen for tabs to be added
3688+ or removed by listening for the container events. we then
3689+ queue up a runnable (so the component has a chance to complete
3690+ the add) which checks the tab title of the new component to see
3691+ if it requires HTML rendering.
3692+
3693+ The Views (one per tab title requiring HTML rendering) are
3694+ stored in the htmlViews Vector, which is only allocated after
3695+ the first time we run into an HTML tab. Note that this vector
3696+ is kept in step with the number of pages, and nulls are added
3697+ for those pages whose tab title do not require HTML rendering.
3698+
3699+ This makes it easy for the paint and layout code to tell
3700+ whether to invoke the HTML engine without having to check
3701+ the string during time-sensitive operations.
3702+
3703+ When we have added a way to listen for tab additions and
3704+ changes to tab text, this code should be removed and
3705+ replaced by something which uses that. */
3706+
3707+ public void componentAdded(ContainerEvent e) {
3708+ JTabbedPane tp = (JTabbedPane)e.getContainer();
3709+ Component child = e.getChild();
3710+ if (child instanceof UIResource) {
3711+ return;
3712+ }
3713+ isRunsDirty = true;
3714+ updateHtmlViews(tp.indexOfComponent(child));
3715+ }
3716+ public void componentRemoved(ContainerEvent e) {
3717+ JTabbedPane tp = (JTabbedPane)e.getContainer();
3718+ Component child = e.getChild();
3719+ if (child instanceof UIResource) {
3720+ return;
3721+ }
3722+
3723+ // NOTE 4/15/2002 (joutwate):
3724+ // This fix is implemented using client properties since there is
3725+ // currently no IndexPropertyChangeEvent. Once
3726+ // IndexPropertyChangeEvents have been added this code should be
3727+ // modified to use it.
3728+ Integer indexObj =
3729+ (Integer)tp.getClientProperty("__index_to_remove__");
3730+ if (indexObj != null) {
3731+ int index = indexObj.intValue();
3732+ if (htmlViews != null && htmlViews.size() > index) {
3733+ htmlViews.removeElementAt(index);
3734+ }
3735+ tp.putClientProperty("__index_to_remove__", null);
3736+ }
3737+ isRunsDirty = true;
3738+ updateMnemonics();
3739+
3740+ validateFocusIndex();
3741+ }
3742+ }
3743+
3744+ /**
3745+ * This class should be treated as a &quot;protected&quot; inner class.
3746+ * Instantiate it only within subclasses of BasicTabbedPaneUI.
3747+ */
3748+ public class PropertyChangeHandler implements PropertyChangeListener {
3749+ // NOTE: This class exists only for backward compatability. All
3750+ // its functionality has been moved into Handler. If you need to add
3751+ // new functionality add it to the Handler, but make sure this
3752+ // class calls into the Handler.
3753+ public void propertyChange(PropertyChangeEvent e) {
3754+ getHandler().propertyChange(e);
3755+ }
3756+ }
3757+
3758+ /**
3759+ * This class should be treated as a &quot;protected&quot; inner class.
3760+ * Instantiate it only within subclasses of BasicTabbedPaneUI.
3761+ */
3762+ public class TabSelectionHandler implements ChangeListener {
3763+ // NOTE: This class exists only for backward compatability. All
3764+ // its functionality has been moved into Handler. If you need to add
3765+ // new functionality add it to the Handler, but make sure this
3766+ // class calls into the Handler.
3767+ public void stateChanged(ChangeEvent e) {
3768+ getHandler().stateChanged(e);
3769+ }
3770+ }
3771+
3772+ /**
3773+ * This class should be treated as a &quot;protected&quot; inner class.
3774+ * Instantiate it only within subclasses of BasicTabbedPaneUI.
3775+ */
3776+ public class MouseHandler extends MouseAdapter {
3777+ // NOTE: This class exists only for backward compatability. All
3778+ // its functionality has been moved into Handler. If you need to add
3779+ // new functionality add it to the Handler, but make sure this
3780+ // class calls into the Handler.
3781+ public void mousePressed(MouseEvent e) {
3782+ getHandler().mousePressed(e);
3783+ }
3784+ }
3785+
3786+ /**
3787+ * This class should be treated as a &quot;protected&quot; inner class.
3788+ * Instantiate it only within subclasses of BasicTabbedPaneUI.
3789+ */
3790+ public class FocusHandler extends FocusAdapter {
3791+ // NOTE: This class exists only for backward compatability. All
3792+ // its functionality has been moved into Handler. If you need to add
3793+ // new functionality add it to the Handler, but make sure this
3794+ // class calls into the Handler.
3795+ public void focusGained(FocusEvent e) {
3796+ getHandler().focusGained(e);
3797+ }
3798+ public void focusLost(FocusEvent e) {
3799+ getHandler().focusLost(e);
3800+ }
3801+ }
3802+
3803+ private Vector<View> createHTMLVector() {
3804+ Vector<View> htmlViews = new Vector<View>();
3805+ int count = tabPane.getTabCount();
3806+ if (count>0) {
3807+ for (int i=0 ; i<count; i++) {
3808+ String title = tabPane.getTitleAt(i);
3809+ if (BasicHTML.isHTMLString(title)) {
3810+ htmlViews.addElement(BasicHTML.createHTMLView(tabPane, title));
3811+ } else {
3812+ htmlViews.addElement(null);
3813+ }
3814+ }
3815+ }
3816+ return htmlViews;
3817+ }
3818+
3819+ private class TabContainer extends JPanel implements UIResource {
3820+ private boolean notifyTabbedPane = true;
3821+
3822+ public TabContainer() {
3823+ super(null);
3824+ setOpaque(false);
3825+ }
3826+
3827+ public void remove(Component comp) {
3828+ int index = tabPane.indexOfTabComponent(comp);
3829+ super.remove(comp);
3830+ if (notifyTabbedPane && index != -1) {
3831+ tabPane.setTabComponentAt(index, null);
3832+ }
3833+ }
3834+
3835+ private void removeUnusedTabComponents() {
3836+ for (Component c : getComponents()) {
3837+ if (!(c instanceof UIResource)) {
3838+ int index = tabPane.indexOfTabComponent(c);
3839+ if (index == -1) {
3840+ super.remove(c);
3841+ }
3842+ }
3843+ }
3844+ }
3845+
3846+ public boolean isOptimizedDrawingEnabled() {
3847+ return tabScroller != null && !tabScroller.croppedEdge.isParamsSet();
3848+ }
3849+
3850+ public void doLayout() {
3851+ // We layout tabComponents in JTabbedPane's layout manager
3852+ // and use this method as a hook for repainting tabs
3853+ // to update tabs area e.g. when the size of tabComponent was changed
3854+ if (scrollableTabLayoutEnabled()) {
3855+ tabScroller.tabPanel.repaint();
3856+ tabScroller.updateView();
3857+ } else {
3858+ tabPane.repaint(getBounds());
3859+ }
3860+ }
3861+ }
3862+
3863+ private class CroppedEdge extends JPanel implements UIResource {
3864+ private Shape shape;
3865+ private int tabIndex;
3866+ private int cropline;
3867+ private int cropx, cropy;
3868+
3869+ public CroppedEdge() {
3870+ setOpaque(false);
3871+ }
3872+
3873+ public void setParams(int tabIndex, int cropline, int cropx, int cropy) {
3874+ this.tabIndex = tabIndex;
3875+ this.cropline = cropline;
3876+ this.cropx = cropx;
3877+ this.cropy = cropy;
3878+ Rectangle tabRect = rects[tabIndex];
3879+ setBounds(tabRect);
3880+ shape = createCroppedTabShape(tabPane.getTabPlacement(), tabRect, cropline);
3881+ if (getParent() == null && tabContainer != null) {
3882+ tabContainer.add(this, 0);
3883+ }
3884+ }
3885+
3886+ public void resetParams() {
3887+ shape = null;
3888+ if (getParent() == tabContainer && tabContainer != null) {
3889+ tabContainer.remove(this);
3890+ }
3891+ }
3892+
3893+ public boolean isParamsSet() {
3894+ return shape != null;
3895+ }
3896+
3897+ public int getTabIndex() {
3898+ return tabIndex;
3899+ }
3900+
3901+ public int getCropline() {
3902+ return cropline;
3903+ }
3904+
3905+ public int getCroppedSideWidth() {
3906+ return 3;
3907+ }
3908+
3909+ private Color getBgColor() {
3910+ Component parent = tabPane.getParent();
3911+ if (parent != null) {
3912+ Color bg = parent.getBackground();
3913+ if (bg != null) {
3914+ return bg;
3915+ }
3916+ }
3917+ return UIManager.getColor("control");
3918+ }
3919+
3920+ protected void paintComponent(Graphics g) {
3921+ super.paintComponent(g);
3922+ if (isParamsSet() && g instanceof Graphics2D) {
3923+ Graphics2D g2 = (Graphics2D) g;
3924+ g2.clipRect(0, 0, getWidth(), getHeight());
3925+ g2.setColor(getBgColor());
3926+ g2.translate(cropx, cropy);
3927+ g2.fill(shape);
3928+ paintCroppedTabEdge(g);
3929+ g2.translate(-cropx, -cropy);
3930+ }
3931+ }
3932+ }
3933+}