1 /* BasicTabbedPaneUI.java --
2 Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package javax.swing.plaf.basic;
41 import java.awt.Color;
42 import java.awt.Component;
43 import java.awt.Container;
44 import java.awt.Dimension;
46 import java.awt.FontMetrics;
47 import java.awt.Graphics;
48 import java.awt.Insets;
49 import java.awt.LayoutManager;
50 import java.awt.Point;
51 import java.awt.Rectangle;
52 import java.awt.event.ActionEvent;
53 import java.awt.event.FocusAdapter;
54 import java.awt.event.FocusEvent;
55 import java.awt.event.FocusListener;
56 import java.awt.event.MouseAdapter;
57 import java.awt.event.MouseEvent;
58 import java.awt.event.MouseListener;
59 import java.beans.PropertyChangeEvent;
60 import java.beans.PropertyChangeListener;
62 import javax.swing.AbstractAction;
63 import javax.swing.ActionMap;
64 import javax.swing.Icon;
65 import javax.swing.InputMap;
66 import javax.swing.JComponent;
67 import javax.swing.JPanel;
68 import javax.swing.JTabbedPane;
69 import javax.swing.JViewport;
70 import javax.swing.KeyStroke;
71 import javax.swing.LookAndFeel;
72 import javax.swing.SwingConstants;
73 import javax.swing.SwingUtilities;
74 import javax.swing.UIManager;
75 import javax.swing.event.ChangeEvent;
76 import javax.swing.event.ChangeListener;
77 import javax.swing.plaf.ActionMapUIResource;
78 import javax.swing.plaf.ComponentUI;
79 import javax.swing.plaf.TabbedPaneUI;
80 import javax.swing.plaf.UIResource;
81 import javax.swing.text.View;
84 * This is the Basic Look and Feel's UI delegate for JTabbedPane.
86 * @author Lillian Angel (langel@redhat.com)
87 * @author Kim Ho (kho@redhat.com)
88 * @author Roman Kennke (kennke@aicas.com)
89 * @author Robert Schuster (robertschuster@fsfe.org)
91 public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
94 static class NavigateAction extends AbstractAction
98 NavigateAction(String name, int dir)
104 public void actionPerformed(ActionEvent event)
106 JTabbedPane tp = (JTabbedPane) event.getSource();
107 BasicTabbedPaneUI ui = (BasicTabbedPaneUI) tp.getUI();
109 ui.navigateSelectedTab(direction);
114 static class NavigatePageDownAction extends AbstractAction
117 public NavigatePageDownAction()
119 super("navigatePageDown");
122 public void actionPerformed(ActionEvent event)
124 JTabbedPane tp = (JTabbedPane) event.getSource();
125 BasicTabbedPaneUI ui = (BasicTabbedPaneUI) tp.getUI();
127 int i = tp.getSelectedIndex();
132 ui.selectNextTabInRun(i);
137 static class NavigatePageUpAction extends AbstractAction
140 public NavigatePageUpAction()
142 super("navigatePageUp");
145 public void actionPerformed(ActionEvent event)
147 JTabbedPane tp = (JTabbedPane) event.getSource();
148 BasicTabbedPaneUI ui = (BasicTabbedPaneUI) tp.getUI();
150 int i = tp.getSelectedIndex();
155 ui.selectPreviousTabInRun(i);
160 static class RequestFocusAction extends AbstractAction
163 public RequestFocusAction()
165 super("requestFocus");
168 public void actionPerformed(ActionEvent event)
170 ((JTabbedPane) event.getSource()).requestFocus();
175 static class RequestFocusForVisibleComponentAction extends AbstractAction
178 public RequestFocusForVisibleComponentAction()
180 super("requestFocusForVisibleComponent");
183 public void actionPerformed(ActionEvent event)
185 JTabbedPane tp = (JTabbedPane) event.getSource();
187 // FIXME: This should select a suitable component within
188 // the tab content. However I dont know whether we have
189 // to search for this component or wether the called is
190 // supposed to do that.
191 tp.getSelectedComponent().requestFocus();
197 * A helper class that handles focus.
198 * <p>The purpose of this class is to implement a more flexible focus
199 * handling for the tabbed pane, which is used to determine whether the
200 * focus indicator should be painted or not. When in scrolling layout
201 * mode the area containing the tabs is a scrollpane, so simply testing
202 * whether the tabbed pane has the focus does not work.</p>
203 * <p>The <code>FocusHandler</code> is installed on the scrollpane and
204 * the tabbed pane and sets the variable <code>hasFocus</code> to
205 * <code>false</code> only when both components do not hold the focus.</p>
207 * @specnote Apparently this class was intended to be protected,
208 * but was made public by a compiler bug and is now
209 * public for compatibility.
211 public class FocusHandler extends FocusAdapter
214 * This method is called when the component gains focus.
216 * @param e The FocusEvent.
218 public void focusGained(FocusEvent e)
220 Object source = e.getSource();
221 if (source == panel )
222 tabPane.requestFocus();
223 else if (source == tabPane)
228 * This method is called when the component loses focus.
230 * @param e The FocusEvent.
232 public void focusLost(FocusEvent e)
234 if (e.getOppositeComponent() == tabPane.getSelectedComponent())
235 tabPane.requestFocus();
236 else if (e.getSource() == tabPane)
242 * A helper class for determining if mouse presses occur inside tabs and
243 * sets the index appropriately. In SCROLL_TAB_MODE, this class also
244 * handles the mouse clicks on the scrolling buttons.
246 * @specnote Apparently this class was intended to be protected,
247 * but was made public by a compiler bug and is now
248 * public for compatibility.
250 public class MouseHandler extends MouseAdapter
252 public void mouseReleased(MouseEvent e)
254 Object s = e.getSource();
256 // Event may originate from the viewport in
257 // SCROLL_TAB_LAYOUT mode. It is redisptached
258 // through the tabbed pane then.
259 if (tabPane != e.getSource())
267 * This method is called when the mouse is pressed. The index cannot
268 * change to a tab that is not enabled.
270 * @param e The MouseEvent.
272 public void mousePressed(MouseEvent e)
274 Object s = e.getSource();
276 // Event may originate from the viewport in
277 // SCROLL_TAB_LAYOUT mode. It is redisptached
278 // through the tabbed pane then.
279 if (tabPane != e.getSource())
285 int placement = tabPane.getTabPlacement();
289 if(!incrButton.isEnabled())
292 currentScrollLocation++;
296 case JTabbedPane.TOP:
297 case JTabbedPane.BOTTOM:
298 currentScrollOffset = getTabAreaInsets(placement).left;
299 for (int i = 0; i < currentScrollLocation; i++)
300 currentScrollOffset += rects[i].width;
303 currentScrollOffset = getTabAreaInsets(placement).top;
304 for (int i = 0; i < currentScrollLocation; i++)
305 currentScrollOffset += rects[i].height;
309 updateViewPosition();
314 else if (s == decrButton)
316 if(!decrButton.isEnabled())
319 // The scroll location may be zero but the offset
320 // greater than zero because of an adjustement to
321 // make a partially visible tab completely visible.
322 if (currentScrollLocation > 0)
323 currentScrollLocation--;
325 // Set the offset back to 0 and recompute it.
326 currentScrollOffset = 0;
330 case JTabbedPane.TOP:
331 case JTabbedPane.BOTTOM:
332 // Take the tab area inset into account.
333 if (currentScrollLocation > 0)
334 currentScrollOffset = getTabAreaInsets(placement).left;
335 // Recompute scroll offset.
336 for (int i = 0; i < currentScrollLocation; i++)
337 currentScrollOffset += rects[i].width;
340 // Take the tab area inset into account.
341 if (currentScrollLocation > 0)
342 currentScrollOffset = getTabAreaInsets(placement).top;
344 for (int i = 0; i < currentScrollLocation; i++)
345 currentScrollOffset += rects[i].height;
348 updateViewPosition();
353 else if (tabPane.isEnabled())
355 int index = tabForCoordinate(tabPane, e.getX(), e.getY());
356 if (!tabPane.isEnabledAt(index))
359 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT
362 scrollTab(index, placement);
364 tabPane.setSelectedIndex(index);
369 tabPane.setSelectedIndex(index);
370 tabPane.revalidate();
379 * Receives notification when the mouse pointer has entered the tabbed
382 * @param e the mouse event
384 public void mouseEntered(MouseEvent e)
386 Object s = e.getSource();
388 // Event may originate from the viewport in
389 // SCROLL_TAB_LAYOUT mode. It is redisptached
390 // through the tabbed pane then.
391 if (tabPane != e.getSource())
397 int tabIndex = tabForCoordinate(tabPane, e.getX(), e.getY());
398 setRolloverTab(tabIndex);
402 * Receives notification when the mouse pointer has exited the tabbed
405 * @param e the mouse event
407 public void mouseExited(MouseEvent e)
409 Object s = e.getSource();
411 // Event may originate from the viewport in
412 // SCROLL_TAB_LAYOUT mode. It is redisptached
413 // through the tabbed pane then.
414 if (tabPane != e.getSource())
424 * Receives notification when the mouse pointer has moved over the tabbed
427 * @param ev the mouse event
429 public void mouseMoved(MouseEvent ev)
431 Object s = ev.getSource();
433 if (tabPane != ev.getSource())
435 ev.setSource(tabPane);
436 tabPane.dispatchEvent(ev);
441 int tabIndex = tabForCoordinate(tabPane, ev.getX(), ev.getY());
442 setRolloverTab(tabIndex);
445 /** Modifies the mouse event to originate from
446 * the tabbed pane and redispatches it.
450 void redispatchEvent(MouseEvent me)
452 me.setSource(tabPane);
453 Point viewPos = viewport.getViewPosition();
454 viewPos.x -= viewport.getX();
455 viewPos.y -= viewport.getY();
456 me.translatePoint(-viewPos.x, -viewPos.y);
457 tabPane.dispatchEvent(me);
459 me.translatePoint(viewPos.x, viewPos.y);
465 * This class handles PropertyChangeEvents fired from the JTabbedPane.
467 * @specnote Apparently this class was intended to be protected,
468 * but was made public by a compiler bug and is now
469 * public for compatibility.
471 public class PropertyChangeHandler implements PropertyChangeListener
474 * This method is called whenever one of the properties of the JTabbedPane
477 * @param e The PropertyChangeEvent.
479 public void propertyChange(PropertyChangeEvent e)
483 if (e.getPropertyName().equals("tabLayoutPolicy"))
485 currentScrollLocation = currentScrollOffset = 0;
487 layoutManager = createLayoutManager();
489 tabPane.setLayout(layoutManager);
491 else if (e.getPropertyName().equals("tabPlacement")
492 && tabPane.getTabLayoutPolicy()
493 == JTabbedPane.SCROLL_TAB_LAYOUT)
495 incrButton = createIncreaseButton();
496 decrButton = createDecreaseButton();
498 // If the tab placement value was changed of a tabbed pane
499 // in SCROLL_TAB_LAYOUT mode we investigate the change to
500 // implement the following behavior which was observed in
502 // The scrolling offset will be reset if we change to
503 // a direction which is orthogonal to the current
504 // direction and stays the same if it is parallel.
506 int oldPlacement = ((Integer) e.getOldValue()).intValue();
507 int newPlacement = ((Integer) e.getNewValue()).intValue();
508 switch (newPlacement)
510 case JTabbedPane.TOP:
511 case JTabbedPane.BOTTOM:
512 if (oldPlacement == JTabbedPane.TOP
513 || oldPlacement == JTabbedPane.BOTTOM)
516 currentScrollOffset = getTabAreaInsets(newPlacement).left;
519 if (oldPlacement == JTabbedPane.LEFT
520 || oldPlacement == JTabbedPane.RIGHT)
523 currentScrollOffset = getTabAreaInsets(newPlacement).top;
526 updateViewPosition();
531 tabPane.revalidate();
537 * A LayoutManager responsible for placing all the tabs and the visible
538 * component inside the JTabbedPane. This class is only used for
541 * @specnote Apparently this class was intended to be protected,
542 * but was made public by a compiler bug and is now
543 * public for compatibility.
545 public class TabbedPaneLayout implements LayoutManager
548 * This method is called when a component is added to the JTabbedPane.
550 * @param name The name of the component.
551 * @param comp The component being added.
553 public void addLayoutComponent(String name, Component comp)
559 * This method is called when the rectangles need to be calculated. It
560 * also fixes the size of the visible component.
562 public void calculateLayoutInfo()
564 int count = tabPane.getTabCount();
565 assureRectsCreated(count);
566 calculateTabRects(tabPane.getTabPlacement(), count);
567 tabRunsDirty = false;
571 * This method calculates the size of the the JTabbedPane.
573 * @param minimum Whether the JTabbedPane will try to be as small as it
576 * @return The desired size of the JTabbedPane.
578 protected Dimension calculateSize(boolean minimum)
580 int tabPlacement = tabPane.getTabPlacement();
587 // Find out the minimum/preferred size to display the largest child
588 // of the tabbed pane.
589 int count = tabPane.getTabCount();
590 for (int i = 0; i < count; i++)
592 c = tabPane.getComponentAt(i);
595 dims = minimum ? c.getMinimumSize() : c.getPreferredSize();
598 height = Math.max(height, dims.height);
599 width = Math.max(width, dims.width);
603 Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
604 if (tabPlacement == SwingConstants.TOP
605 || tabPlacement == SwingConstants.BOTTOM)
607 width = Math.max(calculateMaxTabWidth(tabPlacement), width);
609 height += preferredTabAreaHeight(tabPlacement,
610 width - tabAreaInsets.left
611 - tabAreaInsets.right);
615 height = Math.max(calculateMaxTabHeight(tabPlacement), height);
617 width += preferredTabAreaWidth(tabPlacement,
618 height - tabAreaInsets.top
619 - tabAreaInsets.bottom);
622 Insets tabPaneInsets = tabPane.getInsets();
623 return new Dimension(width + tabPaneInsets.left + tabPaneInsets.right,
624 height + tabPaneInsets.top + tabPaneInsets.bottom);
627 // if tab placement is LEFT OR RIGHT, they share width.
628 // if tab placement is TOP OR BOTTOM, they share height
629 // PRE STEP: finds the default sizes for the labels as well as their
631 // AND where they will be placed within the run system.
632 // 1. calls normalizeTab Runs.
633 // 2. calls rotate tab runs.
634 // 3. pads the tab runs.
635 // 4. pads the selected tab.
638 * This method is called to calculate the tab rectangles. This method
639 * will calculate the size and position of all rectangles (taking into
640 * account which ones should be in which tab run). It will pad them and
641 * normalize them as necessary.
643 * @param tabPlacement The JTabbedPane's tab placement.
644 * @param tabCount The run the current selection is in.
646 protected void calculateTabRects(int tabPlacement, int tabCount)
648 Insets insets = tabPane.getInsets();
649 Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
650 Dimension size = tabPane.getSize();
652 // The coordinates of the upper left corner of the tab area.
655 // The location at which the runs must be broken.
658 // Calculate the bounds for the tab area.
659 switch (tabPlacement)
662 maxTabWidth = calculateMaxTabWidth(tabPlacement);
663 x = insets.left + tabAreaInsets.left;
664 y = insets.top + tabAreaInsets.top;
665 breakAt = size.height - (insets.bottom + tabAreaInsets.bottom);
668 maxTabWidth = calculateMaxTabWidth(tabPlacement);
669 x = size.width - (insets.right + tabAreaInsets.right)
671 y = insets.top + tabAreaInsets.top;
672 breakAt = size.height - (insets.bottom + tabAreaInsets.bottom);
675 maxTabHeight = calculateMaxTabHeight(tabPlacement);
676 x = insets.left + tabAreaInsets.left;
677 y = size.height - (insets.bottom + tabAreaInsets.bottom)
679 breakAt = size.width - (insets.right + tabAreaInsets.right);
683 maxTabHeight = calculateMaxTabHeight(tabPlacement);
684 x = insets.left + tabAreaInsets.left;
685 y = insets.top + tabAreaInsets.top;
686 breakAt = size.width - (insets.right + tabAreaInsets.right);
693 FontMetrics fm = getFontMetrics();
696 int selectedIndex = tabPane.getSelectedIndex();
697 if (selectedIndex < 0)
702 // Go through all the tabs and build the tab runs.
703 if (tabPlacement == SwingConstants.TOP
704 || tabPlacement == SwingConstants.BOTTOM)
706 for (int i = 0; i < tabCount; i++)
711 rect.x = rects[i - 1].x + rects[i - 1].width;
720 rect.width = calculateTabWidth(tabPlacement, i, fm);
721 maxTabWidth = Math.max(maxTabWidth, rect.width);
723 if (rect.x != 2 + insets.left && rect.x + rect.width > breakAt)
725 if (runCount > tabRuns.length - 1)
726 expandTabRunsArray();
727 tabRuns[runCount] = i;
733 rect.height = maxTabHeight;
734 if (i == selectedIndex)
735 selectedRun = runCount - 1;
740 for (int i = 0; i < tabCount; i++)
745 rect.y = rects[i - 1].y + rects[i - 1].height;
754 rect.height = calculateTabHeight(tabPlacement, i,
756 maxTabHeight = Math.max(maxTabHeight, rect.height);
758 if (rect.y != 2 + insets.top && rect.y + rect.height > breakAt)
760 if (runCount > tabRuns.length - 1)
761 expandTabRunsArray();
762 tabRuns[runCount] = i;
768 rect.width = maxTabWidth;
770 if (i == selectedIndex)
771 selectedRun = runCount - 1;
778 if (tabPlacement == SwingConstants.TOP
779 || tabPlacement == SwingConstants.BOTTOM)
783 normalizeTabRuns(tabPlacement, tabCount, start, breakAt);
784 selectedRun = getRunForTab(tabCount, selectedIndex);
785 if (shouldRotateTabRuns(tabPlacement))
787 rotateTabRuns(tabPlacement, selectedRun);
791 // Suppress padding if we have only one tab run.
796 int tabRunOverlay = getTabRunOverlay(tabPlacement);
797 for (int i = runCount - 1; i >= 0; --i)
799 int start = tabRuns[i];
801 if (i == runCount - 1)
805 int next = tabRuns[nextIndex];
806 int end = next != 0 ? next - 1 : tabCount - 1;
807 if (tabPlacement == SwingConstants.TOP
808 || tabPlacement == SwingConstants.BOTTOM)
810 for (int j = start; j <= end; ++j)
814 rect.x += getTabRunIndent(tabPlacement, i);
816 if (shouldPadTabRun(tabPlacement, i))
818 padTabRun(tabPlacement, start, end, breakAt);
820 if (tabPlacement == BOTTOM)
821 y -= maxTabHeight - tabRunOverlay;
823 y += maxTabHeight - tabRunOverlay;
827 for (int j = start; j <= end; ++j)
831 rect.y += getTabRunIndent(tabPlacement, i);
833 if (shouldPadTabRun(tabPlacement, i))
835 padTabRun(tabPlacement, start, end, breakAt);
837 if (tabPlacement == RIGHT)
838 x -= maxTabWidth - tabRunOverlay;
840 x += maxTabWidth - tabRunOverlay;
844 padSelectedTab(tabPlacement, selectedIndex);
848 * This method is called when the JTabbedPane is laid out in
849 * WRAP_TAB_LAYOUT. It calls calculateLayoutInfo to find the positions
850 * of all its components.
852 * @param parent The Container to lay out.
854 public void layoutContainer(Container parent)
856 calculateLayoutInfo();
858 int tabPlacement = tabPane.getTabPlacement();
859 Insets insets = tabPane.getInsets();
861 int selectedIndex = tabPane.getSelectedIndex();
863 Component selectedComponent = null;
864 if (selectedIndex >= 0)
865 selectedComponent = tabPane.getComponentAt(selectedIndex);
866 // The RI doesn't seem to change the component if the new selected
867 // component == null. This is probably so that applications can add
868 // one single component for every tab.
869 if (selectedComponent != null)
871 setVisibleComponent(selectedComponent);
874 int childCount = tabPane.getComponentCount();
879 int tabAreaWidth = 0;
880 int tabAreaHeight = 0;
881 switch (tabPlacement)
884 tabAreaWidth = calculateTabAreaWidth(tabPlacement, runCount,
886 compX = tabAreaWidth + insets.left + contentBorderInsets.left;
887 compY = insets.top + contentBorderInsets.top;
890 tabAreaWidth = calculateTabAreaWidth(tabPlacement, runCount,
892 compX = insets.left + contentBorderInsets.left;
893 compY = insets.top + contentBorderInsets.top;
896 tabAreaHeight = calculateTabAreaHeight(tabPlacement, runCount,
898 compX = insets.left + contentBorderInsets.left;
899 compY = insets.top + contentBorderInsets.top;
903 tabAreaHeight = calculateTabAreaHeight(tabPlacement, runCount,
906 compX = insets.left + contentBorderInsets.left;
907 compY = tabAreaHeight + insets.top + contentBorderInsets.top;
909 Rectangle bounds = tabPane.getBounds();
910 int compWidth = bounds.width - tabAreaWidth - insets.left
911 - insets.right - contentBorderInsets.left
912 - contentBorderInsets.right;
913 int compHeight = bounds.height - tabAreaHeight - insets.top
914 - insets.bottom - contentBorderInsets.top
915 - contentBorderInsets.bottom;
918 for (int i = 0; i < childCount; ++i)
920 Component c = tabPane.getComponent(i);
921 c.setBounds(compX, compY, compWidth, compHeight);
927 * This method returns the minimum layout size for the given container.
929 * @param parent The container that is being sized.
931 * @return The minimum size.
933 public Dimension minimumLayoutSize(Container parent)
935 return calculateSize(true);
938 // If there is more free space in an adjacent run AND the tab
939 // in the run can fit in the adjacent run, move it. This method
940 // is not perfect, it is merely an approximation.
941 // If you play around with Sun's JTabbedPane, you'll see that
942 // it does do some pretty strange things with regards to not moving tabs
943 // that should be moved.
944 // start = the x position where the tabs will begin
945 // max = the maximum position of where the tabs can go to
946 // (tabAreaInsets.left + the width of the tab area)
949 * This method tries to "even out" the number of tabs in each run based on
952 * @param tabPlacement The JTabbedPane's tab placement.
953 * @param tabCount The number of tabs.
954 * @param start The x position where the tabs will begin.
955 * @param max The maximum x position where the tab can run to.
957 protected void normalizeTabRuns(int tabPlacement, int tabCount, int start,
960 boolean horizontal = tabPlacement == TOP || tabPlacement == BOTTOM;
961 int currentRun = runCount - 1;
962 double weight = 1.25;
963 for (boolean adjust = true; adjust == true;)
965 int last = lastTabInRun(tabCount, currentRun);
966 int prevLast = lastTabInRun(tabCount, currentRun - 1);
971 end = rects[last].x + rects[last].width;
972 prevLength = (int) (maxTabWidth * weight);
976 end = rects[last].y + rects[last].height;
977 prevLength = (int) (maxTabWidth * weight * 2);
979 if (max - end > prevLength)
981 tabRuns[currentRun] = prevLast;
983 rects[prevLast].x = start;
985 rects[prevLast].y = start;
986 for (int i = prevLast + 1; i <= last; i++)
989 rects[i].x = rects[i - 1].x + rects[i - 1].width;
991 rects[i].y = rects[i - 1].y + rects[i - 1].height;
994 else if (currentRun == runCount - 1)
996 if (currentRun - 1 > 0)
1000 // Check again, but with higher ratio to avoid
1001 // clogging up the last run.
1002 currentRun = runCount - 1;
1009 * This method pads the tab at the selected index by the selected tab pad
1010 * insets (so that it looks larger).
1012 * @param tabPlacement The placement of the tabs.
1013 * @param selectedIndex The selected index.
1015 protected void padSelectedTab(int tabPlacement, int selectedIndex)
1017 Insets insets = getSelectedTabPadInsets(tabPlacement);
1018 rects[selectedIndex].x -= insets.left;
1019 rects[selectedIndex].y -= insets.top;
1020 rects[selectedIndex].width += insets.left + insets.right;
1021 rects[selectedIndex].height += insets.top + insets.bottom;
1024 // If the tabs on the run don't fill the width of the window, make it
1026 // start = starting index of the run
1027 // end = last index of the run
1028 // max = tabAreaInsets.left + width (or equivalent)
1029 // assert start <= end.
1032 * This method makes each tab in the run larger so that the tabs expand
1033 * to fill the runs width/height (depending on tabPlacement).
1035 * @param tabPlacement The placement of the tabs.
1036 * @param start The index of the first tab.
1037 * @param end The last index of the tab
1038 * @param max The amount of space in the run (width for TOP and BOTTOM
1041 protected void padTabRun(int tabPlacement, int start, int end, int max)
1043 if (tabPlacement == SwingConstants.TOP
1044 || tabPlacement == SwingConstants.BOTTOM)
1046 int runWidth = rects[end].x + rects[end].width;
1047 int spaceRemaining = max - runWidth;
1048 int numTabs = end - start + 1;
1050 // now divvy up the space.
1051 int spaceAllocated = spaceRemaining / numTabs;
1052 int currX = rects[start].x;
1053 for (int i = start; i <= end; i++)
1056 rects[i].width += spaceAllocated;
1058 currX += rects[i].width;
1059 // This is used because since the spaceAllocated
1060 // variable is an int, it rounds down. Sometimes,
1061 // we don't fill an entire row, so we make it do
1064 if (i == end && rects[i].x + rects[i].width != max)
1065 rects[i].width = max - rects[i].x;
1070 int runHeight = rects[end].y + rects[end].height;
1071 int spaceRemaining = max - runHeight;
1072 int numTabs = end - start + 1;
1074 int spaceAllocated = spaceRemaining / numTabs;
1075 int currY = rects[start].y;
1076 for (int i = start; i <= end; i++)
1079 rects[i].height += spaceAllocated;
1080 currY += rects[i].height;
1081 if (i == end && rects[i].y + rects[i].height != max)
1082 rects[i].height = max - rects[i].y;
1088 * This method returns the preferred layout size for the given container.
1090 * @param parent The container to size.
1092 * @return The preferred layout size.
1094 public Dimension preferredLayoutSize(Container parent)
1096 return calculateSize(false);
1100 * This method returns the preferred tab height given a tabPlacement and
1103 * @param tabPlacement The JTabbedPane's tab placement.
1104 * @param width The expected width.
1106 * @return The preferred tab area height.
1108 protected int preferredTabAreaHeight(int tabPlacement, int width)
1110 if (tabPane.getTabCount() == 0)
1111 return calculateTabAreaHeight(tabPlacement, 0, 0);
1117 FontMetrics fm = getFontMetrics();
1119 Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
1120 Insets insets = tabPane.getInsets();
1122 // Only interested in width, this is a messed up rectangle now.
1123 width -= tabAreaInsets.left + tabAreaInsets.right + insets.left
1126 // The reason why we can't use runCount:
1127 // This method is only called to calculate the size request
1128 // for the tabbedPane. However, this size request is dependent on
1129 // our desired width. We need to find out what the height would
1130 // be IF we got our desired width.
1131 for (int i = 0; i < tabPane.getTabCount(); i++)
1133 tabWidth = calculateTabWidth(tabPlacement, i, fm);
1134 if (runWidth + tabWidth > width)
1136 runWidth = tabWidth;
1140 runWidth += tabWidth;
1144 int maxTabHeight = calculateMaxTabHeight(tabPlacement);
1145 int tabAreaHeight = calculateTabAreaHeight(tabPlacement, runs,
1147 return tabAreaHeight;
1151 * This method calculates the preferred tab area width given a tab
1152 * placement and height.
1154 * @param tabPlacement The JTabbedPane's tab placement.
1155 * @param height The expected height.
1157 * @return The preferred tab area width.
1159 protected int preferredTabAreaWidth(int tabPlacement, int height)
1161 if (tabPane.getTabCount() == 0)
1162 return calculateTabAreaHeight(tabPlacement, 0, 0);
1168 FontMetrics fm = getFontMetrics();
1170 Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
1171 Insets insets = tabPane.getInsets();
1173 height -= tabAreaInsets.top + tabAreaInsets.bottom + insets.top
1175 int fontHeight = fm.getHeight();
1177 for (int i = 0; i < tabPane.getTabCount(); i++)
1179 tabHeight = calculateTabHeight(tabPlacement, i, fontHeight);
1180 if (runHeight + tabHeight > height)
1182 runHeight = tabHeight;
1186 runHeight += tabHeight;
1190 int maxTabWidth = calculateMaxTabWidth(tabPlacement);
1191 int tabAreaWidth = calculateTabAreaWidth(tabPlacement, runs,
1193 return tabAreaWidth;
1197 * This method rotates the places each run in the correct place the
1198 * tabRuns array. See the comment for tabRuns for how the runs are placed
1201 * @param tabPlacement The JTabbedPane's tab placement.
1202 * @param selectedRun The run the current selection is in.
1204 protected void rotateTabRuns(int tabPlacement, int selectedRun)
1206 if (runCount == 1 || selectedRun == 0 || selectedRun == -1)
1208 int[] newTabRuns = new int[tabRuns.length];
1209 int currentRun = selectedRun;
1213 newTabRuns[i] = tabRuns[currentRun];
1214 currentRun = getNextTabRun(currentRun);
1217 while (i < runCount);
1219 tabRuns = newTabRuns;
1220 BasicTabbedPaneUI.this.selectedRun = 1;
1224 * This method is called when a component is removed from the
1227 * @param comp The component removed.
1229 public void removeLayoutComponent(Component comp)
1236 * This class acts as the LayoutManager for the JTabbedPane in
1239 private class TabbedPaneScrollLayout extends TabbedPaneLayout
1242 * This method returns the preferred layout size for the given container.
1244 * @param parent The container to calculate a size for.
1246 * @return The preferred layout size.
1248 public Dimension preferredLayoutSize(Container parent)
1250 return super.calculateSize(false);
1254 * This method returns the minimum layout size for the given container.
1256 * @param parent The container to calculate a size for.
1258 * @return The minimum layout size.
1260 public Dimension minimumLayoutSize(Container parent)
1262 return super.calculateSize(true);
1266 * This method calculates the tab area height given a desired width.
1268 * @param tabPlacement The JTabbedPane's tab placement.
1269 * @param width The expected width.
1271 * @return The tab area height given the width.
1273 protected int preferredTabAreaHeight(int tabPlacement, int width)
1275 if (tabPane.getTabCount() == 0)
1276 return calculateTabAreaHeight(tabPlacement, 0, 0);
1280 int maxTabHeight = calculateMaxTabHeight(tabPlacement);
1281 int tabAreaHeight = calculateTabAreaHeight(tabPlacement, runs,
1283 return tabAreaHeight;
1287 * This method calculates the tab area width given a desired height.
1289 * @param tabPlacement The JTabbedPane's tab placement.
1290 * @param height The expected height.
1292 * @return The tab area width given the height.
1294 protected int preferredTabAreaWidth(int tabPlacement, int height)
1296 if (tabPane.getTabCount() == 0)
1297 return calculateTabAreaHeight(tabPlacement, 0, 0);
1301 int maxTabWidth = calculateMaxTabWidth(tabPlacement);
1302 int tabAreaWidth = calculateTabAreaWidth(tabPlacement, runs, maxTabWidth);
1303 return tabAreaWidth;
1307 * This method is called to calculate the tab rectangles. This method
1308 * will calculate the size and position of all rectangles (taking into
1309 * account which ones should be in which tab run). It will pad them and
1310 * normalize them as necessary.
1312 * @param tabPlacement The JTabbedPane's tab placement.
1313 * @param tabCount The number of tabs.
1315 protected void calculateTabRects(int tabPlacement, int tabCount)
1320 FontMetrics fm = getFontMetrics();
1321 SwingUtilities.calculateInnerArea(tabPane, calcRect);
1322 Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
1323 Insets insets = tabPane.getInsets();
1324 if (tabPlacement == SwingConstants.TOP
1325 || tabPlacement == SwingConstants.BOTTOM)
1327 int maxHeight = calculateMaxTabHeight(tabPlacement);
1328 calcRect.width -= tabAreaInsets.left + tabAreaInsets.right;
1330 int runWidth = tabAreaInsets.left + insets.left;
1331 int top = insets.top + tabAreaInsets.top;
1332 for (int i = 0; i < tabCount; i++)
1334 width = calculateTabWidth(tabPlacement, i, fm);
1336 // The proper instances should exists because
1337 // assureRectsCreated() was being run already.
1338 rects[i].setBounds(runWidth, top, width, maxHeight);
1342 tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right;
1343 tabAreaRect.height = maxTabHeight + tabAreaInsets.top
1344 + tabAreaInsets.bottom;
1345 contentRect.width = tabAreaRect.width;
1346 contentRect.height = tabPane.getHeight() - insets.top
1347 - insets.bottom - tabAreaRect.height;
1348 contentRect.x = insets.left;
1349 tabAreaRect.x = insets.left;
1350 if (tabPlacement == SwingConstants.BOTTOM)
1352 contentRect.y = insets.top;
1353 tabAreaRect.y = contentRect.y + contentRect.height;
1357 tabAreaRect.y = insets.top;
1358 contentRect.y = tabAreaRect.y + tabAreaRect.height;
1363 int maxWidth = calculateMaxTabWidth(tabPlacement);
1365 calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom;
1367 int runHeight = tabAreaInsets.top + insets.top;
1368 int fontHeight = fm.getHeight();
1369 int left = insets.left + tabAreaInsets.left;
1370 for (int i = 0; i < tabCount; i++)
1372 height = calculateTabHeight(tabPlacement, i, fontHeight);
1374 // The proper instances should exists because
1375 // assureRectsCreated() was being run already.
1376 rects[i].setBounds(left, runHeight, maxWidth, height);
1377 runHeight += height;
1379 tabAreaRect.width = maxTabWidth + tabAreaInsets.left
1380 + tabAreaInsets.right;
1381 tabAreaRect.height = tabPane.getHeight() - insets.top
1383 tabAreaRect.y = insets.top;
1384 contentRect.width = tabPane.getWidth() - insets.left - insets.right
1385 - tabAreaRect.width;
1386 contentRect.height = tabAreaRect.height;
1387 contentRect.y = insets.top;
1388 if (tabPlacement == SwingConstants.LEFT)
1390 tabAreaRect.x = insets.left;
1391 contentRect.x = tabAreaRect.x + tabAreaRect.width;
1395 contentRect.x = insets.left;
1396 tabAreaRect.x = contentRect.x + contentRect.width;
1400 // Unlike the behavior in the WRAP_TAB_LAYOUT the selected
1401 // tab is not padded specially.
1405 * This method is called when the JTabbedPane is laid out in
1406 * SCROLL_TAB_LAYOUT. It finds the position for all components in the
1409 * @param pane The JTabbedPane to be laid out.
1411 public void layoutContainer(Container pane)
1413 super.layoutContainer(pane);
1414 int tabCount = tabPane.getTabCount();
1417 int tabPlacement = tabPane.getTabPlacement();
1419 if (tabPlacement == SwingConstants.TOP
1420 || tabPlacement == SwingConstants.BOTTOM)
1422 if (tabAreaRect.x + tabAreaRect.width < rects[tabCount - 1].x
1423 + rects[tabCount - 1].width)
1425 Dimension incrDims = incrButton.getPreferredSize();
1426 Dimension decrDims = decrButton.getPreferredSize();
1428 if (tabPlacement == SwingConstants.BOTTOM)
1430 // Align scroll buttons with the bottom border of the tabbed
1431 // pane's content area.
1432 decrButton.setBounds(tabAreaRect.x + tabAreaRect.width
1433 - incrDims.width - decrDims.width,
1434 tabAreaRect.y, decrDims.width,
1436 incrButton.setBounds(tabAreaRect.x + tabAreaRect.width
1437 - incrDims.width, tabAreaRect.y,
1438 incrDims.width, incrDims.height);
1442 // Align scroll buttons with the top border of the tabbed
1443 // pane's content area.
1444 decrButton.setBounds(tabAreaRect.x + tabAreaRect.width
1445 - incrDims.width - decrDims.width,
1446 tabAreaRect.y + tabAreaRect.height
1447 - decrDims.height, decrDims.width,
1449 incrButton.setBounds(tabAreaRect.x + tabAreaRect.width
1451 tabAreaRect.y + tabAreaRect.height
1453 incrDims.width, incrDims.height);
1456 tabAreaRect.width -= decrDims.width + incrDims.width;
1460 incrButton.setVisible(true);
1461 decrButton.setVisible(true);
1465 incrButton.setVisible(false);
1466 decrButton.setVisible(false);
1468 currentScrollOffset = 0;
1469 currentScrollLocation = 0;
1473 if (tabPlacement == SwingConstants.LEFT
1474 || tabPlacement == SwingConstants.RIGHT)
1476 if (tabAreaRect.y + tabAreaRect.height < rects[tabCount - 1].y
1477 + rects[tabCount - 1].height)
1479 Dimension incrDims = incrButton.getPreferredSize();
1480 Dimension decrDims = decrButton.getPreferredSize();
1482 if (tabPlacement == SwingConstants.RIGHT)
1484 // Align scroll buttons with the right border of the tabbed
1485 // pane's content area.
1486 decrButton.setBounds(tabAreaRect.x,
1487 tabAreaRect.y + tabAreaRect.height
1488 - incrDims.height - decrDims.height,
1489 decrDims.width, decrDims.height);
1490 incrButton.setBounds(tabAreaRect.x,
1491 tabAreaRect.y + tabAreaRect.height
1492 - incrDims.height, incrDims.width,
1497 // Align scroll buttons with the left border of the tabbed
1498 // pane's content area.
1499 decrButton.setBounds(tabAreaRect.x + tabAreaRect.width
1501 tabAreaRect.y + tabAreaRect.height
1502 - incrDims.height - decrDims.height,
1503 decrDims.width, decrDims.height);
1504 incrButton.setBounds(tabAreaRect.x + tabAreaRect.width
1506 tabAreaRect.y + tabAreaRect.height
1507 - incrDims.height, incrDims.width,
1511 tabAreaRect.height -= decrDims.height + incrDims.height;
1513 incrButton.setVisible(true);
1514 decrButton.setVisible(true);
1518 incrButton.setVisible(false);
1519 decrButton.setVisible(false);
1521 currentScrollOffset = 0;
1522 currentScrollLocation = 0;
1525 viewport.setBounds(tabAreaRect.x, tabAreaRect.y, tabAreaRect.width,
1526 tabAreaRect.height);
1528 updateViewPosition();
1535 * This class handles ChangeEvents from the JTabbedPane.
1537 * @specnote Apparently this class was intended to be protected,
1538 * but was made public by a compiler bug and is now
1539 * public for compatibility.
1541 public class TabSelectionHandler implements ChangeListener
1544 * This method is called whenever a ChangeEvent is fired from the
1547 * @param e The ChangeEvent fired.
1549 public void stateChanged(ChangeEvent e)
1551 selectedRun = getRunForTab(tabPane.getTabCount(),
1552 tabPane.getSelectedIndex());
1554 if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT)
1555 tabPane.revalidate();
1561 * This helper class is a JPanel that fits inside the ScrollViewport. This
1562 * panel's sole job is to paint the tab rectangles inside the viewport so
1563 * that it's clipped correctly.
1565 private class ScrollingPanel extends JPanel
1568 * This is a private UI class for our panel.
1570 private class ScrollingPanelUI extends BasicPanelUI
1573 * This method overrides the default paint method. It paints the tab
1574 * rectangles for the JTabbedPane in the panel.
1576 * @param g The Graphics object to paint with.
1577 * @param c The JComponent to paint.
1579 public void paint(Graphics g, JComponent c)
1581 int placement = tabPane.getTabPlacement();
1582 g.setColor(highlight);
1583 if (placement == SwingUtilities.TOP
1584 || placement == SwingUtilities.BOTTOM)
1585 g.fillRect(currentScrollOffset, 0,
1586 tabAreaRect.width, tabAreaRect.height);
1588 g.fillRect(0, currentScrollOffset,
1589 tabAreaRect.width, tabAreaRect.height);
1591 paintTabArea(g, placement, tabPane.getSelectedIndex());
1596 * This method overrides the updateUI method. It makes the default UI for
1597 * this ScrollingPanel to be a ScrollingPanelUI.
1599 public void updateUI()
1601 setUI(new ScrollingPanelUI());
1606 * This is a helper class that paints the panel that paints tabs. This
1607 * custom JViewport is used so that the tabs painted in the panel will be
1608 * clipped. This class implements UIResource so tabs are not added when
1609 * this objects of this class are added to the JTabbedPane.
1611 private class ScrollingViewport extends JViewport implements UIResource
1613 // TODO: Maybe remove this inner class.
1617 * This is a helper class that implements UIResource so it is not added as a
1618 * tab when an object of this class is added to the JTabbedPane.
1620 private class ScrollingButton extends BasicArrowButton implements UIResource
1623 * Creates a ScrollingButton given the direction.
1625 * @param dir The direction to point in.
1627 public ScrollingButton(int dir)
1633 /** The button that increments the current scroll location.
1634 * This is package-private to avoid an accessor method. */
1635 transient ScrollingButton incrButton;
1637 /** The button that decrements the current scroll location.
1638 * This is package-private to avoid an accessor method. */
1639 transient ScrollingButton decrButton;
1641 /** The viewport used to display the tabs.
1642 * This is package-private to avoid an accessor method. */
1643 transient ScrollingViewport viewport;
1645 /** The panel inside the viewport that paints the tabs.
1646 * This is package-private to avoid an accessor method. */
1647 transient ScrollingPanel panel;
1649 /** The starting visible tab in the run in SCROLL_TAB_MODE.
1650 * This is package-private to avoid an accessor method. */
1651 transient int currentScrollLocation;
1653 transient int currentScrollOffset;
1655 /** A reusable rectangle. */
1656 protected Rectangle calcRect;
1658 /** An array of Rectangles keeping track of the tabs' area and position. */
1659 protected Rectangle[] rects;
1661 /** The insets around the content area. */
1662 protected Insets contentBorderInsets;
1664 /** The extra insets around the selected tab. */
1665 protected Insets selectedTabPadInsets;
1667 /** The insets around the tab area. */
1668 protected Insets tabAreaInsets;
1670 /** The insets around each and every tab. */
1671 protected Insets tabInsets;
1674 * The outer bottom and right edge color for both the tab and content
1677 protected Color darkShadow;
1679 /** The color of the focus outline on the selected tab. */
1680 protected Color focus;
1682 /** FIXME: find a use for this. */
1683 protected Color highlight;
1685 /** The top and left edge color for both the tab and content border. */
1686 protected Color lightHighlight;
1688 /** The inner bottom and right edge color for the tab and content border. */
1689 protected Color shadow;
1691 /** The maximum tab height. */
1692 protected int maxTabHeight;
1694 /** The maximum tab width. */
1695 protected int maxTabWidth;
1697 /** The number of runs in the JTabbedPane. */
1698 protected int runCount;
1700 /** The index of the run that the selected index is in. */
1701 protected int selectedRun;
1703 /** The amount of space each run overlaps the previous by. */
1704 protected int tabRunOverlay;
1706 /** The gap between text and label */
1707 protected int textIconGap;
1709 /** This array keeps track of which tabs are in which run.
1710 * <p>The value at index i denotes the index of the first tab in run i.</p>
1711 * <p>If the value for any index (i > 0) is 0 then (i - 1) is the last
1714 protected int[] tabRuns;
1717 * Indicates if the layout of the tab runs is ok or not. This is package
1718 * private to avoid a synthetic accessor method.
1720 boolean tabRunsDirty;
1723 * This is the keystroke for moving down.
1727 protected KeyStroke downKey;
1730 * This is the keystroke for moving left.
1734 protected KeyStroke leftKey;
1737 * This is the keystroke for moving right.
1741 protected KeyStroke rightKey;
1744 * This is the keystroke for moving up.
1748 protected KeyStroke upKey;
1750 /** The listener that listens for focus events. */
1751 protected FocusListener focusListener;
1753 /** The listener that listens for mouse events. */
1754 protected MouseListener mouseListener;
1756 /** The listener that listens for property change events. */
1757 protected PropertyChangeListener propertyChangeListener;
1759 /** The listener that listens for change events. */
1760 protected ChangeListener tabChangeListener;
1762 /** The tab pane that this UI paints. */
1763 protected JTabbedPane tabPane;
1765 /** The current layout manager for the tabPane.
1766 * This is package-private to avoid an accessor method. */
1767 transient LayoutManager layoutManager;
1769 /** The rectangle that describes the tab area's position and size.
1770 * This is package-private to avoid an accessor method. */
1771 transient Rectangle tabAreaRect;
1773 /** The rectangle that describes the content area's position and
1774 * size. This is package-private to avoid an accessor method. */
1775 transient Rectangle contentRect;
1778 * The index over which the mouse is currently moving.
1780 private int rolloverTab;
1783 * Determines if tabs are painted opaque or not. This can be adjusted using
1784 * the UIManager property 'TabbedPane.tabsOpaque'.
1786 private boolean tabsOpaque;
1789 * The currently visible component.
1791 private Component visibleComponent;
1793 private Color selectedColor;
1795 private Rectangle tempTextRect = new Rectangle();
1797 private Rectangle tempIconRect = new Rectangle();
1800 * Creates a new BasicTabbedPaneUI object.
1802 public BasicTabbedPaneUI()
1805 rects = new Rectangle[0];
1806 tabRuns = new int[10];
1810 * This method creates a ScrollingButton that points in the appropriate
1811 * direction for an increasing button.
1812 * This is package-private to avoid an accessor method.
1814 * @return The increase ScrollingButton.
1816 ScrollingButton createIncreaseButton()
1818 if (incrButton == null)
1819 incrButton = new ScrollingButton(SwingConstants.NORTH);
1820 if (tabPane.getTabPlacement() == SwingConstants.TOP
1821 || tabPane.getTabPlacement() == SwingConstants.BOTTOM)
1822 incrButton.setDirection(SwingConstants.EAST);
1824 incrButton.setDirection(SwingConstants.SOUTH);
1829 * This method creates a ScrollingButton that points in the appropriate
1830 * direction for a decreasing button.
1831 * This is package-private to avoid an accessor method.
1833 * @return The decrease ScrollingButton.
1835 ScrollingButton createDecreaseButton()
1837 if (decrButton == null)
1838 decrButton = new ScrollingButton(SwingConstants.SOUTH);
1839 if (tabPane.getTabPlacement() == SwingConstants.TOP
1840 || tabPane.getTabPlacement() == SwingConstants.BOTTOM)
1841 decrButton.setDirection(SwingConstants.WEST);
1843 decrButton.setDirection(SwingConstants.NORTH);
1848 * This method finds the point to set the view position at given the index
1849 * of a tab. The tab will be the first visible tab in the run.
1850 * This is package-private to avoid an accessor method.
1852 * @param index The index of the first visible tab.
1854 * @return The position of the first visible tab.
1856 Point findPointForIndex(int index)
1858 int tabPlacement = tabPane.getTabPlacement();
1859 int selectedIndex = tabPane.getSelectedIndex();
1860 Insets insets = getSelectedTabPadInsets(tabPlacement);
1864 if (tabPlacement == TOP || tabPlacement == BOTTOM)
1868 w += rects[index - 1].x + rects[index - 1].width;
1869 if (index > selectedIndex)
1870 w -= insets.left + insets.right;
1878 h += rects[index - 1].y + rects[index - 1].height;
1879 if (index > selectedIndex)
1880 h -= insets.top + insets.bottom;
1884 Point p = new Point(w, h);
1888 /** TabbedPanes in scrolling mode should use this method to
1889 * scroll properly to the tab given by the index argument.
1891 * @param index The tab to scroll to.
1892 * @param placement The tab's placement.
1894 final void scrollTab(int index, int placement)
1897 if (index >= 0 && tabPane.isEnabledAt(index))
1899 // If the user clicked on the last tab and that one was
1900 // only partially visible shift the scroll offset to make
1901 // it completely visible.
1904 case JTabbedPane.TOP:
1905 case JTabbedPane.BOTTOM:
1906 if ((diff = rects[index].x
1907 + rects[index].width
1908 - decrButton.getX() - currentScrollOffset) > 0)
1909 currentScrollOffset += diff;
1910 else if ((diff = rects[index].x - currentScrollOffset) < 0)
1913 currentScrollOffset = 0;
1915 currentScrollOffset += diff;
1918 currentScrollLocation = tabForCoordinate(tabPane,
1919 currentScrollOffset,
1923 if ((diff = rects[index].y + rects[index].height
1924 - decrButton.getY() - currentScrollOffset) > 0)
1925 currentScrollOffset += diff;
1926 else if ((diff = rects[index].y - currentScrollOffset) < 0)
1929 currentScrollOffset = 0;
1931 currentScrollOffset += diff;
1934 currentScrollLocation = tabForCoordinate(tabPane,
1936 currentScrollOffset);
1939 updateViewPosition();
1944 /** Sets the enabled state of the increase and decrease button
1945 * according to the current scrolling offset and tab pane width
1946 * (or height in TOP/BOTTOM placement).
1948 final void updateButtons()
1950 int tc = tabPane.getTabCount();
1952 // The increase button should be enabled as long as the
1953 // right/bottom border of the last tab is under the left/top
1954 // border of the decrease button.
1955 switch (tabPane.getTabPlacement())
1957 case JTabbedPane.BOTTOM:
1958 case JTabbedPane.TOP:
1959 incrButton.setEnabled(currentScrollLocation + 1 < tc
1960 && rects[tc-1].x + rects[tc-1].width
1961 - currentScrollOffset > decrButton.getX());
1964 incrButton.setEnabled(currentScrollLocation + 1 < tc
1965 && rects[tc-1].y + rects[tc-1].height
1966 - currentScrollOffset > decrButton.getY());
1969 // The decrease button is enabled when the tab pane is scrolled in any way.
1970 decrButton.setEnabled(currentScrollOffset > 0);
1975 * Updates the position of the scrolling viewport's view
1976 * according to the current scroll offset.
1978 final void updateViewPosition()
1980 Point p = viewport.getViewPosition();
1982 // The unneeded coordinate must be set to zero
1983 // in order to correctly handle placement changes.
1984 switch (tabPane.getTabPlacement())
1986 case JTabbedPane.LEFT:
1987 case JTabbedPane.RIGHT:
1989 p.y = currentScrollOffset;
1992 p.x = currentScrollOffset;
1996 viewport.setViewPosition(p);
2000 * This method creates a new BasicTabbedPaneUI.
2002 * @param c The JComponent to create a UI for.
2004 * @return A new BasicTabbedPaneUI.
2006 public static ComponentUI createUI(JComponent c)
2008 return new BasicTabbedPaneUI();
2012 * This method installs the UI for the given JComponent.
2014 * @param c The JComponent to install the UI for.
2016 public void installUI(JComponent c)
2019 if (c instanceof JTabbedPane)
2021 tabPane = (JTabbedPane) c;
2023 installComponents();
2026 installKeyboardActions();
2028 layoutManager = createLayoutManager();
2029 tabPane.setLayout(layoutManager);
2034 * This method uninstalls the UI for the given JComponent.
2036 * @param c The JComponent to uninstall the UI for.
2038 public void uninstallUI(JComponent c)
2040 layoutManager = null;
2042 uninstallKeyboardActions();
2043 uninstallListeners();
2044 uninstallDefaults();
2045 uninstallComponents();
2051 * This method creates the appropriate layout manager for the JTabbedPane's
2052 * current tab layout policy. If the tab layout policy is
2053 * SCROLL_TAB_LAYOUT, then all the associated components that need to be
2054 * created will be done so now.
2056 * @return A layout manager given the tab layout policy.
2058 protected LayoutManager createLayoutManager()
2060 if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT)
2061 return new TabbedPaneLayout();
2067 incrButton = createIncreaseButton();
2068 incrButton.addMouseListener(mouseListener);
2070 decrButton = createDecreaseButton();
2071 decrButton.addMouseListener(mouseListener);
2072 decrButton.setEnabled(false);
2074 panel = new ScrollingPanel();
2075 panel.setSize(Integer.MAX_VALUE, Integer.MAX_VALUE);
2076 panel.addMouseListener(mouseListener);
2077 panel.addFocusListener(focusListener);
2079 viewport = new ScrollingViewport();
2080 viewport.setBackground(Color.LIGHT_GRAY);
2081 viewport.setView(panel);
2082 viewport.setLayout(null);
2084 tabPane.add(incrButton);
2085 tabPane.add(decrButton);
2086 tabPane.add(viewport);
2088 return new TabbedPaneScrollLayout();
2093 * This method installs components for this JTabbedPane.
2095 protected void installComponents()
2097 // Nothing to be done.
2101 * This method uninstalls components for this JTabbedPane.
2103 protected void uninstallComponents()
2105 if (incrButton != null)
2106 tabPane.remove(incrButton);
2108 if (decrButton != null)
2109 tabPane.remove(decrButton);
2111 if (viewport != null)
2112 tabPane.remove(viewport);
2116 * This method installs defaults for the Look and Feel.
2118 protected void installDefaults()
2120 LookAndFeel.installColorsAndFont(tabPane, "TabbedPane.background",
2121 "TabbedPane.foreground",
2123 tabPane.setOpaque(false);
2125 lightHighlight = UIManager.getColor("TabbedPane.highlight");
2126 highlight = UIManager.getColor("TabbedPane.light");
2128 shadow = UIManager.getColor("TabbedPane.shadow");
2129 darkShadow = UIManager.getColor("TabbedPane.darkShadow");
2131 focus = UIManager.getColor("TabbedPane.focus");
2133 textIconGap = UIManager.getInt("TabbedPane.textIconGap");
2134 tabRunOverlay = UIManager.getInt("TabbedPane.tabRunOverlay");
2136 tabInsets = UIManager.getInsets("TabbedPane.tabInsets");
2137 selectedTabPadInsets
2138 = UIManager.getInsets("TabbedPane.selectedTabPadInsets");
2139 tabAreaInsets = UIManager.getInsets("TabbedPane.tabAreaInsets");
2141 = UIManager.getInsets("TabbedPane.contentBorderInsets");
2142 tabsOpaque = UIManager.getBoolean("TabbedPane.tabsOpaque");
2144 // Although 'TabbedPane.contentAreaColor' is not defined in the defaults
2145 // of BasicLookAndFeel it is used by this class.
2146 selectedColor = UIManager.getColor("TabbedPane.contentAreaColor");
2147 if (selectedColor == null)
2148 selectedColor = UIManager.getColor("control");
2150 calcRect = new Rectangle();
2151 tabRuns = new int[10];
2152 tabAreaRect = new Rectangle();
2153 contentRect = new Rectangle();
2157 * This method uninstalls defaults for the Look and Feel.
2159 protected void uninstallDefaults()
2166 tempIconRect = null;
2167 tempTextRect = null;
2169 contentBorderInsets = null;
2170 tabAreaInsets = null;
2171 selectedTabPadInsets = null;
2177 lightHighlight = null;
2180 selectedColor = null;
2184 * This method creates and installs the listeners for this UI.
2186 protected void installListeners()
2188 mouseListener = createMouseListener();
2189 tabChangeListener = createChangeListener();
2190 propertyChangeListener = createPropertyChangeListener();
2191 focusListener = createFocusListener();
2193 tabPane.addMouseListener(mouseListener);
2194 tabPane.addChangeListener(tabChangeListener);
2195 tabPane.addPropertyChangeListener(propertyChangeListener);
2196 tabPane.addFocusListener(focusListener);
2200 * This method removes and nulls the listeners for this UI.
2202 protected void uninstallListeners()
2204 tabPane.removeFocusListener(focusListener);
2205 tabPane.removePropertyChangeListener(propertyChangeListener);
2206 tabPane.removeChangeListener(tabChangeListener);
2207 tabPane.removeMouseListener(mouseListener);
2209 if (incrButton != null)
2210 incrButton.removeMouseListener(mouseListener);
2212 if (decrButton != null)
2213 decrButton.removeMouseListener(mouseListener);
2217 panel.removeMouseListener(mouseListener);
2218 panel.removeFocusListener(focusListener);
2221 focusListener = null;
2222 propertyChangeListener = null;
2223 tabChangeListener = null;
2224 mouseListener = null;
2228 * This method creates a new MouseListener.
2230 * @return A new MouseListener.
2232 protected MouseListener createMouseListener()
2234 return new MouseHandler();
2238 * This method creates a new FocusListener.
2240 * @return A new FocusListener.
2242 protected FocusListener createFocusListener()
2244 return new FocusHandler();
2248 * This method creates a new ChangeListener.
2250 * @return A new ChangeListener.
2252 protected ChangeListener createChangeListener()
2254 return new TabSelectionHandler();
2258 * This method creates a new PropertyChangeListener.
2260 * @return A new PropertyChangeListener.
2262 protected PropertyChangeListener createPropertyChangeListener()
2264 return new PropertyChangeHandler();
2268 * This method installs keyboard actions for the JTabbedPane.
2270 protected void installKeyboardActions()
2272 InputMap keyMap = (InputMap) UIManager.get("TabbedPane.focusInputMap");
2273 SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, keyMap);
2275 keyMap = (InputMap) UIManager.get("TabbedPane.ancestorInputMap");
2277 .replaceUIInputMap(tabPane,
2278 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
2281 ActionMap map = getActionMap();
2282 SwingUtilities.replaceUIActionMap(tabPane, map);
2286 * This method uninstalls keyboard actions for the JTabbedPane.
2288 protected void uninstallKeyboardActions()
2290 SwingUtilities.replaceUIActionMap(tabPane, null);
2291 SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, null);
2293 .replaceUIInputMap(tabPane,
2294 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
2299 * This method returns the minimum size of the JTabbedPane.
2301 * @param c The JComponent to find a size for.
2303 * @return The minimum size.
2305 public Dimension getMinimumSize(JComponent c)
2307 return layoutManager.minimumLayoutSize(tabPane);
2311 * This method returns the maximum size of the JTabbedPane.
2313 * @param c The JComponent to find a size for.
2315 * @return The maximum size.
2317 public Dimension getMaximumSize(JComponent c)
2319 return new Dimension(Short.MAX_VALUE, Short.MAX_VALUE);
2323 * This method paints the JTabbedPane.
2325 * @param g The Graphics object to paint with.
2326 * @param c The JComponent to paint.
2328 public void paint(Graphics g, JComponent c)
2330 if (!tabPane.isValid())
2333 if (tabPane.getTabCount() == 0)
2336 int index = tabPane.getSelectedIndex();
2340 int tabPlacement = tabPane.getTabPlacement();
2342 // Paint the tab area only in WRAP_TAB_LAYOUT Mode from this method
2343 // because it is done through the ScrollingViewport.paint() method
2344 // for the SCROLL_TAB_LAYOUT mode.
2345 if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT)
2347 g.setColor(highlight);
2348 g.fillRect(tabAreaRect.x, tabAreaRect.y,
2349 tabAreaRect.width, tabAreaRect.height);
2350 paintTabArea(g, tabPlacement, index);
2353 paintContentBorder(g, tabPlacement, index);
2357 * This method paints the tab area. This includes painting the rectangles
2358 * that make up the tabs.
2360 * @param g The Graphics object to paint with.
2361 * @param tabPlacement The JTabbedPane's tab placement.
2362 * @param selectedIndex The selected index.
2364 protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex)
2366 // Please note: the ordering of the painting is important.
2367 // we WANT to paint the outermost run first and then work our way in.
2369 // The following drawing code works for both tab layouts.
2370 int tabCount = tabPane.getTabCount();
2372 for (int i = runCount - 1; i >= 0; --i)
2374 int start = tabRuns[i];
2376 if (i == runCount - 1)
2379 next = tabRuns[i + 1];
2380 int end = next != 0 ? next - 1 : tabCount - 1;
2381 for (int j = start; j <= end; ++j)
2383 if (j != selectedIndex)
2385 paintTab(g, tabPlacement, rects, j,
2386 tempIconRect, tempTextRect);
2391 // Paint selected tab in front of every other tab.
2392 if (selectedIndex >= 0)
2393 paintTab(g, tabPlacement, rects, selectedIndex,
2394 tempIconRect, tempTextRect);
2398 * This method paints an individual tab.
2400 * @param g The Graphics object to paint with.
2401 * @param tabPlacement The JTabbedPane's tab placement.
2402 * @param rects The array of rectangles that keep the size and position of
2404 * @param tabIndex The tab index to paint.
2405 * @param iconRect The rectangle to use for the icon.
2406 * @param textRect The rectangle to use for the text.
2408 protected void paintTab(Graphics g, int tabPlacement, Rectangle[] rects,
2409 int tabIndex, Rectangle iconRect, Rectangle textRect)
2411 Rectangle rect = rects[tabIndex];
2412 boolean isSelected = tabIndex == tabPane.getSelectedIndex();
2413 // Paint background if necessary.
2414 if (tabsOpaque || tabPane.isOpaque())
2416 paintTabBackground(g, tabPlacement, tabIndex, rect.x, rect.y,
2417 rect.width, rect.height, isSelected);
2421 paintTabBorder(g, tabPlacement, tabIndex, rect.x, rect.y, rect.width,
2422 rect.height, isSelected);
2425 FontMetrics fm = getFontMetrics();
2426 Icon icon = getIconForTab(tabIndex);
2427 String title = tabPane.getTitleAt(tabIndex);
2428 layoutLabel(tabPlacement, fm, tabIndex, title, icon, rect, iconRect,
2429 textRect, isSelected);
2431 paintText(g, tabPlacement, tabPane.getFont(), fm, tabIndex, title,
2432 textRect, isSelected);
2434 // Paint icon if necessary.
2435 paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected);
2437 // Paint focus indicator.
2438 paintFocusIndicator(g, tabPlacement, rects, tabIndex, iconRect, textRect,
2443 * This method lays out the tab and finds the location to paint the icon
2446 * @param tabPlacement The JTabbedPane's tab placement.
2447 * @param metrics The font metrics for the font to paint with.
2448 * @param tabIndex The tab index to paint.
2449 * @param title The string painted.
2450 * @param icon The icon painted.
2451 * @param tabRect The tab bounds.
2452 * @param iconRect The calculated icon bounds.
2453 * @param textRect The calculated text bounds.
2454 * @param isSelected Whether this tab is selected.
2456 protected void layoutLabel(int tabPlacement, FontMetrics metrics,
2457 int tabIndex, String title, Icon icon,
2458 Rectangle tabRect, Rectangle iconRect,
2459 Rectangle textRect, boolean isSelected)
2461 // Reset the icon and text rectangles, as the result is not specified
2462 // when the locations are not (0,0).
2466 textRect.height = 0;
2470 iconRect.height = 0;
2471 SwingUtilities.layoutCompoundLabel(tabPane, metrics, title, icon,
2472 SwingConstants.CENTER,
2473 SwingConstants.CENTER,
2474 SwingConstants.CENTER,
2475 SwingConstants.RIGHT, tabRect,
2476 iconRect, textRect, textIconGap);
2478 int shiftX = getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
2479 int shiftY = getTabLabelShiftY(tabPlacement, tabIndex, isSelected);
2481 iconRect.x += shiftX;
2482 iconRect.y += shiftY;
2484 textRect.x += shiftX;
2485 textRect.y += shiftY;
2489 * This method paints the icon.
2491 * @param g The Graphics object to paint.
2492 * @param tabPlacement The JTabbedPane's tab placement.
2493 * @param tabIndex The tab index to paint.
2494 * @param icon The icon to paint.
2495 * @param iconRect The bounds of the icon.
2496 * @param isSelected Whether this tab is selected.
2498 protected void paintIcon(Graphics g, int tabPlacement, int tabIndex,
2499 Icon icon, Rectangle iconRect, boolean isSelected)
2502 icon.paintIcon(tabPane, g, iconRect.x, iconRect.y);
2506 * This method paints the text for the given tab.
2508 * @param g The Graphics object to paint with.
2509 * @param tabPlacement The JTabbedPane's tab placement.
2510 * @param font The font to paint with.
2511 * @param metrics The fontmetrics of the given font.
2512 * @param tabIndex The tab index.
2513 * @param title The string to paint.
2514 * @param textRect The bounds of the string.
2515 * @param isSelected Whether this tab is selected.
2517 protected void paintText(Graphics g, int tabPlacement, Font font,
2518 FontMetrics metrics, int tabIndex, String title,
2519 Rectangle textRect, boolean isSelected)
2522 View textView = getTextViewForTab(tabIndex);
2523 if (textView != null)
2525 textView.paint(g, textRect);
2529 int ascent = metrics.getAscent();
2531 int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
2532 if (tabPane.isEnabled() && tabPane.isEnabledAt(tabIndex))
2534 Color fg = tabPane.getForegroundAt(tabIndex);
2535 if (isSelected && (fg instanceof UIResource))
2537 Color selectionForeground =
2538 UIManager.getColor("TabbedPane.selectionForeground");
2539 if (selectionForeground != null)
2540 fg = selectionForeground;
2544 if (mnemIndex != -1)
2545 BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
2547 textRect.y + ascent);
2549 g.drawString(title, textRect.x, textRect.y + ascent);
2553 Color bg = tabPane.getBackgroundAt(tabIndex);
2554 g.setColor(bg.brighter());
2555 if (mnemIndex != -1)
2556 BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
2557 textRect.x, textRect.y
2560 g.drawString(title, textRect.x, textRect.y + ascent);
2562 g.setColor(bg.darker());
2563 if (mnemIndex != -1)
2564 BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
2569 g.drawString(title, textRect.x + 1, textRect.y + 1 + ascent);
2574 * This method returns how much the label for the tab should shift in the X
2577 * @param tabPlacement The JTabbedPane's tab placement.
2578 * @param tabIndex The tab index being painted.
2579 * @param isSelected Whether this tab is selected.
2581 * @return The amount the label should shift by in the X direction.
2583 protected int getTabLabelShiftX(int tabPlacement, int tabIndex,
2586 switch (tabPlacement)
2589 case SwingUtilities.TOP:
2590 case SwingUtilities.BOTTOM:
2592 case SwingUtilities.LEFT:
2593 return (isSelected) ? -1 : 1;
2594 case SwingUtilities.RIGHT:
2595 return (isSelected) ? 1 : -1;
2600 * This method returns how much the label for the tab should shift in the Y
2603 * @param tabPlacement The JTabbedPane's tab placement.
2604 * @param tabIndex The tab index being painted.
2605 * @param isSelected Whether this tab is selected.
2607 * @return The amount the label should shift by in the Y direction.
2609 protected int getTabLabelShiftY(int tabPlacement, int tabIndex,
2612 switch (tabPlacement)
2615 case SwingUtilities.TOP:
2616 return (isSelected) ? -1 : 1;
2617 case SwingUtilities.BOTTOM:
2618 return (isSelected) ? 1 : -1;
2619 case SwingUtilities.LEFT:
2620 case SwingUtilities.RIGHT:
2626 * This method paints the focus rectangle around the selected tab.
2628 * @param g The Graphics object to paint with.
2629 * @param tabPlacement The JTabbedPane's tab placement.
2630 * @param rects The array of rectangles keeping track of size and position.
2631 * @param tabIndex The tab index.
2632 * @param iconRect The icon bounds.
2633 * @param textRect The text bounds.
2634 * @param isSelected Whether this tab is selected.
2636 protected void paintFocusIndicator(Graphics g, int tabPlacement,
2637 Rectangle[] rects, int tabIndex,
2638 Rectangle iconRect, Rectangle textRect,
2641 if (tabPane.hasFocus() && isSelected)
2643 Rectangle rect = rects[tabIndex];
2644 // The focus rectangle.
2651 switch (tabPlacement)
2657 h = rect.height - 6;
2663 h = rect.height - 5;
2669 h = rect.height - 5;
2676 h = rect.height - 5;
2679 BasicGraphicsUtils.drawDashedRect(g, x, y, w, h);
2684 * This method paints the border for an individual tab.
2686 * @param g The Graphics object to paint with.
2687 * @param tabPlacement The JTabbedPane's tab placement.
2688 * @param tabIndex The tab index.
2689 * @param x The x position of the tab.
2690 * @param y The y position of the tab.
2691 * @param w The width of the tab.
2692 * @param h The height of the tab.
2693 * @param isSelected Whether the tab is selected.
2695 protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex,
2696 int x, int y, int w, int h, boolean isSelected)
2698 Color saved = g.getColor();
2700 switch (tabPlacement)
2702 case SwingConstants.TOP:
2704 // Inner right line.
2705 g.drawLine(x + w - 2, y + 2, x + w - 2, y + h);
2707 g.setColor(darkShadow);
2708 // Outer right line.
2709 g.drawLine(x + w - 1, y + 2, x + w - 1, y + h);
2711 // Upper right corner.
2712 g.drawLine(x + w - 2, y + 1, x + w - 1, y + 2);
2714 g.setColor(lightHighlight);
2717 g.drawLine(x, y + 3, x, y + h);
2720 g.drawLine(x + 3, y, x + w - 3, y);
2722 // Upper left corner.
2723 g.drawLine(x, y + 2, x + 2, y);
2726 case SwingConstants.LEFT:
2727 g.setColor(lightHighlight);
2729 g.drawLine(x + 3, y, x + w - 1, y);
2732 g.drawLine(x + 2, y, x, y + 2);
2735 g.drawLine(x, y + 3, x, y + h - 4);
2737 // Bottom left corner.
2738 g.drawLine(x, y + h - 3, x + 1, y + h - 2);
2740 g.setColor(darkShadow);
2741 // Outer bottom line.
2742 g.drawLine(x + 2, y + h - 1, x + w - 1, y + h - 1);
2745 // Inner bottom line.
2746 g.drawLine(x + 2, y + h - 2, x + w - 1, y + h - 2);
2749 case SwingConstants.BOTTOM:
2751 // Inner right line.
2752 g.drawLine(x + w - 2, y, x + w - 2, y + h - 2);
2754 // Inner bottom line.
2755 g.drawLine(x + 2, y + h - 1, x + w - 3, y + h - 1);
2757 g.setColor(darkShadow);
2758 // Outer right line.
2759 g.drawLine(x + w - 1, y, x + w - 1, y + h - 3);
2761 // Bottom right corner.
2762 g.drawLine(x + w - 1, y + h - 2, x + w - 3, y + h);
2765 g.drawLine(x + 2, y + h, x + w - 4, y + h);
2767 g.setColor(lightHighlight);
2769 g.drawLine(x, y, x, y + h - 3);
2771 // Bottom left corner.
2772 g.drawLine(x, y + h - 2, x + 1, y + h - 1);
2774 case SwingConstants.RIGHT:
2775 g.setColor(lightHighlight);
2777 g.drawLine(x, y, x + w - 3, y);
2779 g.setColor(darkShadow);
2780 // Top right corner.
2781 g.drawLine(x + w - 2, y + 1, x + w - 1, y + 2);
2783 // Outer right line.
2784 g.drawLine(x + w - 1, y + 3, x + w - 1, y + h - 3);
2786 // Bottom right corner.
2787 g.drawLine(x + w - 2, y + h - 2, x + w - 3, y + h - 1);
2790 g.drawLine(x, y + h - 1, x + w - 4, y + h - 1);
2794 // Inner right line.
2795 g.drawLine(x + w - 2, y + 2, x + w - 2, y + h - 3);
2797 // Inner bottom line.
2798 g.drawLine(x, y + h - 2, x + w - 3, y + h - 2);
2807 * This method paints the background for an individual tab.
2809 * @param g The Graphics object to paint with.
2810 * @param tabPlacement The JTabbedPane's tab placement.
2811 * @param tabIndex The tab index.
2812 * @param x The x position of the tab.
2813 * @param y The y position of the tab.
2814 * @param w The width of the tab.
2815 * @param h The height of the tab.
2816 * @param isSelected Whether the tab is selected.
2818 protected void paintTabBackground(Graphics g, int tabPlacement,
2819 int tabIndex, int x, int y, int w, int h,
2822 Color saved = g.getColor();
2825 g.setColor(selectedColor);
2828 Color bg = tabPane.getBackgroundAt(tabIndex);
2830 bg = Color.LIGHT_GRAY;
2834 switch (tabPlacement)
2836 case SwingConstants.TOP:
2837 g.fillRect(x + 1, y + 1, w - 1, h - 1);
2839 case SwingConstants.BOTTOM:
2840 g.fillRect(x, y, w - 1, h - 1);
2842 case SwingConstants.LEFT:
2843 g.fillRect(x + 1, y + 1, w - 1, h - 2);
2845 case SwingConstants.RIGHT:
2846 g.fillRect(x, y + 1, w - 1, h - 2);
2854 * This method paints the border around the content area.
2856 * @param g The Graphics object to paint with.
2857 * @param tabPlacement The JTabbedPane's tab placement.
2858 * @param selectedIndex The index of the selected tab.
2860 protected void paintContentBorder(Graphics g, int tabPlacement,
2863 int width = tabPane.getWidth();
2864 int height = tabPane.getHeight();
2865 Insets insets = tabPane.getInsets();
2867 // Calculate coordinates of content area.
2868 int x = insets.left;
2870 int w = width - insets.left - insets.right;
2871 int h = height - insets.top - insets.bottom;
2873 switch (tabPlacement)
2876 x += calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
2877 w -= x - insets.left;
2880 w -= calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
2883 h -= calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
2887 y += calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
2888 h -= y - insets.top;
2891 // Fill background if necessary.
2892 if (tabPane.isOpaque())
2894 Color bg = UIManager.getColor("TabbedPane.contentAreaColor");
2896 g.fillRect(x, y, w, h);
2900 paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2901 paintContentBorderLeftEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2902 paintContentBorderBottomEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2903 paintContentBorderRightEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2907 * This method paints the top edge of the content border.
2909 * @param g The Graphics object to paint with.
2910 * @param tabPlacement The JTabbedPane's tab placement.
2911 * @param selectedIndex The selected tab index.
2912 * @param x The x coordinate for the content area.
2913 * @param y The y coordinate for the content area.
2914 * @param w The width of the content area.
2915 * @param h The height of the content area.
2917 protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
2918 int selectedIndex, int x, int y,
2921 Color saved = g.getColor();
2922 g.setColor(lightHighlight);
2924 int startgap = rects[selectedIndex].x - currentScrollOffset;
2925 int endgap = rects[selectedIndex].x + rects[selectedIndex].width
2926 - currentScrollOffset;
2928 // Paint the highlight line with a gap if the tabs are at the top
2929 // and the selected tab is inside the visible area.
2930 if (tabPlacement == SwingConstants.TOP && startgap >= 0)
2932 g.drawLine(x, y, startgap, y);
2933 g.drawLine(endgap, y, x + w - 1, y);
2935 g.setColor(selectedColor);
2936 g.drawLine(startgap, y, endgap - 1, y);
2939 g.drawLine(x, y, x + w, y);
2941 g.setColor(selectedColor);
2942 g.drawLine(x, y + 1, x + w - 1, y + 1);
2943 g.drawLine(x, y + 2, x + w - 1, y + 2);
2949 * This method paints the left edge of the content border.
2951 * @param g The Graphics object to paint with.
2952 * @param tabPlacement The JTabbedPane's tab placement.
2953 * @param selectedIndex The selected tab index.
2954 * @param x The x coordinate for the content area.
2955 * @param y The y coordinate for the content area.
2956 * @param w The width of the content area.
2957 * @param h The height of the content area.
2959 protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
2960 int selectedIndex, int x, int y,
2963 Color saved = g.getColor();
2964 g.setColor(lightHighlight);
2966 int startgap = rects[selectedIndex].y - currentScrollOffset;
2967 int endgap = rects[selectedIndex].y + rects[selectedIndex].height
2968 - currentScrollOffset;
2970 if (tabPlacement == SwingConstants.LEFT && startgap >= 0)
2972 g.drawLine(x, y, x, startgap);
2973 g.drawLine(x, endgap, x, y + h - 1);
2975 g.setColor(selectedColor);
2976 g.drawLine(x, startgap, x, endgap - 1);
2979 g.drawLine(x, y, x, y + h - 1);
2981 g.setColor(selectedColor);
2982 g.drawLine(x + 1, y + 1, x + 1, y + h - 4);
2988 * This method paints the bottom edge of the content border.
2990 * @param g The Graphics object to paint with.
2991 * @param tabPlacement The JTabbedPane's tab placement.
2992 * @param selectedIndex The selected tab index.
2993 * @param x The x coordinate for the content area.
2994 * @param y The y coordinate for the content area.
2995 * @param w The width of the content area.
2996 * @param h The height of the content area.
2998 protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
2999 int selectedIndex, int x, int y,
3002 Color saved = g.getColor();
3004 int startgap = rects[selectedIndex].x - currentScrollOffset;
3005 int endgap = rects[selectedIndex].x + rects[selectedIndex].width
3006 - currentScrollOffset;
3008 if (tabPlacement == SwingConstants.BOTTOM && startgap >= 0)
3011 g.drawLine(x + 1, y + h - 2, startgap, y + h - 2);
3012 g.drawLine(endgap, y + h - 2, x + w - 2, y + h - 2);
3014 g.setColor(darkShadow);
3015 g.drawLine(x, y + h - 1, startgap , y + h - 1);
3016 g.drawLine(endgap, y + h - 1, x + w - 1, y + h - 1);
3018 g.setColor(selectedColor);
3019 g.drawLine(startgap, y + h - 1, endgap - 1, y + h - 1);
3020 g.drawLine(startgap, y + h - 2, endgap - 1, y + h - 2);
3025 g.drawLine(x + 1, y + h - 2, x + w - 1, y + h - 2);
3026 g.setColor(darkShadow);
3027 g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
3030 g.setColor(selectedColor);
3031 g.drawLine(x + 1, y + h - 3, x + w - 2, y + h - 3);
3037 * This method paints the right edge of the content border.
3039 * @param g The Graphics object to paint with.
3040 * @param tabPlacement The JTabbedPane's tab placement.
3041 * @param selectedIndex The selected tab index.
3042 * @param x The x coordinate for the content area.
3043 * @param y The y coordinate for the content area.
3044 * @param w The width of the content area.
3045 * @param h The height of the content area.
3047 protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
3048 int selectedIndex, int x, int y,
3051 Color saved = g.getColor();
3052 int startgap = rects[selectedIndex].y - currentScrollOffset;
3053 int endgap = rects[selectedIndex].y + rects[selectedIndex].height
3054 - currentScrollOffset;
3056 if (tabPlacement == SwingConstants.RIGHT && startgap >= 0)
3059 g.drawLine(x + w - 2, y + 1, x + w - 2, startgap);
3060 g.drawLine(x + w - 2, endgap, x + w - 2, y + h - 2);
3062 g.setColor(darkShadow);
3063 g.drawLine(x + w - 1, y, x + w - 1, startgap);
3064 g.drawLine(x + w - 1, endgap, x + w - 1, y + h - 2);
3066 g.setColor(selectedColor);
3067 g.drawLine(x + w - 2, startgap, x + w - 2, endgap - 1);
3068 g.drawLine(x + w - 1, startgap, x + w - 1, endgap - 1);
3073 g.drawLine(x + w - 2, y + 1, x + w - 2, y + h - 2);
3074 g.setColor(darkShadow);
3075 g.drawLine(x + w - 1, y, x + w - 1, y + h - 2);
3078 g.setColor(selectedColor);
3079 g.drawLine(x + w - 3, y + 1, x + w - 3, y + h - 4);
3085 * <p>This method returns the bounds of a tab for the given index
3086 * and shifts it by the current scrolling offset if the tabbed
3087 * pane is in scrolling tab layout mode.</p>
3089 * <p>Subclassses should retrievs a tab's bounds by this method
3090 * if they want to find out whether the tab is currently visible.</p>
3092 * @param pane The JTabbedPane.
3093 * @param i The index to look for.
3095 * @return The bounds of the tab with the given index.
3097 public Rectangle getTabBounds(JTabbedPane pane, int i)
3099 // Need to re-layout container if tab does not exist.
3100 if (i >= rects.length)
3101 layoutManager.layoutContainer(pane);
3103 // Properly shift coordinates if scrolling has taken
3105 if (pane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
3107 Rectangle r = new Rectangle(rects[i]);
3109 switch(pane.getTabPlacement())
3111 case SwingConstants.TOP:
3112 case SwingConstants.BOTTOM:
3113 r.x -= currentScrollOffset;
3116 r.y -= currentScrollOffset;
3126 * This method returns the number of runs.
3128 * @param pane The JTabbedPane.
3130 * @return The number of runs.
3132 public int getTabRunCount(JTabbedPane pane)
3138 * This method returns the tab index given a coordinate.
3140 * @param pane The JTabbedPane.
3141 * @param x The x coordinate.
3142 * @param y The y coordinate.
3144 * @return The tab index that the coordinate lands in.
3146 public int tabForCoordinate(JTabbedPane pane, int x, int y)
3148 // Note: This code is tab layout mode agnostic.
3149 if (! tabPane.isValid())
3152 int tabCount = tabPane.getTabCount();
3154 // If the user clicked outside of any tab rect the
3155 // selection should not change.
3156 int index = tabPane.getSelectedIndex();
3157 for (int i = 0; i < tabCount; ++i)
3159 if (rects[i].contains(x, y))
3170 * <p>This method returns the tab bounds in the given rectangle.</p>
3172 * <p>The returned rectangle will be shifted by the current scroll
3173 * offset if the tabbed pane is in scrolling tab layout mode.</p>.
3175 * @param tabIndex The index to get bounds for.
3176 * @param dest The rectangle to store bounds in.
3178 * @return The rectangle passed in.
3180 protected Rectangle getTabBounds(int tabIndex, Rectangle dest)
3182 dest.setBounds(getTabBounds(tabPane, tabIndex));
3187 * This method returns the component that is shown in the content area.
3189 * @return The component that is shown in the content area.
3191 protected Component getVisibleComponent()
3193 return visibleComponent;
3197 * This method sets the visible component.
3199 * @param component The component to be set visible.
3201 protected void setVisibleComponent(Component component)
3203 // Make old component invisible.
3204 if (visibleComponent != null && visibleComponent != component
3205 && visibleComponent.getParent() == tabPane)
3207 visibleComponent.setVisible(false);
3210 // Make new component visible.
3211 if (component != null && ! component.isVisible())
3213 component.setVisible(true);
3215 visibleComponent = component;
3219 * This method assures that enough rectangles are created given the
3220 * tabCount. The old array is copied to the new one.
3222 * @param tabCount The number of tabs.
3224 protected void assureRectsCreated(int tabCount)
3226 if (rects.length < tabCount)
3228 Rectangle[] old = rects;
3229 rects = new Rectangle[tabCount];
3230 System.arraycopy(old, 0, rects, 0, old.length);
3231 for (int i = old.length; i < rects.length; i++)
3232 rects[i] = new Rectangle();
3237 * This method expands the tabRuns array to give it more room. The old array
3238 * is copied to the new one.
3240 protected void expandTabRunsArray()
3242 // This method adds another 10 index positions to the tabRuns array.
3243 if (tabRuns == null)
3244 tabRuns = new int[10];
3247 int[] newRuns = new int[tabRuns.length + 10];
3248 System.arraycopy(tabRuns, 0, newRuns, 0, tabRuns.length);
3254 * This method returns which run a particular tab belongs to.
3256 * @param tabCount The number of tabs.
3257 * @param tabIndex The tab to find.
3259 * @return The tabRuns index that it belongs to.
3261 protected int getRunForTab(int tabCount, int tabIndex)
3263 if (runCount == 1 && tabIndex < tabCount && tabIndex >= 0)
3265 for (int i = 0; i < runCount; i++)
3267 int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1;
3268 if (first == tabCount)
3270 int last = lastTabInRun(tabCount, i);
3271 if (last >= tabIndex && first <= tabIndex)
3278 * This method returns the index of the last tab in a run.
3280 * @param tabCount The number of tabs.
3281 * @param run The run to check.
3283 * @return The last tab in the given run.
3285 protected int lastTabInRun(int tabCount, int run)
3289 lastTab = tabCount - 1;
3293 if (run == runCount - 1)
3298 if (tabRuns[nextRun] == 0)
3299 lastTab = tabCount - 1;
3301 lastTab = tabRuns[nextRun] - 1;
3307 * This method returns the tab run overlay.
3309 * @param tabPlacement The JTabbedPane's tab placement.
3311 * @return The tab run overlay.
3313 protected int getTabRunOverlay(int tabPlacement)
3315 return tabRunOverlay;
3319 * This method returns the tab run indent. It is used in WRAP_TAB_LAYOUT and
3320 * makes each tab run start indented by a certain amount.
3322 * @param tabPlacement The JTabbedPane's tab placement.
3323 * @param run The run to get indent for.
3325 * @return The amount a run should be indented.
3327 protected int getTabRunIndent(int tabPlacement, int run)
3333 * This method returns whether a tab run should be padded.
3335 * @param tabPlacement The JTabbedPane's tab placement.
3336 * @param run The run to check.
3338 * @return Whether the given run should be padded.
3340 protected boolean shouldPadTabRun(int tabPlacement, int run)
3346 * This method returns whether the tab runs should be rotated.
3348 * @param tabPlacement The JTabbedPane's tab placement.
3350 * @return Whether runs should be rotated.
3352 protected boolean shouldRotateTabRuns(int tabPlacement)
3358 * This method returns an icon for the tab. If the tab is disabled, it
3359 * should return the disabledIcon. If it is enabled, then it should return
3362 * @param tabIndex The tab index to get an icon for.
3364 * @return The icon for the tab index.
3366 protected Icon getIconForTab(int tabIndex)
3368 if (tabPane.isEnabledAt(tabIndex))
3369 return tabPane.getIconAt(tabIndex);
3371 return tabPane.getDisabledIconAt(tabIndex);
3375 * This method returns a view that can paint the text for the label.
3377 * @param tabIndex The tab index to get a view for.
3379 * @return The view for the tab index.
3381 protected View getTextViewForTab(int tabIndex)
3383 // FIXME: When the label contains HTML this should return something
3389 * This method returns the tab height, including insets, for the given index
3392 * @param tabPlacement The JTabbedPane's tab placement.
3393 * @param tabIndex The index of the tab to calculate.
3394 * @param fontHeight The font height.
3396 * @return This tab's height.
3398 protected int calculateTabHeight(int tabPlacement, int tabIndex,
3401 // FIXME: Handle HTML by using the view (see getTextViewForTab).
3403 int height = fontHeight;
3404 Icon icon = getIconForTab(tabIndex);
3405 Insets tabInsets = getTabInsets(tabPlacement, tabIndex);
3407 height = Math.max(height, icon.getIconHeight());
3408 height += tabInsets.top + tabInsets.bottom + 2;
3413 * This method returns the max tab height.
3415 * @param tabPlacement The JTabbedPane's tab placement.
3417 * @return The maximum tab height.
3419 protected int calculateMaxTabHeight(int tabPlacement)
3423 FontMetrics fm = getFontMetrics();
3424 int fontHeight = fm.getHeight();
3426 for (int i = 0; i < tabPane.getTabCount(); i++)
3427 maxTabHeight = Math.max(calculateTabHeight(tabPlacement, i, fontHeight),
3430 return maxTabHeight;
3434 * This method calculates the tab width, including insets, for the given tab
3435 * index and font metrics.
3437 * @param tabPlacement The JTabbedPane's tab placement.
3438 * @param tabIndex The tab index to calculate for.
3439 * @param metrics The font's metrics.
3441 * @return The tab width for the given index.
3443 protected int calculateTabWidth(int tabPlacement, int tabIndex,
3444 FontMetrics metrics)
3446 Icon icon = getIconForTab(tabIndex);
3447 Insets insets = getTabInsets(tabPlacement, tabIndex);
3449 int width = insets.bottom + insets.right + 3;
3452 width += icon.getIconWidth() + textIconGap;
3455 View v = getTextViewForTab(tabIndex);
3457 width += v.getPreferredSpan(View.X_AXIS);
3460 String label = tabPane.getTitleAt(tabIndex);
3461 width += metrics.stringWidth(label);
3467 * This method calculates the max tab width.
3469 * @param tabPlacement The JTabbedPane's tab placement.
3471 * @return The maximum tab width.
3473 protected int calculateMaxTabWidth(int tabPlacement)
3477 FontMetrics fm = getFontMetrics();
3479 for (int i = 0; i < tabPane.getTabCount(); i++)
3480 maxTabWidth = Math.max(calculateTabWidth(tabPlacement, i, fm),
3487 * This method calculates the tab area height, including insets, for the
3488 * given amount of runs and tab height.
3490 * @param tabPlacement The JTabbedPane's tab placement.
3491 * @param horizRunCount The number of runs.
3492 * @param maxTabHeight The max tab height.
3494 * @return The tab area height.
3496 protected int calculateTabAreaHeight(int tabPlacement, int horizRunCount,
3499 Insets insets = getTabAreaInsets(tabPlacement);
3500 int tabAreaHeight = horizRunCount * maxTabHeight
3501 - (horizRunCount - 1)
3502 * getTabRunOverlay(tabPlacement);
3504 tabAreaHeight += insets.top + insets.bottom;
3506 return tabAreaHeight;
3510 * This method calculates the tab area width, including insets, for the
3511 * given amount of runs and tab width.
3513 * @param tabPlacement The JTabbedPane's tab placement.
3514 * @param vertRunCount The number of runs.
3515 * @param maxTabWidth The max tab width.
3517 * @return The tab area width.
3519 protected int calculateTabAreaWidth(int tabPlacement, int vertRunCount,
3522 Insets insets = getTabAreaInsets(tabPlacement);
3523 int tabAreaWidth = vertRunCount * maxTabWidth
3524 - (vertRunCount - 1)
3525 * getTabRunOverlay(tabPlacement);
3527 tabAreaWidth += insets.left + insets.right;
3529 return tabAreaWidth;
3533 * This method returns the tab insets appropriately rotated.
3535 * @param tabPlacement The JTabbedPane's tab placement.
3536 * @param tabIndex The tab index.
3538 * @return The tab insets for the given index.
3540 protected Insets getTabInsets(int tabPlacement, int tabIndex)
3546 * This method returns the selected tab pad insets appropriately rotated.
3548 * @param tabPlacement The JTabbedPane's tab placement.
3550 * @return The selected tab pad insets.
3552 protected Insets getSelectedTabPadInsets(int tabPlacement)
3554 Insets target = new Insets(0, 0, 0, 0);
3555 rotateInsets(selectedTabPadInsets, target, tabPlacement);
3560 * This method returns the tab area insets appropriately rotated.
3562 * @param tabPlacement The JTabbedPane's tab placement.
3564 * @return The tab area insets.
3566 protected Insets getTabAreaInsets(int tabPlacement)
3568 Insets target = new Insets(0, 0, 0, 0);
3569 rotateInsets(tabAreaInsets, target, tabPlacement);
3574 * This method returns the content border insets appropriately rotated.
3576 * @param tabPlacement The JTabbedPane's tab placement.
3578 * @return The content border insets.
3580 protected Insets getContentBorderInsets(int tabPlacement)
3582 Insets target = new Insets(0, 0, 0, 0);
3583 rotateInsets(contentBorderInsets, target, tabPlacement);
3588 * This method returns the fontmetrics for the font of the JTabbedPane.
3590 * @return The font metrics for the JTabbedPane.
3592 protected FontMetrics getFontMetrics()
3594 FontMetrics fm = tabPane.getFontMetrics(tabPane.getFont());
3599 * This method navigates from the selected tab into the given direction. As
3600 * a result, a new tab will be selected (if possible).
3602 * @param direction The direction to navigate in.
3604 protected void navigateSelectedTab(int direction)
3606 int tabPlacement = tabPane.getTabPlacement();
3607 if (tabPlacement == SwingConstants.TOP
3608 || tabPlacement == SwingConstants.BOTTOM)
3610 if (direction == SwingConstants.WEST)
3611 selectPreviousTabInRun(tabPane.getSelectedIndex());
3612 else if (direction == SwingConstants.EAST)
3613 selectNextTabInRun(tabPane.getSelectedIndex());
3617 int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(),
3618 tabPane.getSelectedIndex(),
3619 (tabPlacement == SwingConstants.TOP)
3620 ? direction == SwingConstants.NORTH
3621 : direction == SwingConstants.SOUTH);
3622 selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(),
3626 if (tabPlacement == SwingConstants.LEFT
3627 || tabPlacement == SwingConstants.RIGHT)
3629 if (direction == SwingConstants.NORTH)
3630 selectPreviousTabInRun(tabPane.getSelectedIndex());
3631 else if (direction == SwingConstants.SOUTH)
3632 selectNextTabInRun(tabPane.getSelectedIndex());
3635 int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(),
3636 tabPane.getSelectedIndex(),
3637 (tabPlacement == SwingConstants.LEFT)
3638 ? direction == SwingConstants.WEST
3639 : direction == SwingConstants.EAST);
3640 selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(),
3647 * This method selects the next tab in the run.
3649 * @param current The current selected index.
3651 protected void selectNextTabInRun(int current)
3653 current = getNextTabIndexInRun(tabPane.getTabCount(),
3656 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
3657 scrollTab(current, tabPane.getTabPlacement());
3659 tabPane.setSelectedIndex(current);
3663 * This method selects the previous tab in the run.
3665 * @param current The current selected index.
3667 protected void selectPreviousTabInRun(int current)
3669 current = getPreviousTabIndexInRun(tabPane.getTabCount(),
3672 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
3673 scrollTab(current, tabPane.getTabPlacement());
3675 tabPane.setSelectedIndex(current);
3679 * This method selects the next tab (regardless of runs).
3681 * @param current The current selected index.
3683 protected void selectNextTab(int current)
3685 current = getNextTabIndex(current);
3687 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
3688 scrollTab(current, tabPane.getTabPlacement());
3690 tabPane.setSelectedIndex(current);
3694 * This method selects the previous tab (regardless of runs).
3696 * @param current The current selected index.
3698 protected void selectPreviousTab(int current)
3700 current = getPreviousTabIndex(current);
3702 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
3703 scrollTab(current, tabPane.getTabPlacement());
3705 tabPane.setSelectedIndex(current);
3709 * This method selects the correct tab given an offset from the current tab
3710 * index. If the tab placement is TOP or BOTTOM, the offset will be in the
3711 * y direction, otherwise, it will be in the x direction. A new coordinate
3712 * will be found by adding the offset to the current location of the tab.
3713 * The tab that the new location will be selected.
3715 * @param tabPlacement The JTabbedPane's tab placement.
3716 * @param tabIndex The tab to start from.
3717 * @param offset The coordinate offset.
3719 protected void selectAdjacentRunTab(int tabPlacement, int tabIndex,
3722 int x = rects[tabIndex].x + rects[tabIndex].width / 2;
3723 int y = rects[tabIndex].y + rects[tabIndex].height / 2;
3725 switch (tabPlacement)
3727 case SwingConstants.TOP:
3728 case SwingConstants.BOTTOM:
3731 case SwingConstants.RIGHT:
3732 case SwingConstants.LEFT:
3737 int index = tabForCoordinate(tabPane, x, y);
3740 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
3741 scrollTab(index, tabPlacement);
3742 tabPane.setSelectedIndex(index);
3746 // This method is called when you press up/down to cycle through tab runs.
3747 // it returns the distance (between the two runs' x/y position.
3748 // where one run is the current selected run and the other run is the run in the
3749 // direction of the scroll (dictated by the forward flag)
3750 // the offset is an absolute value of the difference
3753 * This method calculates the offset distance for use in
3754 * selectAdjacentRunTab. The offset returned will be a difference in the y
3755 * coordinate between the run in the desired direction and the current run
3756 * (for tabPlacement in TOP or BOTTOM). Use x coordinate for LEFT and
3759 * @param tabPlacement The JTabbedPane's tab placement.
3760 * @param tabCount The number of tabs.
3761 * @param tabIndex The starting index.
3762 * @param forward If forward, the run in the desired direction will be the
3765 * @return The offset between the two runs.
3767 protected int getTabRunOffset(int tabPlacement, int tabCount, int tabIndex,
3770 int currRun = getRunForTab(tabCount, tabIndex);
3772 int nextRun = forward ? getNextTabRun(currRun) : getPreviousTabRun(currRun);
3773 if (tabPlacement == SwingConstants.TOP
3774 || tabPlacement == SwingConstants.BOTTOM)
3775 offset = rects[lastTabInRun(tabCount, nextRun)].y
3776 - rects[lastTabInRun(tabCount, currRun)].y;
3778 offset = rects[lastTabInRun(tabCount, nextRun)].x
3779 - rects[lastTabInRun(tabCount, currRun)].x;
3785 * This method returns the previous tab index.
3787 * @param base The index to start from.
3789 * @return The previous tab index.
3791 protected int getPreviousTabIndex(int base)
3795 return tabPane.getTabCount() - 1;
3800 * This method returns the next tab index.
3802 * @param base The index to start from.
3804 * @return The next tab index.
3806 protected int getNextTabIndex(int base)
3809 if (base == tabPane.getTabCount())
3815 * This method returns the next tab index in the run. If the next index is
3816 * out of this run, it will return the starting tab index for the run.
3818 * @param tabCount The number of tabs.
3819 * @param base The index to start from.
3821 * @return The next tab index in the run.
3823 protected int getNextTabIndexInRun(int tabCount, int base)
3825 int index = getNextTabIndex(base);
3826 int run = getRunForTab(tabCount, base);
3827 if (base == lastTabInRun(tabCount, run))
3829 ? lastTabInRun(tabCount, getPreviousTabRun(run)) + 1
3836 * This method returns the previous tab index in the run. If the previous
3837 * index is out of this run, it will return the last index for the run.
3839 * @param tabCount The number of tabs.
3840 * @param base The index to start from.
3842 * @return The previous tab index in the run.
3844 protected int getPreviousTabIndexInRun(int tabCount, int base)
3846 int index = getPreviousTabIndex(base);
3847 int run = getRunForTab(tabCount, base);
3848 if (index == lastTabInRun(tabCount, getPreviousTabRun(run)))
3849 index = lastTabInRun(tabCount, run);
3855 * This method returns the index of the previous run.
3857 * @param baseRun The run to start from.
3859 * @return The index of the previous run.
3861 protected int getPreviousTabRun(int baseRun)
3863 if (getTabRunCount(tabPane) == 1)
3866 int prevRun = --baseRun;
3868 prevRun = getTabRunCount(tabPane) - 1;
3873 * This method returns the index of the next run.
3875 * @param baseRun The run to start from.
3877 * @return The index of the next run.
3879 protected int getNextTabRun(int baseRun)
3881 if (getTabRunCount(tabPane) == 1)
3884 int nextRun = ++baseRun;
3885 if (nextRun == getTabRunCount(tabPane))
3891 * This method rotates the insets given a direction to rotate them in.
3892 * Target placement should be one of TOP, LEFT, BOTTOM, RIGHT. The rotated
3893 * insets will be stored in targetInsets. Passing in TOP as the direction
3894 * does nothing. Passing in LEFT switches top and left, right and bottom.
3895 * Passing in BOTTOM switches top and bottom. Passing in RIGHT switches top
3896 * for left, left for bottom, bottom for right, and right for top.
3898 * @param topInsets The reference insets.
3899 * @param targetInsets An Insets object to store the new insets.
3900 * @param targetPlacement The rotation direction.
3902 protected static void rotateInsets(Insets topInsets, Insets targetInsets,
3903 int targetPlacement)
3905 // Sun's version will happily throw an NPE if params are null,
3906 // so I won't check it either.
3907 switch (targetPlacement)
3910 case SwingConstants.TOP:
3911 targetInsets.top = topInsets.top;
3912 targetInsets.left = topInsets.left;
3913 targetInsets.right = topInsets.right;
3914 targetInsets.bottom = topInsets.bottom;
3916 case SwingConstants.LEFT:
3917 targetInsets.left = topInsets.top;
3918 targetInsets.top = topInsets.left;
3919 targetInsets.right = topInsets.bottom;
3920 targetInsets.bottom = topInsets.right;
3922 case SwingConstants.BOTTOM:
3923 targetInsets.top = topInsets.bottom;
3924 targetInsets.bottom = topInsets.top;
3925 targetInsets.left = topInsets.left;
3926 targetInsets.right = topInsets.right;
3928 case SwingConstants.RIGHT:
3929 targetInsets.top = topInsets.left;
3930 targetInsets.left = topInsets.bottom;
3931 targetInsets.bottom = topInsets.right;
3932 targetInsets.right = topInsets.top;
3937 ActionMap getActionMap()
3939 ActionMap map = (ActionMap) UIManager.get("TabbedPane.actionMap");
3941 if (map == null) // first time here
3943 map = createActionMap();
3945 UIManager.put("TabbedPane.actionMap", map);
3950 ActionMap createActionMap()
3952 ActionMap map = new ActionMapUIResource();
3954 map.put("navigatePageDown", new NavigatePageDownAction());
3955 map.put("navigatePageUp", new NavigatePageUpAction());
3956 map.put("navigateDown",
3957 new NavigateAction("navigateDown", SwingConstants.SOUTH));
3959 map.put("navigateUp",
3960 new NavigateAction("navigateUp", SwingConstants.NORTH));
3962 map.put("navigateLeft",
3963 new NavigateAction("navigateLeft", SwingConstants.WEST));
3965 map.put("navigateRight",
3966 new NavigateAction("navigateRight", SwingConstants.EAST));
3968 map.put("requestFocusForVisibleComponent",
3969 new RequestFocusForVisibleComponentAction());
3970 map.put("requestFocus", new RequestFocusAction());
3976 * Sets the tab which should be highlighted when in rollover mode. And
3977 * <code>index</code> of <code>-1</code> means that the rollover tab
3978 * is deselected (i.e. the mouse is outside of the tabarea).
3980 * @param index the index of the tab that is under the mouse, <code>-1</code>
3985 protected void setRolloverTab(int index)
3987 rolloverTab = index;
3991 * Retunrs the index of the tab over which the mouse is currently moving,
3992 * or <code>-1</code> for no tab.
3994 * @return the index of the tab over which the mouse is currently moving,
3995 * or <code>-1</code> for no tab
3999 protected int getRolloverTab()