1 /* BasicTabbedPaneUI.java --
2 Copyright (C) 2002, 2004, 2005 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.FocusAdapter;
53 import java.awt.event.FocusEvent;
54 import java.awt.event.FocusListener;
55 import java.awt.event.MouseAdapter;
56 import java.awt.event.MouseEvent;
57 import java.awt.event.MouseListener;
58 import java.beans.PropertyChangeEvent;
59 import java.beans.PropertyChangeListener;
61 import javax.swing.Icon;
62 import javax.swing.JComponent;
63 import javax.swing.JPanel;
64 import javax.swing.JTabbedPane;
65 import javax.swing.JViewport;
66 import javax.swing.KeyStroke;
67 import javax.swing.SwingConstants;
68 import javax.swing.SwingUtilities;
69 import javax.swing.UIDefaults;
70 import javax.swing.UIManager;
71 import javax.swing.event.ChangeEvent;
72 import javax.swing.event.ChangeListener;
73 import javax.swing.plaf.ComponentUI;
74 import javax.swing.plaf.PanelUI;
75 import javax.swing.plaf.TabbedPaneUI;
76 import javax.swing.plaf.UIResource;
77 import javax.swing.text.View;
80 * This is the Basic Look and Feel's UI delegate for JTabbedPane.
82 public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
85 * A helper class that handles focus.
87 * @specnote Apparently this class was intended to be protected,
88 * but was made public by a compiler bug and is now
89 * public for compatibility.
91 public class FocusHandler extends FocusAdapter
94 * This method is called when the component gains focus.
96 * @param e The FocusEvent.
98 public void focusGained(FocusEvent e)
104 * This method is called when the component loses focus.
106 * @param e The FocusEvent.
108 public void focusLost(FocusEvent e)
115 * A helper class for determining if mouse presses occur inside tabs and
116 * sets the index appropriately. In SCROLL_TAB_MODE, this class also
117 * handles the mouse clicks on the scrolling buttons.
119 * @specnote Apparently this class was intended to be protected,
120 * but was made public by a compiler bug and is now
121 * public for compatibility.
123 public class MouseHandler extends MouseAdapter
126 * This method is called when the mouse is pressed. The index cannot
127 * change to a tab that is not enabled.
129 * @param e The MouseEvent.
131 public void mousePressed(MouseEvent e)
135 int tabCount = tabPane.getTabCount();
137 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
139 if (e.getSource() == incrButton)
141 if (++currentScrollLocation >= tabCount)
142 currentScrollLocation = tabCount - 1;
145 for (int i = currentScrollLocation - 1; i < tabCount; i++)
146 width += rects[i].width;
147 if (width < viewport.getWidth())
148 // FIXME: Still getting mouse events after the button is disabled.
149 // incrButton.setEnabled(false);
150 currentScrollLocation--;
151 else if (! decrButton.isEnabled())
152 decrButton.setEnabled(true);
153 tabPane.revalidate();
157 else if (e.getSource() == decrButton)
159 if (--currentScrollLocation < 0)
160 currentScrollLocation = 0;
161 if (currentScrollLocation == 0)
162 decrButton.setEnabled(false);
163 else if (! incrButton.isEnabled())
164 incrButton.setEnabled(true);
165 tabPane.revalidate();
171 int index = tabForCoordinate(tabPane, x, y);
173 // We need to check since there are areas where tabs cannot be
174 // e.g. in the inset area.
175 if (index != -1 && tabPane.isEnabledAt(index))
176 tabPane.setSelectedIndex(index);
177 tabPane.revalidate();
183 * This class handles PropertyChangeEvents fired from the JTabbedPane.
185 * @specnote Apparently this class was intended to be protected,
186 * but was made public by a compiler bug and is now
187 * public for compatibility.
189 public class PropertyChangeHandler implements PropertyChangeListener
192 * This method is called whenever one of the properties of the JTabbedPane
195 * @param e The PropertyChangeEvent.
197 public void propertyChange(PropertyChangeEvent e)
199 if (e.getPropertyName().equals("tabLayoutPolicy"))
201 layoutManager = createLayoutManager();
203 tabPane.setLayout(layoutManager);
205 else if (e.getPropertyName().equals("tabPlacement")
206 && tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
208 incrButton = createIncreaseButton();
209 decrButton = createDecreaseButton();
217 * A LayoutManager responsible for placing all the tabs and the visible
218 * component inside the JTabbedPane. This class is only used for
221 * @specnote Apparently this class was intended to be protected,
222 * but was made public by a compiler bug and is now
223 * public for compatibility.
225 public class TabbedPaneLayout implements LayoutManager
228 * This method is called when a component is added to the JTabbedPane.
230 * @param name The name of the component.
231 * @param comp The component being added.
233 public void addLayoutComponent(String name, Component comp)
239 * This method is called when the rectangles need to be calculated. It
240 * also fixes the size of the visible component.
242 public void calculateLayoutInfo()
244 calculateTabRects(tabPane.getTabPlacement(), tabPane.getTabCount());
246 if (tabPane.getSelectedIndex() != -1)
248 Component visible = getVisibleComponent();
249 Insets insets = getContentBorderInsets(tabPane.getTabPlacement());
251 visible.setBounds(contentRect.x + insets.left,
252 contentRect.y + insets.top,
253 contentRect.width - insets.left - insets.right,
254 contentRect.height - insets.top - insets.bottom);
259 * This method calculates the size of the the JTabbedPane.
261 * @param minimum Whether the JTabbedPane will try to be as small as it
264 * @return The desired size of the JTabbedPane.
266 protected Dimension calculateSize(boolean minimum)
268 int tabPlacement = tabPane.getTabPlacement();
272 int componentHeight = 0;
273 int componentWidth = 0;
276 for (int i = 0; i < tabPane.getTabCount(); i++)
278 c = tabPane.getComponentAt(i);
281 calcRect = c.getBounds();
282 dims = c.getPreferredSize();
285 componentHeight = Math.max(componentHeight, dims.height);
286 componentWidth = Math.max(componentWidth, dims.width);
289 Insets insets = tabPane.getInsets();
291 if (tabPlacement == SwingConstants.TOP
292 || tabPlacement == SwingConstants.BOTTOM)
294 int min = calculateMaxTabWidth(tabPlacement);
295 width = Math.max(min, componentWidth);
297 int tabAreaHeight = preferredTabAreaHeight(tabPlacement, width);
298 height = tabAreaHeight + componentHeight;
302 int min = calculateMaxTabHeight(tabPlacement);
303 height = Math.max(min, componentHeight);
305 int tabAreaWidth = preferredTabAreaWidth(tabPlacement, height);
306 width = tabAreaWidth + componentWidth;
309 return new Dimension(width, height);
312 // if tab placement is LEFT OR RIGHT, they share width.
313 // if tab placement is TOP OR BOTTOM, they share height
314 // PRE STEP: finds the default sizes for the labels as well as their locations.
315 // AND where they will be placed within the run system.
316 // 1. calls normalizeTab Runs.
317 // 2. calls rotate tab runs.
318 // 3. pads the tab runs.
319 // 4. pads the selected tab.
322 * This method is called to calculate the tab rectangles. This method
323 * will calculate the size and position of all rectangles (taking into
324 * account which ones should be in which tab run). It will pad them and
325 * normalize them as necessary.
327 * @param tabPlacement The JTabbedPane's tab placement.
328 * @param tabCount The run the current selection is in.
330 protected void calculateTabRects(int tabPlacement, int tabCount)
334 assureRectsCreated(tabCount);
336 FontMetrics fm = getFontMetrics();
337 SwingUtilities.calculateInnerArea(tabPane, calcRect);
338 Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
339 Insets insets = tabPane.getInsets();
342 int start = getTabRunIndent(tabPlacement, 1);
343 if (tabPlacement == SwingConstants.TOP
344 || tabPlacement == SwingConstants.BOTTOM)
346 int maxHeight = calculateMaxTabHeight(tabPlacement);
348 calcRect.width -= tabAreaInsets.left + tabAreaInsets.right;
349 max = calcRect.width + tabAreaInsets.left + insets.left;
350 start += tabAreaInsets.left + insets.left;
352 int runWidth = start;
354 for (int i = 0; i < tabCount; i++)
356 width = calculateTabWidth(tabPlacement, i, fm);
358 if (runWidth + width > max)
360 runWidth = tabAreaInsets.left + insets.left
361 + getTabRunIndent(tabPlacement, ++runs);
362 rects[i] = new Rectangle(runWidth,
363 insets.top + tabAreaInsets.top,
366 if (runs > tabRuns.length - 1)
367 expandTabRunsArray();
372 rects[i] = new Rectangle(runWidth,
373 insets.top + tabAreaInsets.top,
379 tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right;
380 tabAreaRect.height = runs * maxTabHeight
381 - (runs - 1) * tabRunOverlay
382 + tabAreaInsets.top + tabAreaInsets.bottom;
383 contentRect.width = tabAreaRect.width;
384 contentRect.height = tabPane.getHeight() - insets.top
385 - insets.bottom - tabAreaRect.height;
386 contentRect.x = insets.left;
387 tabAreaRect.x = insets.left;
388 if (tabPlacement == SwingConstants.BOTTOM)
390 contentRect.y = insets.top;
391 tabAreaRect.y = contentRect.y + contentRect.height;
395 tabAreaRect.y = insets.top;
396 contentRect.y = tabAreaRect.y + tabAreaRect.height;
401 int maxWidth = calculateMaxTabWidth(tabPlacement);
402 calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom;
403 max = calcRect.height + tabAreaInsets.top + insets.top;
406 start += tabAreaInsets.top + insets.top;
407 int runHeight = start;
409 int fontHeight = fm.getHeight();
411 for (int i = 0; i < tabCount; i++)
413 height = calculateTabHeight(tabPlacement, i, fontHeight);
414 if (runHeight + height > max)
416 runHeight = tabAreaInsets.top + insets.top
417 + getTabRunIndent(tabPlacement, ++runs);
418 rects[i] = new Rectangle(insets.left + tabAreaInsets.left,
419 runHeight, maxWidth, height);
421 if (runs > tabRuns.length - 1)
422 expandTabRunsArray();
427 rects[i] = new Rectangle(insets.left + tabAreaInsets.left,
428 runHeight, maxWidth, height);
434 tabAreaRect.width = runs * maxTabWidth - (runs - 1) * tabRunOverlay
435 + tabAreaInsets.left + tabAreaInsets.right;
436 tabAreaRect.height = tabPane.getHeight() - insets.top
438 tabAreaRect.y = insets.top;
439 contentRect.width = tabPane.getWidth() - insets.left - insets.right
441 contentRect.height = tabAreaRect.height;
442 contentRect.y = insets.top;
443 if (tabPlacement == SwingConstants.LEFT)
445 tabAreaRect.x = insets.left;
446 contentRect.x = tabAreaRect.x + tabAreaRect.width;
450 contentRect.x = insets.left;
451 tabAreaRect.x = contentRect.x + contentRect.width;
457 normalizeTabRuns(tabPlacement, tabCount, start, max);
458 selectedRun = getRunForTab(tabCount, tabPane.getSelectedIndex());
459 if (shouldRotateTabRuns(tabPlacement))
460 rotateTabRuns(tabPlacement, selectedRun);
462 // Need to pad the runs and move them to the correct location.
463 for (int i = 0; i < runCount; i++)
465 int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1;
466 if (first == tabCount)
468 int last = lastTabInRun(tabCount, i);
469 if (shouldPadTabRun(tabPlacement, i))
470 padTabRun(tabPlacement, first, last, max);
472 // Done padding, now need to move it.
473 if (tabPlacement == SwingConstants.TOP && i > 0)
475 for (int j = first; j <= last; j++)
476 rects[j].y += (runCount - i) * maxTabHeight
477 - (runCount - i) * tabRunOverlay;
480 if (tabPlacement == SwingConstants.BOTTOM)
482 int height = tabPane.getBounds().height - insets.bottom
483 - tabAreaInsets.bottom;
486 adjustment = height - maxTabHeight;
488 adjustment = height - (runCount - i + 1) * maxTabHeight
489 - (runCount - i) * tabRunOverlay;
491 for (int j = first; j <= last; j++)
492 rects[j].y = adjustment;
495 if (tabPlacement == SwingConstants.LEFT && i > 0)
497 for (int j = first; j <= last; j++)
498 rects[j].x += (runCount - i) * maxTabWidth
499 - (runCount - i) * tabRunOverlay;
502 if (tabPlacement == SwingConstants.RIGHT)
504 int width = tabPane.getBounds().width - insets.right
505 - tabAreaInsets.right;
508 adjustment = width - maxTabWidth;
510 adjustment = width - (runCount - i + 1) * maxTabWidth
511 + (runCount - i) * tabRunOverlay;
513 for (int j = first; j <= last; j++)
514 rects[j].x = adjustment;
517 padSelectedTab(tabPlacement, tabPane.getSelectedIndex());
521 * This method is called when the JTabbedPane is laid out in
522 * WRAP_TAB_LAYOUT. It calls calculateLayoutInfo to find the positions
523 * of all its components.
525 * @param parent The Container to lay out.
527 public void layoutContainer(Container parent)
529 calculateLayoutInfo();
533 * This method returns the minimum layout size for the given container.
535 * @param parent The container that is being sized.
537 * @return The minimum size.
539 public Dimension minimumLayoutSize(Container parent)
541 return calculateSize(false);
544 // If there is more free space in an adjacent run AND the tab in the run can fit in the
545 // adjacent run, move it. This method is not perfect, it is merely an approximation.
546 // If you play around with Sun's JTabbedPane, you'll see that
547 // it does do some pretty strange things with regards to not moving tabs
548 // that should be moved.
549 // start = the x position where the tabs will begin
550 // max = the maximum position of where the tabs can go to (tabAreaInsets.left + the width of the tab area)
553 * This method tries to "even out" the number of tabs in each run based on
556 * @param tabPlacement The JTabbedPane's tab placement.
557 * @param tabCount The number of tabs.
558 * @param start The x position where the tabs will begin.
559 * @param max The maximum x position where the tab can run to.
561 protected void normalizeTabRuns(int tabPlacement, int tabCount, int start,
564 Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
565 if (tabPlacement == SwingUtilities.TOP
566 || tabPlacement == SwingUtilities.BOTTOM)
568 // We should only do this for runCount - 1, cause we can only shift that many times between
570 for (int i = 1; i < runCount; i++)
572 Rectangle currRun = rects[lastTabInRun(tabCount, i)];
573 Rectangle nextRun = rects[lastTabInRun(tabCount, getNextTabRun(i))];
574 int spaceInCurr = currRun.x + currRun.width;
575 int spaceInNext = nextRun.x + nextRun.width;
577 int diffNow = spaceInCurr - spaceInNext;
578 int diffLater = (spaceInCurr - currRun.width)
579 - (spaceInNext + currRun.width);
580 while (Math.abs(diffLater) < Math.abs(diffNow)
581 && spaceInNext + currRun.width < max)
584 spaceInNext += currRun.width;
585 spaceInCurr -= currRun.width;
586 currRun = rects[lastTabInRun(tabCount, i)];
587 diffNow = spaceInCurr - spaceInNext;
588 diffLater = (spaceInCurr - currRun.width)
589 - (spaceInNext + currRun.width);
593 int first = lastTabInRun(tabCount, i) + 1;
594 int last = lastTabInRun(tabCount, getNextTabRun(i));
595 int currX = tabAreaInsets.left;
596 for (int j = first; j <= last; j++)
599 currX += rects[j].width;
605 for (int i = 1; i < runCount; i++)
607 Rectangle currRun = rects[lastTabInRun(tabCount, i)];
608 Rectangle nextRun = rects[lastTabInRun(tabCount, getNextTabRun(i))];
609 int spaceInCurr = currRun.y + currRun.height;
610 int spaceInNext = nextRun.y + nextRun.height;
612 int diffNow = spaceInCurr - spaceInNext;
613 int diffLater = (spaceInCurr - currRun.height)
614 - (spaceInNext + currRun.height);
615 while (Math.abs(diffLater) < Math.abs(diffNow)
616 && spaceInNext + currRun.height < max)
619 spaceInNext += currRun.height;
620 spaceInCurr -= currRun.height;
621 currRun = rects[lastTabInRun(tabCount, i)];
622 diffNow = spaceInCurr - spaceInNext;
623 diffLater = (spaceInCurr - currRun.height)
624 - (spaceInNext + currRun.height);
627 int first = lastTabInRun(tabCount, i) + 1;
628 int last = lastTabInRun(tabCount, getNextTabRun(i));
629 int currY = tabAreaInsets.top;
630 for (int j = first; j <= last; j++)
633 currY += rects[j].height;
640 * This method pads the tab at the selected index by the selected tab pad
641 * insets (so that it looks larger).
643 * @param tabPlacement The placement of the tabs.
644 * @param selectedIndex The selected index.
646 protected void padSelectedTab(int tabPlacement, int selectedIndex)
648 Insets insets = getSelectedTabPadInsets(tabPlacement);
649 rects[selectedIndex].x -= insets.left;
650 rects[selectedIndex].y -= insets.top;
651 rects[selectedIndex].width += insets.left + insets.right;
652 rects[selectedIndex].height += insets.top + insets.bottom;
655 // If the tabs on the run don't fill the width of the window, make it fit now.
656 // start = starting index of the run
657 // end = last index of the run
658 // max = tabAreaInsets.left + width (or equivalent)
659 // assert start <= end.
662 * This method makes each tab in the run larger so that the tabs expand
663 * to fill the runs width/height (depending on tabPlacement).
665 * @param tabPlacement The placement of the tabs.
666 * @param start The index of the first tab.
667 * @param end The last index of the tab
668 * @param max The amount of space in the run (width for TOP and BOTTOM
671 protected void padTabRun(int tabPlacement, int start, int end, int max)
673 if (tabPlacement == SwingConstants.TOP
674 || tabPlacement == SwingConstants.BOTTOM)
676 int runWidth = rects[end].x + rects[end].width;
677 int spaceRemaining = max - runWidth;
678 int numTabs = end - start + 1;
680 // now divvy up the space.
681 int spaceAllocated = spaceRemaining / numTabs;
682 int currX = rects[start].x;
683 for (int i = start; i <= end; i++)
686 rects[i].width += spaceAllocated;
687 currX += rects[i].width;
688 // This is used because since the spaceAllocated
689 // variable is an int, it rounds down. Sometimes,
690 // we don't fill an entire row, so we make it do
692 if (i == end && rects[i].x + rects[i].width != max)
693 rects[i].width = max - rects[i].x;
698 int runHeight = rects[end].y + rects[end].height;
699 int spaceRemaining = max - runHeight;
700 int numTabs = end - start + 1;
702 int spaceAllocated = spaceRemaining / numTabs;
703 int currY = rects[start].y;
704 for (int i = start; i <= end; i++)
707 rects[i].height += spaceAllocated;
708 currY += rects[i].height;
709 if (i == end && rects[i].y + rects[i].height != max)
710 rects[i].height = max - rects[i].y;
716 * This method returns the preferred layout size for the given container.
718 * @param parent The container to size.
720 * @return The preferred layout size.
722 public Dimension preferredLayoutSize(Container parent)
724 return calculateSize(false);
728 * This method returns the preferred tab height given a tabPlacement and
731 * @param tabPlacement The JTabbedPane's tab placement.
732 * @param width The expected width.
734 * @return The preferred tab area height.
736 protected int preferredTabAreaHeight(int tabPlacement, int width)
738 if (tabPane.getTabCount() == 0)
739 return calculateTabAreaHeight(tabPlacement, 0, 0);
745 FontMetrics fm = getFontMetrics();
747 Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
748 Insets insets = tabPane.getInsets();
750 // Only interested in width, this is a messed up rectangle now.
751 width -= tabAreaInsets.left + tabAreaInsets.right + insets.left
754 // The reason why we can't use runCount:
755 // This method is only called to calculate the size request
756 // for the tabbedPane. However, this size request is dependent on
757 // our desired width. We need to find out what the height would
758 // be IF we got our desired width.
759 for (int i = 0; i < tabPane.getTabCount(); i++)
761 tabWidth = calculateTabWidth(tabPlacement, i, fm);
762 if (runWidth + tabWidth > width)
768 runWidth += tabWidth;
772 int maxTabHeight = calculateMaxTabHeight(tabPlacement);
773 int tabAreaHeight = calculateTabAreaHeight(tabPlacement, runs,
775 return tabAreaHeight;
779 * This method calculates the preferred tab area width given a tab
780 * placement and height.
782 * @param tabPlacement The JTabbedPane's tab placement.
783 * @param height The expected height.
785 * @return The preferred tab area width.
787 protected int preferredTabAreaWidth(int tabPlacement, int height)
789 if (tabPane.getTabCount() == 0)
790 return calculateTabAreaHeight(tabPlacement, 0, 0);
796 FontMetrics fm = getFontMetrics();
798 Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
799 Insets insets = tabPane.getInsets();
801 height -= tabAreaInsets.top + tabAreaInsets.bottom + insets.top
803 int fontHeight = fm.getHeight();
805 for (int i = 0; i < tabPane.getTabCount(); i++)
807 tabHeight = calculateTabHeight(tabPlacement, i, fontHeight);
808 if (runHeight + tabHeight > height)
810 runHeight = tabHeight;
814 runHeight += tabHeight;
818 int maxTabWidth = calculateMaxTabWidth(tabPlacement);
819 int tabAreaWidth = calculateTabAreaWidth(tabPlacement, runs, maxTabWidth);
824 * This method rotates the places each run in the correct place the
825 * tabRuns array. See the comment for tabRuns for how the runs are placed
828 * @param tabPlacement The JTabbedPane's tab placement.
829 * @param selectedRun The run the current selection is in.
831 protected void rotateTabRuns(int tabPlacement, int selectedRun)
833 if (runCount == 1 || selectedRun == 1 || selectedRun == -1)
835 int[] newTabRuns = new int[tabRuns.length];
836 int currentRun = selectedRun;
840 newTabRuns[i] = tabRuns[currentRun];
841 currentRun = getNextTabRun(currentRun);
844 while (i < runCount);
846 newTabRuns[0] = tabRuns[currentRun];
848 tabRuns = newTabRuns;
849 BasicTabbedPaneUI.this.selectedRun = 1;
853 * This method is called when a component is removed from the
856 * @param comp The component removed.
858 public void removeLayoutComponent(Component comp)
865 * This class acts as the LayoutManager for the JTabbedPane in
868 private class TabbedPaneScrollLayout extends TabbedPaneLayout
871 * This method returns the preferred layout size for the given container.
873 * @param parent The container to calculate a size for.
875 * @return The preferred layout size.
877 public Dimension preferredLayoutSize(Container parent)
879 return super.calculateSize(true);
883 * This method returns the minimum layout size for the given container.
885 * @param parent The container to calculate a size for.
887 * @return The minimum layout size.
889 public Dimension minimumLayoutSize(Container parent)
891 return super.calculateSize(true);
895 * This method calculates the tab area height given a desired width.
897 * @param tabPlacement The JTabbedPane's tab placement.
898 * @param width The expected width.
900 * @return The tab area height given the width.
902 protected int preferredTabAreaHeight(int tabPlacement, int width)
904 if (tabPane.getTabCount() == 0)
905 return calculateTabAreaHeight(tabPlacement, 0, 0);
909 int maxTabHeight = calculateMaxTabHeight(tabPlacement);
910 int tabAreaHeight = calculateTabAreaHeight(tabPlacement, runs,
912 return tabAreaHeight;
916 * This method calculates the tab area width given a desired height.
918 * @param tabPlacement The JTabbedPane's tab placement.
919 * @param height The expected height.
921 * @return The tab area width given the height.
923 protected int preferredTabAreaWidth(int tabPlacement, int height)
925 if (tabPane.getTabCount() == 0)
926 return calculateTabAreaHeight(tabPlacement, 0, 0);
930 int maxTabWidth = calculateMaxTabWidth(tabPlacement);
931 int tabAreaWidth = calculateTabAreaWidth(tabPlacement, runs, maxTabWidth);
936 * This method is called to calculate the tab rectangles. This method
937 * will calculate the size and position of all rectangles (taking into
938 * account which ones should be in which tab run). It will pad them and
939 * normalize them as necessary.
941 * @param tabPlacement The JTabbedPane's tab placement.
942 * @param tabCount The number of tabs.
944 protected void calculateTabRects(int tabPlacement, int tabCount)
948 assureRectsCreated(tabCount);
950 FontMetrics fm = getFontMetrics();
951 SwingUtilities.calculateInnerArea(tabPane, calcRect);
952 Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
953 Insets insets = tabPane.getInsets();
958 if (tabPlacement == SwingConstants.TOP
959 || tabPlacement == SwingConstants.BOTTOM)
961 int maxHeight = calculateMaxTabHeight(tabPlacement);
962 calcRect.width -= tabAreaInsets.left + tabAreaInsets.right;
963 max = calcRect.width + tabAreaInsets.left + insets.left;
964 start = tabAreaInsets.left + insets.left;
966 int runWidth = start;
967 top = insets.top + tabAreaInsets.top;
968 for (int i = 0; i < tabCount; i++)
970 width = calculateTabWidth(tabPlacement, i, fm);
972 rects[i] = new Rectangle(runWidth, top, width, maxHeight);
975 tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right;
976 tabAreaRect.height = runs * maxTabHeight
977 - (runs - 1) * tabRunOverlay
978 + tabAreaInsets.top + tabAreaInsets.bottom;
979 contentRect.width = tabAreaRect.width;
980 contentRect.height = tabPane.getHeight() - insets.top
981 - insets.bottom - tabAreaRect.height;
982 contentRect.x = insets.left;
983 tabAreaRect.x = insets.left;
984 if (tabPlacement == SwingConstants.BOTTOM)
986 contentRect.y = insets.top;
987 tabAreaRect.y = contentRect.y + contentRect.height;
991 tabAreaRect.y = insets.top;
992 contentRect.y = tabAreaRect.y + tabAreaRect.height;
997 int maxWidth = calculateMaxTabWidth(tabPlacement);
999 calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom;
1000 max = calcRect.height + tabAreaInsets.top;
1002 start = tabAreaInsets.top + insets.top;
1003 int runHeight = start;
1004 int fontHeight = fm.getHeight();
1005 top = insets.left + tabAreaInsets.left;
1006 for (int i = 0; i < tabCount; i++)
1008 height = calculateTabHeight(tabPlacement, i, fontHeight);
1009 rects[i] = new Rectangle(top, runHeight, maxWidth, height);
1010 runHeight += height;
1012 tabAreaRect.width = runs * maxTabWidth - (runs - 1) * tabRunOverlay
1013 + tabAreaInsets.left + tabAreaInsets.right;
1014 tabAreaRect.height = tabPane.getHeight() - insets.top
1016 tabAreaRect.y = insets.top;
1017 contentRect.width = tabPane.getWidth() - insets.left - insets.right
1018 - tabAreaRect.width;
1019 contentRect.height = tabAreaRect.height;
1020 contentRect.y = insets.top;
1021 if (tabPlacement == SwingConstants.LEFT)
1023 tabAreaRect.x = insets.left;
1024 contentRect.x = tabAreaRect.x + tabAreaRect.width;
1028 contentRect.x = insets.left;
1029 tabAreaRect.x = contentRect.x + contentRect.width;
1034 padSelectedTab(tabPlacement, tabPane.getSelectedIndex());
1038 * This method is called when the JTabbedPane is laid out in
1039 * SCROLL_TAB_LAYOUT. It finds the position for all components in the
1042 * @param pane The JTabbedPane to be laid out.
1044 public void layoutContainer(Container pane)
1046 super.layoutContainer(pane);
1047 int tabCount = tabPane.getTabCount();
1051 int tabPlacement = tabPane.getTabPlacement();
1054 if (tabPlacement == SwingConstants.TOP
1055 || tabPlacement == SwingConstants.BOTTOM)
1057 if (tabAreaRect.x + tabAreaRect.width < rects[tabCount - 1].x
1058 + rects[tabCount - 1].width)
1060 Dimension incrDims = incrButton.getPreferredSize();
1061 Dimension decrDims = decrButton.getPreferredSize();
1063 decrButton.setBounds(tabAreaRect.x + tabAreaRect.width
1064 - incrDims.width - decrDims.width,
1065 tabAreaRect.y, decrDims.width,
1066 tabAreaRect.height);
1067 incrButton.setBounds(tabAreaRect.x + tabAreaRect.width
1068 - incrDims.width, tabAreaRect.y,
1069 decrDims.width, tabAreaRect.height);
1071 tabAreaRect.width -= decrDims.width + incrDims.width;
1077 if (tabPlacement == SwingConstants.LEFT
1078 || tabPlacement == SwingConstants.RIGHT)
1080 if (tabAreaRect.y + tabAreaRect.height < rects[tabCount - 1].y
1081 + rects[tabCount - 1].height)
1083 Dimension incrDims = incrButton.getPreferredSize();
1084 Dimension decrDims = decrButton.getPreferredSize();
1086 decrButton.setBounds(tabAreaRect.x,
1087 tabAreaRect.y + tabAreaRect.height
1088 - incrDims.height - decrDims.height,
1089 tabAreaRect.width, decrDims.height);
1090 incrButton.setBounds(tabAreaRect.x,
1091 tabAreaRect.y + tabAreaRect.height
1092 - incrDims.height, tabAreaRect.width,
1095 tabAreaRect.height -= decrDims.height + incrDims.height;
1100 viewport.setBounds(tabAreaRect.x, tabAreaRect.y, tabAreaRect.width,
1101 tabAreaRect.height);
1102 int tabC = tabPane.getTabCount() - 1;
1105 int w = Math.max(rects[tabC].width + rects[tabC].x, tabAreaRect.width);
1106 int h = Math.max(rects[tabC].height, tabAreaRect.height);
1107 p = findPointForIndex(currentScrollLocation);
1109 // we want to cover that entire space so that borders that run under
1110 // the tab area don't show up when we move the viewport around.
1111 panel.setSize(w + p.x, h + p.y);
1113 viewport.setViewPosition(p);
1119 * This class handles ChangeEvents from the JTabbedPane.
1121 * @specnote Apparently this class was intended to be protected,
1122 * but was made public by a compiler bug and is now
1123 * public for compatibility.
1125 public class TabSelectionHandler implements ChangeListener
1128 * This method is called whenever a ChangeEvent is fired from the
1131 * @param e The ChangeEvent fired.
1133 public void stateChanged(ChangeEvent e)
1135 selectedRun = getRunForTab(tabPane.getTabCount(),
1136 tabPane.getSelectedIndex());
1137 tabPane.revalidate();
1143 * This helper class is a JPanel that fits inside the ScrollViewport. This
1144 * panel's sole job is to paint the tab rectangles inside the viewport so
1145 * that it's clipped correctly.
1147 private class ScrollingPanel extends JPanel
1150 * This is a private UI class for our panel.
1152 private class ScrollingPanelUI extends BasicPanelUI
1155 * This method overrides the default paint method. It paints the tab
1156 * rectangles for the JTabbedPane in the panel.
1158 * @param g The Graphics object to paint with.
1159 * @param c The JComponent to paint.
1161 public void paint(Graphics g, JComponent c)
1163 paintTabArea(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex());
1168 * This method overrides the updateUI method. It makes the default UI for
1169 * this ScrollingPanel to be a ScrollingPanelUI.
1171 public void updateUI()
1173 setUI((PanelUI) new ScrollingPanelUI());
1178 * This is a helper class that paints the panel that paints tabs. This
1179 * custom JViewport is used so that the tabs painted in the panel will be
1180 * clipped. This class implements UIResource so tabs are not added when
1181 * this objects of this class are added to the JTabbedPane.
1183 private class ScrollingViewport extends JViewport implements UIResource
1188 * This is a helper class that implements UIResource so it is not added as a
1189 * tab when an object of this class is added to the JTabbedPane.
1191 private class ScrollingButton extends BasicArrowButton implements UIResource
1194 * Creates a ScrollingButton given the direction.
1196 * @param dir The direction to point in.
1198 public ScrollingButton(int dir)
1204 /** The button that increments the current scroll location.
1205 * This is package-private to avoid an accessor method. */
1206 transient ScrollingButton incrButton;
1208 /** The button that decrements the current scroll location.
1209 * This is package-private to avoid an accessor method. */
1210 transient ScrollingButton decrButton;
1212 /** The viewport used to display the tabs.
1213 * This is package-private to avoid an accessor method. */
1214 transient ScrollingViewport viewport;
1216 /** The panel inside the viewport that paints the tabs.
1217 * This is package-private to avoid an accessor method. */
1218 transient ScrollingPanel panel;
1220 /** The starting visible tab in the run in SCROLL_TAB_MODE.
1221 * This is package-private to avoid an accessor method. */
1222 transient int currentScrollLocation;
1224 /** A reusable rectangle. */
1225 protected Rectangle calcRect;
1227 /** An array of Rectangles keeping track of the tabs' area and position. */
1228 protected Rectangle[] rects;
1230 /** The insets around the content area. */
1231 protected Insets contentBorderInsets;
1233 /** The extra insets around the selected tab. */
1234 protected Insets selectedTabPadInsets;
1236 /** The insets around the tab area. */
1237 protected Insets tabAreaInsets;
1239 /** The insets around each and every tab. */
1240 protected Insets tabInsets;
1243 * The outer bottom and right edge color for both the tab and content
1246 protected Color darkShadow;
1248 /** The color of the focus outline on the selected tab. */
1249 protected Color focus;
1251 /** FIXME: find a use for this. */
1252 protected Color highlight;
1254 /** The top and left edge color for both the tab and content border. */
1255 protected Color lightHighlight;
1257 /** The inner bottom and right edge color for the tab and content border. */
1258 protected Color shadow;
1260 /** The maximum tab height. */
1261 protected int maxTabHeight;
1263 /** The maximum tab width. */
1264 protected int maxTabWidth;
1266 /** The number of runs in the JTabbedPane. */
1267 protected int runCount;
1269 /** The index of the run that the selected index is in. */
1270 protected int selectedRun;
1272 /** The amount of space each run overlaps the previous by. */
1273 protected int tabRunOverlay;
1275 /** The gap between text and label */
1276 protected int textIconGap;
1278 // Keeps track of tab runs.
1279 // The organization of this array is as follows (lots of experimentation to
1281 // index 0 = furthest away from the component area (aka outer run)
1282 // index 1 = closest to component area (aka selected run)
1283 // index > 1 = listed in order leading from selected run to outer run.
1284 // each int in the array is the tab index + 1 (counting starts at 1)
1285 // for the last tab in the run. (same as the rects array)
1287 /** This array keeps track of which tabs are in which run. See above. */
1288 protected int[] tabRuns;
1291 * This is the keystroke for moving down.
1295 protected KeyStroke downKey;
1298 * This is the keystroke for moving left.
1302 protected KeyStroke leftKey;
1305 * This is the keystroke for moving right.
1309 protected KeyStroke rightKey;
1312 * This is the keystroke for moving up.
1316 protected KeyStroke upKey;
1318 /** The listener that listens for focus events. */
1319 protected FocusListener focusListener;
1321 /** The listener that listens for mouse events. */
1322 protected MouseListener mouseListener;
1324 /** The listener that listens for property change events. */
1325 protected PropertyChangeListener propertyChangeListener;
1327 /** The listener that listens for change events. */
1328 protected ChangeListener tabChangeListener;
1330 /** The tab pane that this UI paints. */
1331 protected JTabbedPane tabPane;
1333 /** The current layout manager for the tabPane.
1334 * This is package-private to avoid an accessor method. */
1335 transient LayoutManager layoutManager;
1337 /** The rectangle that describes the tab area's position and size.
1338 * This is package-private to avoid an accessor method. */
1339 transient Rectangle tabAreaRect;
1341 /** The rectangle that describes the content area's position and
1342 * size. This is package-private to avoid an accessor method. */
1343 transient Rectangle contentRect;
1346 * Creates a new BasicTabbedPaneUI object.
1348 public BasicTabbedPaneUI()
1354 * This method creates a ScrollingButton that points in the appropriate
1355 * direction for an increasing button.
1356 * This is package-private to avoid an accessor method.
1358 * @return The increase ScrollingButton.
1360 ScrollingButton createIncreaseButton()
1362 if (incrButton == null)
1363 incrButton = new ScrollingButton(SwingConstants.NORTH);
1364 if (tabPane.getTabPlacement() == SwingConstants.TOP
1365 || tabPane.getTabPlacement() == SwingConstants.BOTTOM)
1366 incrButton.setDirection(SwingConstants.EAST);
1368 incrButton.setDirection(SwingConstants.SOUTH);
1373 * This method creates a ScrollingButton that points in the appropriate
1374 * direction for a decreasing button.
1375 * This is package-private to avoid an accessor method.
1377 * @return The decrease ScrollingButton.
1379 ScrollingButton createDecreaseButton()
1381 if (decrButton == null)
1382 decrButton = new ScrollingButton(SwingConstants.SOUTH);
1383 if (tabPane.getTabPlacement() == SwingConstants.TOP
1384 || tabPane.getTabPlacement() == SwingConstants.BOTTOM)
1385 decrButton.setDirection(SwingConstants.WEST);
1387 decrButton.setDirection(SwingConstants.NORTH);
1392 * This method finds the point to set the view position at given the index
1393 * of a tab. The tab will be the first visible tab in the run.
1394 * This is package-private to avoid an accessor method.
1396 * @param index The index of the first visible tab.
1398 * @return The position of the first visible tab.
1400 Point findPointForIndex(int index)
1402 int tabPlacement = tabPane.getTabPlacement();
1403 int selectedIndex = tabPane.getSelectedIndex();
1404 Insets insets = getSelectedTabPadInsets(tabPlacement);
1408 if (tabPlacement == TOP || tabPlacement == BOTTOM)
1412 w += rects[index - 1].x + rects[index - 1].width;
1413 if (index > selectedIndex)
1414 w -= insets.left + insets.right;
1422 h += rects[index - 1].y + rects[index - 1].height;
1423 if (index > selectedIndex)
1424 h -= insets.top + insets.bottom;
1428 Point p = new Point(w, h);
1433 * This method creates a new BasicTabbedPaneUI.
1435 * @param c The JComponent to create a UI for.
1437 * @return A new BasicTabbedPaneUI.
1439 public static ComponentUI createUI(JComponent c)
1441 return new BasicTabbedPaneUI();
1445 * This method installs the UI for the given JComponent.
1447 * @param c The JComponent to install the UI for.
1449 public void installUI(JComponent c)
1452 if (c instanceof JTabbedPane)
1454 tabPane = (JTabbedPane) c;
1456 installComponents();
1459 installKeyboardActions();
1461 layoutManager = createLayoutManager();
1462 tabPane.setLayout(layoutManager);
1468 * This method uninstalls the UI for the given JComponent.
1470 * @param c The JComponent to uninstall the UI for.
1472 public void uninstallUI(JComponent c)
1474 layoutManager = null;
1476 uninstallKeyboardActions();
1477 uninstallListeners();
1478 uninstallDefaults();
1479 uninstallComponents();
1485 * This method creates the appropriate layout manager for the JTabbedPane's
1486 * current tab layout policy. If the tab layout policy is
1487 * SCROLL_TAB_LAYOUT, then all the associated components that need to be
1488 * created will be done so now.
1490 * @return A layout manager given the tab layout policy.
1492 protected LayoutManager createLayoutManager()
1494 if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT)
1495 return new TabbedPaneLayout();
1498 incrButton = createIncreaseButton();
1499 decrButton = createDecreaseButton();
1500 viewport = new ScrollingViewport();
1501 viewport.setLayout(null);
1502 panel = new ScrollingPanel();
1503 viewport.setView(panel);
1504 tabPane.add(incrButton);
1505 tabPane.add(decrButton);
1506 tabPane.add(viewport);
1507 currentScrollLocation = 0;
1508 decrButton.setEnabled(false);
1509 panel.addMouseListener(mouseListener);
1510 incrButton.addMouseListener(mouseListener);
1511 decrButton.addMouseListener(mouseListener);
1512 viewport.setBackground(Color.LIGHT_GRAY);
1514 return new TabbedPaneScrollLayout();
1519 * This method installs components for this JTabbedPane.
1521 protected void installComponents()
1523 // Nothing to be done.
1527 * This method uninstalls components for this JTabbedPane.
1529 protected void uninstallComponents()
1531 // Nothing to be done.
1535 * This method installs defaults for the Look and Feel.
1537 protected void installDefaults()
1539 UIDefaults defaults = UIManager.getLookAndFeelDefaults();
1541 tabPane.setFont(defaults.getFont("TabbedPane.font"));
1542 tabPane.setForeground(defaults.getColor("TabbedPane.foreground"));
1543 tabPane.setBackground(defaults.getColor("TabbedPane.background"));
1544 tabPane.setOpaque(false);
1546 highlight = defaults.getColor("TabbedPane.highlight");
1547 lightHighlight = defaults.getColor("TabbedPane.lightHighlight");
1549 shadow = defaults.getColor("TabbedPane.shadow");
1550 darkShadow = defaults.getColor("TabbedPane.darkShadow");
1552 focus = defaults.getColor("TabbedPane.focus");
1554 textIconGap = defaults.getInt("TabbedPane.textIconGap");
1555 tabRunOverlay = defaults.getInt("TabbedPane.tabRunOverlay");
1557 tabInsets = defaults.getInsets("TabbedPane.tabbedPaneTabInsets");
1558 selectedTabPadInsets = defaults.getInsets("TabbedPane.tabbedPaneTabPadInsets");
1559 tabAreaInsets = defaults.getInsets("TabbedPane.tabbedPaneTabAreaInsets");
1560 contentBorderInsets = defaults.getInsets("TabbedPane.tabbedPaneContentBorderInsets");
1562 calcRect = new Rectangle();
1563 tabRuns = new int[10];
1564 tabAreaRect = new Rectangle();
1565 contentRect = new Rectangle();
1569 * This method uninstalls defaults for the Look and Feel.
1571 protected void uninstallDefaults()
1578 contentBorderInsets = null;
1579 tabAreaInsets = null;
1580 selectedTabPadInsets = null;
1586 lightHighlight = null;
1589 tabPane.setBackground(null);
1590 tabPane.setForeground(null);
1591 tabPane.setFont(null);
1595 * This method creates and installs the listeners for this UI.
1597 protected void installListeners()
1599 mouseListener = createMouseListener();
1600 tabChangeListener = createChangeListener();
1601 propertyChangeListener = createPropertyChangeListener();
1602 focusListener = createFocusListener();
1604 tabPane.addMouseListener(mouseListener);
1605 tabPane.addChangeListener(tabChangeListener);
1606 tabPane.addPropertyChangeListener(propertyChangeListener);
1607 tabPane.addFocusListener(focusListener);
1611 * This method removes and nulls the listeners for this UI.
1613 protected void uninstallListeners()
1615 tabPane.removeFocusListener(focusListener);
1616 tabPane.removePropertyChangeListener(propertyChangeListener);
1617 tabPane.removeChangeListener(tabChangeListener);
1618 tabPane.removeMouseListener(mouseListener);
1620 focusListener = null;
1621 propertyChangeListener = null;
1622 tabChangeListener = null;
1623 mouseListener = null;
1627 * This method creates a new MouseListener.
1629 * @return A new MouseListener.
1631 protected MouseListener createMouseListener()
1633 return new MouseHandler();
1637 * This method creates a new FocusListener.
1639 * @return A new FocusListener.
1641 protected FocusListener createFocusListener()
1643 return new FocusHandler();
1647 * This method creates a new ChangeListener.
1649 * @return A new ChangeListener.
1651 protected ChangeListener createChangeListener()
1653 return new TabSelectionHandler();
1657 * This method creates a new PropertyChangeListener.
1659 * @return A new PropertyChangeListener.
1661 protected PropertyChangeListener createPropertyChangeListener()
1663 return new PropertyChangeHandler();
1667 * This method installs keyboard actions for the JTabbedPane.
1669 protected void installKeyboardActions()
1671 // FIXME: Implement.
1675 * This method uninstalls keyboard actions for the JTabbedPane.
1677 protected void uninstallKeyboardActions()
1679 // FIXME: Implement.
1683 * This method returns the minimum size of the JTabbedPane.
1685 * @param c The JComponent to find a size for.
1687 * @return The minimum size.
1689 public Dimension getMinimumSize(JComponent c)
1691 return layoutManager.minimumLayoutSize(tabPane);
1695 * This method returns the maximum size of the JTabbedPane.
1697 * @param c The JComponent to find a size for.
1699 * @return The maximum size.
1701 public Dimension getMaximumSize(JComponent c)
1703 return new Dimension(Short.MAX_VALUE, Short.MAX_VALUE);
1707 * This method paints the JTabbedPane.
1709 * @param g The Graphics object to paint with.
1710 * @param c The JComponent to paint.
1712 public void paint(Graphics g, JComponent c)
1714 if (tabPane.getTabCount() == 0)
1716 if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT)
1717 paintTabArea(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex());
1718 paintContentBorder(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex());
1722 * This method paints the tab area. This includes painting the rectangles
1723 * that make up the tabs.
1725 * @param g The Graphics object to paint with.
1726 * @param tabPlacement The JTabbedPane's tab placement.
1727 * @param selectedIndex The selected index.
1729 protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex)
1731 Rectangle ir = new Rectangle();
1732 Rectangle tr = new Rectangle();
1734 boolean isScroll = tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT;
1736 // Please note: the ordering of the painting is important.
1737 // we WANT to paint the outermost run first and then work our way in.
1738 int tabCount = tabPane.getTabCount();
1745 for (int i = 0; i < runCount; i++)
1747 int first = lastTabInRun(tabCount, getPreviousTabRun(currRun)) + 1;
1749 first = currentScrollLocation;
1750 else if (first == tabCount)
1752 int last = lastTabInRun(tabCount, currRun);
1755 for (int k = first; k < tabCount; k++)
1757 if (rects[k].x + rects[k].width - rects[first].x > viewport
1766 for (int j = first; j <= last; j++)
1768 if (j != selectedIndex || isScroll)
1769 paintTab(g, tabPlacement, rects, j, ir, tr);
1771 currRun = getPreviousTabRun(currRun);
1774 paintTab(g, tabPlacement, rects, selectedIndex, ir, tr);
1778 * This method paints an individual tab.
1780 * @param g The Graphics object to paint with.
1781 * @param tabPlacement The JTabbedPane's tab placement.
1782 * @param rects The array of rectangles that keep the size and position of
1784 * @param tabIndex The tab index to paint.
1785 * @param iconRect The rectangle to use for the icon.
1786 * @param textRect The rectangle to use for the text.
1788 protected void paintTab(Graphics g, int tabPlacement, Rectangle[] rects,
1789 int tabIndex, Rectangle iconRect, Rectangle textRect)
1791 FontMetrics fm = getFontMetrics();
1792 Icon icon = getIconForTab(tabIndex);
1793 String title = tabPane.getTitleAt(tabIndex);
1794 boolean isSelected = tabIndex == tabPane.getSelectedIndex();
1795 calcRect = getTabBounds(tabPane, tabIndex);
1799 int w = calcRect.width;
1800 int h = calcRect.height;
1801 if (getRunForTab(tabPane.getTabCount(), tabIndex) == 1)
1803 Insets insets = getTabAreaInsets(tabPlacement);
1804 switch (tabPlacement)
1823 layoutLabel(tabPlacement, fm, tabIndex, title, icon, calcRect, iconRect,
1824 textRect, isSelected);
1825 paintTabBackground(g, tabPlacement, tabIndex, x, y, w, h, isSelected);
1826 paintTabBorder(g, tabPlacement, tabIndex, x, y, w, h, isSelected);
1828 // FIXME: Paint little folding corner and jagged edge clipped tab.
1830 paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected);
1831 if (title != null && ! title.equals(""))
1832 paintText(g, tabPlacement, tabPane.getFont(), fm, tabIndex, title,
1833 textRect, isSelected);
1837 * This method lays out the tab and finds the location to paint the icon
1840 * @param tabPlacement The JTabbedPane's tab placement.
1841 * @param metrics The font metrics for the font to paint with.
1842 * @param tabIndex The tab index to paint.
1843 * @param title The string painted.
1844 * @param icon The icon painted.
1845 * @param tabRect The tab bounds.
1846 * @param iconRect The calculated icon bounds.
1847 * @param textRect The calculated text bounds.
1848 * @param isSelected Whether this tab is selected.
1850 protected void layoutLabel(int tabPlacement, FontMetrics metrics,
1851 int tabIndex, String title, Icon icon,
1852 Rectangle tabRect, Rectangle iconRect,
1853 Rectangle textRect, boolean isSelected)
1855 SwingUtilities.layoutCompoundLabel(metrics, title, icon,
1856 SwingConstants.CENTER,
1857 SwingConstants.CENTER,
1858 SwingConstants.CENTER,
1859 SwingConstants.CENTER, tabRect,
1860 iconRect, textRect, textIconGap);
1862 int shiftX = getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
1863 int shiftY = getTabLabelShiftY(tabPlacement, tabIndex, isSelected);
1865 iconRect.x += shiftX;
1866 iconRect.y += shiftY;
1868 textRect.x += shiftX;
1869 textRect.y += shiftY;
1873 * This method paints the icon.
1875 * @param g The Graphics object to paint.
1876 * @param tabPlacement The JTabbedPane's tab placement.
1877 * @param tabIndex The tab index to paint.
1878 * @param icon The icon to paint.
1879 * @param iconRect The bounds of the icon.
1880 * @param isSelected Whether this tab is selected.
1882 protected void paintIcon(Graphics g, int tabPlacement, int tabIndex,
1883 Icon icon, Rectangle iconRect, boolean isSelected)
1885 icon.paintIcon(tabPane, g, iconRect.x, iconRect.y);
1889 * This method paints the text for the given tab.
1891 * @param g The Graphics object to paint with.
1892 * @param tabPlacement The JTabbedPane's tab placement.
1893 * @param font The font to paint with.
1894 * @param metrics The fontmetrics of the given font.
1895 * @param tabIndex The tab index.
1896 * @param title The string to paint.
1897 * @param textRect The bounds of the string.
1898 * @param isSelected Whether this tab is selected.
1900 protected void paintText(Graphics g, int tabPlacement, Font font,
1901 FontMetrics metrics, int tabIndex, String title,
1902 Rectangle textRect, boolean isSelected)
1904 View textView = getTextViewForTab(tabIndex);
1905 if (textView != null)
1907 textView.paint(g, textRect);
1911 Color fg = tabPane.getForegroundAt(tabIndex);
1913 fg = tabPane.getForeground();
1914 Color bg = tabPane.getBackgroundAt(tabIndex);
1916 bg = tabPane.getBackground();
1918 Color saved_color = g.getColor();
1919 Font f = g.getFont();
1922 if (tabPane.isEnabledAt(tabIndex))
1926 int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
1928 if (mnemIndex != -1)
1929 BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
1932 + metrics.getAscent());
1934 g.drawString(title, textRect.x, textRect.y + metrics.getAscent());
1938 g.setColor(bg.brighter());
1940 int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
1942 if (mnemIndex != -1)
1943 BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
1944 textRect.x, textRect.y);
1946 g.drawString(title, textRect.x, textRect.y);
1948 g.setColor(bg.darker());
1949 if (mnemIndex != -1)
1950 BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
1954 g.drawString(title, textRect.x + 1, textRect.y + 1);
1957 g.setColor(saved_color);
1962 * This method returns how much the label for the tab should shift in the X
1965 * @param tabPlacement The JTabbedPane's tab placement.
1966 * @param tabIndex The tab index being painted.
1967 * @param isSelected Whether this tab is selected.
1969 * @return The amount the label should shift by in the X direction.
1971 protected int getTabLabelShiftX(int tabPlacement, int tabIndex,
1974 // No reason to shift.
1979 * This method returns how much the label for the tab should shift in the Y
1982 * @param tabPlacement The JTabbedPane's tab placement.
1983 * @param tabIndex The tab index being painted.
1984 * @param isSelected Whether this tab is selected.
1986 * @return The amount the label should shift by in the Y direction.
1988 protected int getTabLabelShiftY(int tabPlacement, int tabIndex,
1991 // No reason to shift.
1996 * This method paints the focus rectangle around the selected tab.
1998 * @param g The Graphics object to paint with.
1999 * @param tabPlacement The JTabbedPane's tab placement.
2000 * @param rects The array of rectangles keeping track of size and position.
2001 * @param tabIndex The tab index.
2002 * @param iconRect The icon bounds.
2003 * @param textRect The text bounds.
2004 * @param isSelected Whether this tab is selected.
2006 protected void paintFocusIndicator(Graphics g, int tabPlacement,
2007 Rectangle[] rects, int tabIndex,
2008 Rectangle iconRect, Rectangle textRect,
2011 Color saved = g.getColor();
2012 calcRect = iconRect.union(textRect);
2016 g.drawRect(calcRect.x, calcRect.y, calcRect.width, calcRect.height);
2022 * This method paints the border for an individual tab.
2024 * @param g The Graphics object to paint with.
2025 * @param tabPlacement The JTabbedPane's tab placement.
2026 * @param tabIndex The tab index.
2027 * @param x The x position of the tab.
2028 * @param y The y position of the tab.
2029 * @param w The width of the tab.
2030 * @param h The height of the tab.
2031 * @param isSelected Whether the tab is selected.
2033 protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex,
2034 int x, int y, int w, int h, boolean isSelected)
2036 Color saved = g.getColor();
2038 if (! isSelected || tabPlacement != SwingConstants.TOP)
2041 g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1);
2042 g.setColor(darkShadow);
2043 g.drawLine(x, y + h, x + w, y + h);
2046 if (! isSelected || tabPlacement != SwingConstants.LEFT)
2048 g.setColor(darkShadow);
2049 g.drawLine(x + w, y, x + w, y + h);
2051 g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1);
2054 if (! isSelected || tabPlacement != SwingConstants.RIGHT)
2056 g.setColor(lightHighlight);
2057 g.drawLine(x, y, x, y + h);
2060 if (! isSelected || tabPlacement != SwingConstants.BOTTOM)
2062 g.setColor(lightHighlight);
2063 g.drawLine(x, y, x + w, y);
2070 * This method paints the background for an individual tab.
2072 * @param g The Graphics object to paint with.
2073 * @param tabPlacement The JTabbedPane's tab placement.
2074 * @param tabIndex The tab index.
2075 * @param x The x position of the tab.
2076 * @param y The y position of the tab.
2077 * @param w The width of the tab.
2078 * @param h The height of the tab.
2079 * @param isSelected Whether the tab is selected.
2081 protected void paintTabBackground(Graphics g, int tabPlacement,
2082 int tabIndex, int x, int y, int w, int h,
2085 Color saved = g.getColor();
2087 g.setColor(Color.LIGHT_GRAY);
2090 Color bg = tabPane.getBackgroundAt(tabIndex);
2096 g.fillRect(x, y, w, h);
2102 * This method paints the border around the content area.
2104 * @param g The Graphics object to paint with.
2105 * @param tabPlacement The JTabbedPane's tab placement.
2106 * @param selectedIndex The index of the selected tab.
2108 protected void paintContentBorder(Graphics g, int tabPlacement,
2111 Insets insets = getContentBorderInsets(tabPlacement);
2112 int x = contentRect.x;
2113 int y = contentRect.y;
2114 int w = contentRect.width;
2115 int h = contentRect.height;
2116 paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2117 paintContentBorderLeftEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2118 paintContentBorderBottomEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2119 paintContentBorderRightEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2123 * This method paints the top edge of the content border.
2125 * @param g The Graphics object to paint with.
2126 * @param tabPlacement The JTabbedPane's tab placement.
2127 * @param selectedIndex The selected tab index.
2128 * @param x The x coordinate for the content area.
2129 * @param y The y coordinate for the content area.
2130 * @param w The width of the content area.
2131 * @param h The height of the content area.
2133 protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
2134 int selectedIndex, int x, int y,
2137 Color saved = g.getColor();
2138 g.setColor(lightHighlight);
2140 int startgap = rects[selectedIndex].x;
2141 int endgap = rects[selectedIndex].x + rects[selectedIndex].width;
2145 if (tabPlacement == SwingConstants.TOP)
2147 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
2149 Point p = findPointForIndex(currentScrollLocation);
2153 g.drawLine(x, y, startgap - diff, y);
2154 g.drawLine(endgap - diff, y, x + w, y);
2157 g.drawLine(x, y, x + w, y);
2163 * This method paints the left edge of the content border.
2165 * @param g The Graphics object to paint with.
2166 * @param tabPlacement The JTabbedPane's tab placement.
2167 * @param selectedIndex The selected tab index.
2168 * @param x The x coordinate for the content area.
2169 * @param y The y coordinate for the content area.
2170 * @param w The width of the content area.
2171 * @param h The height of the content area.
2173 protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
2174 int selectedIndex, int x, int y,
2177 Color saved = g.getColor();
2178 g.setColor(lightHighlight);
2180 int startgap = rects[selectedIndex].y;
2181 int endgap = rects[selectedIndex].y + rects[selectedIndex].height;
2185 if (tabPlacement == SwingConstants.LEFT)
2187 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
2189 Point p = findPointForIndex(currentScrollLocation);
2193 g.drawLine(x, y, x, startgap - diff);
2194 g.drawLine(x, endgap - diff, x, y + h);
2197 g.drawLine(x, y, x, y + h);
2203 * This method paints the bottom edge of the content border.
2205 * @param g The Graphics object to paint with.
2206 * @param tabPlacement The JTabbedPane's tab placement.
2207 * @param selectedIndex The selected tab index.
2208 * @param x The x coordinate for the content area.
2209 * @param y The y coordinate for the content area.
2210 * @param w The width of the content area.
2211 * @param h The height of the content area.
2213 protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
2214 int selectedIndex, int x, int y,
2217 Color saved = g.getColor();
2219 int startgap = rects[selectedIndex].x;
2220 int endgap = rects[selectedIndex].x + rects[selectedIndex].width;
2224 if (tabPlacement == SwingConstants.BOTTOM)
2226 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
2228 Point p = findPointForIndex(currentScrollLocation);
2233 g.drawLine(x + 1, y + h - 1, startgap - diff, y + h - 1);
2234 g.drawLine(endgap - diff, y + h - 1, x + w - 1, y + h - 1);
2236 g.setColor(darkShadow);
2237 g.drawLine(x, y + h, startgap - diff, y + h);
2238 g.drawLine(endgap - diff, y + h, x + w, y + h);
2243 g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1);
2244 g.setColor(darkShadow);
2245 g.drawLine(x, y + h, x + w, y + h);
2252 * This method paints the right edge of the content border.
2254 * @param g The Graphics object to paint with.
2255 * @param tabPlacement The JTabbedPane's tab placement.
2256 * @param selectedIndex The selected tab index.
2257 * @param x The x coordinate for the content area.
2258 * @param y The y coordinate for the content area.
2259 * @param w The width of the content area.
2260 * @param h The height of the content area.
2262 protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
2263 int selectedIndex, int x, int y,
2266 Color saved = g.getColor();
2267 int startgap = rects[selectedIndex].y;
2268 int endgap = rects[selectedIndex].y + rects[selectedIndex].height;
2272 if (tabPlacement == SwingConstants.RIGHT)
2274 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
2276 Point p = findPointForIndex(currentScrollLocation);
2281 g.drawLine(x + w - 1, y + 1, x + w - 1, startgap - diff);
2282 g.drawLine(x + w - 1, endgap - diff, x + w - 1, y + h - 1);
2284 g.setColor(darkShadow);
2285 g.drawLine(x + w, y, x + w, startgap - diff);
2286 g.drawLine(x + w, endgap - diff, x + w, y + h);
2291 g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1);
2292 g.setColor(darkShadow);
2293 g.drawLine(x + w, y, x + w, y + h);
2300 * This method returns the tab bounds for the given index.
2302 * @param pane The JTabbedPane.
2303 * @param i The index to look for.
2305 * @return The bounds of the tab with the given index.
2307 public Rectangle getTabBounds(JTabbedPane pane, int i)
2313 * This method returns the number of runs.
2315 * @param pane The JTabbedPane.
2317 * @return The number of runs.
2319 public int getTabRunCount(JTabbedPane pane)
2325 * This method returns the tab index given a coordinate.
2327 * @param pane The JTabbedPane.
2328 * @param x The x coordinate.
2329 * @param y The y coordinate.
2331 * @return The tab index that the coordinate lands in.
2333 public int tabForCoordinate(JTabbedPane pane, int x, int y)
2335 Point p = new Point(x, y);
2336 int tabCount = tabPane.getTabCount();
2338 for (int i = 0; i < runCount; i++)
2340 int first = lastTabInRun(tabCount, getPreviousTabRun(currRun)) + 1;
2341 if (first == tabCount)
2343 int last = lastTabInRun(tabCount, currRun);
2344 for (int j = first; j <= last; j++)
2346 if (getTabBounds(pane, j).contains(p))
2349 currRun = getNextTabRun(currRun);
2355 * This method returns the tab bounds in the given rectangle.
2357 * @param tabIndex The index to get bounds for.
2358 * @param dest The rectangle to store bounds in.
2360 * @return The rectangle passed in.
2362 protected Rectangle getTabBounds(int tabIndex, Rectangle dest)
2364 dest.setBounds(getTabBounds(tabPane, tabIndex));
2369 * This method returns the component that is shown in the content area.
2371 * @return The component that is shown in the content area.
2373 protected Component getVisibleComponent()
2375 return tabPane.getComponentAt(tabPane.getSelectedIndex());
2379 * This method sets the visible component.
2381 * @param component The component to be set visible.
2383 protected void setVisibleComponent(Component component)
2385 component.setVisible(true);
2386 tabPane.setSelectedComponent(component);
2390 * This method assures that enough rectangles are created given the
2391 * tabCount. The old array is copied to the new one.
2393 * @param tabCount The number of tabs.
2395 protected void assureRectsCreated(int tabCount)
2398 rects = new Rectangle[tabCount];
2399 if (tabCount == rects.length)
2403 int numToCopy = Math.min(tabCount, rects.length);
2404 Rectangle[] tmp = new Rectangle[tabCount];
2405 System.arraycopy(rects, 0, tmp, 0, numToCopy);
2411 * This method expands the tabRuns array to give it more room. The old array
2412 * is copied to the new one.
2414 protected void expandTabRunsArray()
2416 // This method adds another 10 index positions to the tabRuns array.
2417 if (tabRuns == null)
2418 tabRuns = new int[10];
2421 int[] newRuns = new int[tabRuns.length + 10];
2422 System.arraycopy(tabRuns, 0, newRuns, 0, tabRuns.length);
2428 * This method returns which run a particular tab belongs to.
2430 * @param tabCount The number of tabs.
2431 * @param tabIndex The tab to find.
2433 * @return The tabRuns index that it belongs to.
2435 protected int getRunForTab(int tabCount, int tabIndex)
2437 if (runCount == 1 && tabIndex < tabCount && tabIndex >= 0)
2439 for (int i = 0; i < runCount; i++)
2441 int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1;
2442 if (first == tabCount)
2444 int last = lastTabInRun(tabCount, i);
2445 if (last >= tabIndex && first <= tabIndex)
2452 * This method returns the index of the last tab in a run.
2454 * @param tabCount The number of tabs.
2455 * @param run The run to check.
2457 * @return The last tab in the given run.
2459 protected int lastTabInRun(int tabCount, int run)
2461 if (tabRuns[run] == 0)
2462 return tabCount - 1;
2464 return tabRuns[run] - 1;
2468 * This method returns the tab run overlay.
2470 * @param tabPlacement The JTabbedPane's tab placement.
2472 * @return The tab run overlay.
2474 protected int getTabRunOverlay(int tabPlacement)
2476 return tabRunOverlay;
2480 * This method returns the tab run indent. It is used in WRAP_TAB_LAYOUT and
2481 * makes each tab run start indented by a certain amount.
2483 * @param tabPlacement The JTabbedPane's tab placement.
2484 * @param run The run to get indent for.
2486 * @return The amount a run should be indented.
2488 protected int getTabRunIndent(int tabPlacement, int run)
2494 * This method returns whether a tab run should be padded.
2496 * @param tabPlacement The JTabbedPane's tab placement.
2497 * @param run The run to check.
2499 * @return Whether the given run should be padded.
2501 protected boolean shouldPadTabRun(int tabPlacement, int run)
2507 * This method returns whether the tab runs should be rotated.
2509 * @param tabPlacement The JTabbedPane's tab placement.
2511 * @return Whether runs should be rotated.
2513 protected boolean shouldRotateTabRuns(int tabPlacement)
2519 * This method returns an icon for the tab. If the tab is disabled, it
2520 * should return the disabledIcon. If it is enabled, then it should return
2523 * @param tabIndex The tab index to get an icon for.
2525 * @return The icon for the tab index.
2527 protected Icon getIconForTab(int tabIndex)
2529 if (tabPane.isEnabledAt(tabIndex))
2530 return tabPane.getIconAt(tabIndex);
2532 return tabPane.getDisabledIconAt(tabIndex);
2536 * This method returns a view that can paint the text for the label.
2538 * @param tabIndex The tab index to get a view for.
2540 * @return The view for the tab index.
2542 protected View getTextViewForTab(int tabIndex)
2548 * This method returns the tab height, including insets, for the given index
2551 * @param tabPlacement The JTabbedPane's tab placement.
2552 * @param tabIndex The index of the tab to calculate.
2553 * @param fontHeight The font height.
2555 * @return This tab's height.
2557 protected int calculateTabHeight(int tabPlacement, int tabIndex,
2560 Icon icon = getIconForTab(tabIndex);
2561 Insets insets = getTabInsets(tabPlacement, tabIndex);
2565 Rectangle vr = new Rectangle();
2566 Rectangle ir = new Rectangle();
2567 Rectangle tr = new Rectangle();
2568 layoutLabel(tabPlacement, getFontMetrics(), tabIndex,
2569 tabPane.getTitleAt(tabIndex), icon, vr, ir, tr,
2570 tabIndex == tabPane.getSelectedIndex());
2571 calcRect = tr.union(ir);
2574 calcRect.height = fontHeight;
2576 calcRect.height += insets.top + insets.bottom;
2577 return calcRect.height;
2581 * This method returns the max tab height.
2583 * @param tabPlacement The JTabbedPane's tab placement.
2585 * @return The maximum tab height.
2587 protected int calculateMaxTabHeight(int tabPlacement)
2591 FontMetrics fm = getFontMetrics();
2592 int fontHeight = fm.getHeight();
2594 for (int i = 0; i < tabPane.getTabCount(); i++)
2595 maxTabHeight = Math.max(calculateTabHeight(tabPlacement, i, fontHeight),
2598 return maxTabHeight;
2602 * This method calculates the tab width, including insets, for the given tab
2603 * index and font metrics.
2605 * @param tabPlacement The JTabbedPane's tab placement.
2606 * @param tabIndex The tab index to calculate for.
2607 * @param metrics The font's metrics.
2609 * @return The tab width for the given index.
2611 protected int calculateTabWidth(int tabPlacement, int tabIndex,
2612 FontMetrics metrics)
2614 Icon icon = getIconForTab(tabIndex);
2615 Insets insets = getTabInsets(tabPlacement, tabIndex);
2619 Rectangle vr = new Rectangle();
2620 Rectangle ir = new Rectangle();
2621 Rectangle tr = new Rectangle();
2622 layoutLabel(tabPlacement, getFontMetrics(), tabIndex,
2623 tabPane.getTitleAt(tabIndex), icon, vr, ir, tr,
2624 tabIndex == tabPane.getSelectedIndex());
2625 calcRect = tr.union(ir);
2628 calcRect.width = metrics.stringWidth(tabPane.getTitleAt(tabIndex));
2630 calcRect.width += insets.left + insets.right;
2631 return calcRect.width;
2635 * This method calculates the max tab width.
2637 * @param tabPlacement The JTabbedPane's tab placement.
2639 * @return The maximum tab width.
2641 protected int calculateMaxTabWidth(int tabPlacement)
2645 FontMetrics fm = getFontMetrics();
2647 for (int i = 0; i < tabPane.getTabCount(); i++)
2648 maxTabWidth = Math.max(calculateTabWidth(tabPlacement, i, fm),
2655 * This method calculates the tab area height, including insets, for the
2656 * given amount of runs and tab height.
2658 * @param tabPlacement The JTabbedPane's tab placement.
2659 * @param horizRunCount The number of runs.
2660 * @param maxTabHeight The max tab height.
2662 * @return The tab area height.
2664 protected int calculateTabAreaHeight(int tabPlacement, int horizRunCount,
2667 Insets insets = getTabAreaInsets(tabPlacement);
2668 int tabAreaHeight = horizRunCount * maxTabHeight
2669 - (horizRunCount - 1) * tabRunOverlay;
2671 tabAreaHeight += insets.top + insets.bottom;
2673 return tabAreaHeight;
2677 * This method calculates the tab area width, including insets, for the
2678 * given amount of runs and tab width.
2680 * @param tabPlacement The JTabbedPane's tab placement.
2681 * @param vertRunCount The number of runs.
2682 * @param maxTabWidth The max tab width.
2684 * @return The tab area width.
2686 protected int calculateTabAreaWidth(int tabPlacement, int vertRunCount,
2689 Insets insets = getTabAreaInsets(tabPlacement);
2690 int tabAreaWidth = vertRunCount * maxTabWidth
2691 - (vertRunCount - 1) * tabRunOverlay;
2693 tabAreaWidth += insets.left + insets.right;
2695 return tabAreaWidth;
2699 * This method returns the tab insets appropriately rotated.
2701 * @param tabPlacement The JTabbedPane's tab placement.
2702 * @param tabIndex The tab index.
2704 * @return The tab insets for the given index.
2706 protected Insets getTabInsets(int tabPlacement, int tabIndex)
2708 Insets target = new Insets(0, 0, 0, 0);
2709 rotateInsets(tabInsets, target, tabPlacement);
2714 * This method returns the selected tab pad insets appropriately rotated.
2716 * @param tabPlacement The JTabbedPane's tab placement.
2718 * @return The selected tab pad insets.
2720 protected Insets getSelectedTabPadInsets(int tabPlacement)
2722 Insets target = new Insets(0, 0, 0, 0);
2723 rotateInsets(selectedTabPadInsets, target, tabPlacement);
2728 * This method returns the tab area insets appropriately rotated.
2730 * @param tabPlacement The JTabbedPane's tab placement.
2732 * @return The tab area insets.
2734 protected Insets getTabAreaInsets(int tabPlacement)
2736 Insets target = new Insets(0, 0, 0, 0);
2737 rotateInsets(tabAreaInsets, target, tabPlacement);
2742 * This method returns the content border insets appropriately rotated.
2744 * @param tabPlacement The JTabbedPane's tab placement.
2746 * @return The content border insets.
2748 protected Insets getContentBorderInsets(int tabPlacement)
2750 Insets target = new Insets(0, 0, 0, 0);
2751 rotateInsets(contentBorderInsets, target, tabPlacement);
2756 * This method returns the fontmetrics for the font of the JTabbedPane.
2758 * @return The font metrics for the JTabbedPane.
2760 protected FontMetrics getFontMetrics()
2762 FontMetrics fm = tabPane.getToolkit().getFontMetrics(tabPane.getFont());
2767 * This method navigates from the selected tab into the given direction. As
2768 * a result, a new tab will be selected (if possible).
2770 * @param direction The direction to navigate in.
2772 protected void navigateSelectedTab(int direction)
2774 int tabPlacement = tabPane.getTabPlacement();
2775 if (tabPlacement == SwingConstants.TOP
2776 || tabPlacement == SwingConstants.BOTTOM)
2778 if (direction == SwingConstants.WEST)
2779 selectPreviousTabInRun(tabPane.getSelectedIndex());
2780 else if (direction == SwingConstants.EAST)
2781 selectNextTabInRun(tabPane.getSelectedIndex());
2785 int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(),
2786 tabPane.getSelectedIndex(),
2787 (tabPlacement == SwingConstants.RIGHT)
2789 selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(),
2793 if (tabPlacement == SwingConstants.LEFT
2794 || tabPlacement == SwingConstants.RIGHT)
2796 if (direction == SwingConstants.NORTH)
2797 selectPreviousTabInRun(tabPane.getSelectedIndex());
2798 else if (direction == SwingConstants.SOUTH)
2799 selectNextTabInRun(tabPane.getSelectedIndex());
2802 int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(),
2803 tabPane.getSelectedIndex(),
2804 (tabPlacement == SwingConstants.RIGHT)
2806 selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(),
2813 * This method selects the next tab in the run.
2815 * @param current The current selected index.
2817 protected void selectNextTabInRun(int current)
2819 tabPane.setSelectedIndex(getNextTabIndexInRun(tabPane.getTabCount(),
2824 * This method selects the previous tab in the run.
2826 * @param current The current selected index.
2828 protected void selectPreviousTabInRun(int current)
2830 tabPane.setSelectedIndex(getPreviousTabIndexInRun(tabPane.getTabCount(),
2835 * This method selects the next tab (regardless of runs).
2837 * @param current The current selected index.
2839 protected void selectNextTab(int current)
2841 tabPane.setSelectedIndex(getNextTabIndex(current));
2845 * This method selects the previous tab (regardless of runs).
2847 * @param current The current selected index.
2849 protected void selectPreviousTab(int current)
2851 tabPane.setSelectedIndex(getPreviousTabIndex(current));
2855 * This method selects the correct tab given an offset from the current tab
2856 * index. If the tab placement is TOP or BOTTOM, the offset will be in the
2857 * y direction, otherwise, it will be in the x direction. A new coordinate
2858 * will be found by adding the offset to the current location of the tab.
2859 * The tab that the new location will be selected.
2861 * @param tabPlacement The JTabbedPane's tab placement.
2862 * @param tabIndex The tab to start from.
2863 * @param offset The coordinate offset.
2865 protected void selectAdjacentRunTab(int tabPlacement, int tabIndex,
2868 int x = rects[tabIndex].x + rects[tabIndex].width / 2;
2869 int y = rects[tabIndex].y + rects[tabIndex].height / 2;
2871 switch (tabPlacement)
2873 case SwingConstants.TOP:
2874 case SwingConstants.BOTTOM:
2877 case SwingConstants.RIGHT:
2878 case SwingConstants.LEFT:
2883 int index = tabForCoordinate(tabPane, x, y);
2885 tabPane.setSelectedIndex(index);
2888 // This method is called when you press up/down to cycle through tab runs.
2889 // it returns the distance (between the two runs' x/y position.
2890 // where one run is the current selected run and the other run is the run in the
2891 // direction of the scroll (dictated by the forward flag)
2892 // the offset is an absolute value of the difference
2895 * This method calculates the offset distance for use in
2896 * selectAdjacentRunTab. The offset returned will be a difference in the y
2897 * coordinate between the run in the desired direction and the current run
2898 * (for tabPlacement in TOP or BOTTOM). Use x coordinate for LEFT and
2901 * @param tabPlacement The JTabbedPane's tab placement.
2902 * @param tabCount The number of tabs.
2903 * @param tabIndex The starting index.
2904 * @param forward If forward, the run in the desired direction will be the
2907 * @return The offset between the two runs.
2909 protected int getTabRunOffset(int tabPlacement, int tabCount, int tabIndex,
2912 int currRun = getRunForTab(tabCount, tabIndex);
2914 int nextRun = (forward) ? getNextTabRun(currRun) : getPreviousTabRun(currRun);
2915 if (tabPlacement == SwingConstants.TOP
2916 || tabPlacement == SwingConstants.BOTTOM)
2917 offset = rects[lastTabInRun(tabCount, nextRun)].y
2918 - rects[lastTabInRun(tabCount, currRun)].y;
2920 offset = rects[lastTabInRun(tabCount, nextRun)].x
2921 - rects[lastTabInRun(tabCount, currRun)].x;
2926 * This method returns the previous tab index.
2928 * @param base The index to start from.
2930 * @return The previous tab index.
2932 protected int getPreviousTabIndex(int base)
2936 return tabPane.getTabCount() - 1;
2941 * This method returns the next tab index.
2943 * @param base The index to start from.
2945 * @return The next tab index.
2947 protected int getNextTabIndex(int base)
2950 if (base == tabPane.getTabCount())
2956 * This method returns the next tab index in the run. If the next index is
2957 * out of this run, it will return the starting tab index for the run.
2959 * @param tabCount The number of tabs.
2960 * @param base The index to start from.
2962 * @return The next tab index in the run.
2964 protected int getNextTabIndexInRun(int tabCount, int base)
2966 int index = getNextTabIndex(base);
2967 int run = getRunForTab(tabCount, base);
2968 if (index == lastTabInRun(tabCount, run) + 1)
2969 index = lastTabInRun(tabCount, getPreviousTabRun(run)) + 1;
2970 return getNextTabIndex(base);
2974 * This method returns the previous tab index in the run. If the previous
2975 * index is out of this run, it will return the last index for the run.
2977 * @param tabCount The number of tabs.
2978 * @param base The index to start from.
2980 * @return The previous tab index in the run.
2982 protected int getPreviousTabIndexInRun(int tabCount, int base)
2984 int index = getPreviousTabIndex(base);
2985 int run = getRunForTab(tabCount, base);
2986 if (index == lastTabInRun(tabCount, getPreviousTabRun(run)))
2987 index = lastTabInRun(tabCount, run);
2988 return getPreviousTabIndex(base);
2992 * This method returns the index of the previous run.
2994 * @param baseRun The run to start from.
2996 * @return The index of the previous run.
2998 protected int getPreviousTabRun(int baseRun)
3000 if (getTabRunCount(tabPane) == 1)
3003 int prevRun = --baseRun;
3005 prevRun = getTabRunCount(tabPane) - 1;
3010 * This method returns the index of the next run.
3012 * @param baseRun The run to start from.
3014 * @return The index of the next run.
3016 protected int getNextTabRun(int baseRun)
3018 if (getTabRunCount(tabPane) == 1)
3021 int nextRun = ++baseRun;
3022 if (nextRun == getTabRunCount(tabPane))
3028 * This method rotates the insets given a direction to rotate them in.
3029 * Target placement should be one of TOP, LEFT, BOTTOM, RIGHT. The rotated
3030 * insets will be stored in targetInsets. Passing in TOP as the direction
3031 * does nothing. Passing in LEFT switches top and left, right and bottom.
3032 * Passing in BOTTOM switches top and bottom. Passing in RIGHT switches top
3033 * for left, left for bottom, bottom for right, and right for top.
3035 * @param topInsets The reference insets.
3036 * @param targetInsets An Insets object to store the new insets.
3037 * @param targetPlacement The rotation direction.
3039 protected static void rotateInsets(Insets topInsets, Insets targetInsets,
3040 int targetPlacement)
3042 // Sun's version will happily throw an NPE if params are null,
3043 // so I won't check it either.
3044 switch (targetPlacement)
3046 case SwingConstants.TOP:
3047 targetInsets.top = topInsets.top;
3048 targetInsets.left = topInsets.left;
3049 targetInsets.right = topInsets.right;
3050 targetInsets.bottom = topInsets.bottom;
3052 case SwingConstants.LEFT:
3053 targetInsets.left = topInsets.top;
3054 targetInsets.top = topInsets.left;
3055 targetInsets.right = topInsets.bottom;
3056 targetInsets.bottom = topInsets.right;
3058 case SwingConstants.BOTTOM:
3059 targetInsets.top = topInsets.bottom;
3060 targetInsets.bottom = topInsets.top;
3061 targetInsets.left = topInsets.left;
3062 targetInsets.right = topInsets.right;
3064 case SwingConstants.RIGHT:
3065 targetInsets.top = topInsets.left;
3066 targetInsets.left = topInsets.bottom;
3067 targetInsets.bottom = topInsets.right;
3068 targetInsets.right = topInsets.top;