* javax/swing/JToggleButton.java (ToggleButtonModel):
[official-gcc.git] / libjava / javax / swing / plaf / basic / BasicTabbedPaneUI.java
blob563a8fef6daa4fe57ecaabb070adfc4f198e6983
1 /* BasicTabbedPaneUI.java
2 Copyright (C) 2002, 2004 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)
9 any later version.
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., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
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
24 combination.
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 of7 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. */
38 package javax.swing.plaf.basic;
40 import java.awt.Color;
41 import java.awt.Component;
42 import java.awt.Container;
43 import java.awt.Dimension;
44 import java.awt.Font;
45 import java.awt.FontMetrics;
46 import java.awt.Graphics;
47 import java.awt.Insets;
48 import java.awt.LayoutManager;
49 import java.awt.Point;
50 import java.awt.Polygon;
51 import java.awt.Rectangle;
52 import java.awt.event.ActionEvent;
53 import java.awt.event.ActionListener;
54 import java.awt.event.FocusAdapter;
55 import java.awt.event.FocusEvent;
56 import java.awt.event.FocusListener;
57 import java.awt.event.MouseAdapter;
58 import java.awt.event.MouseEvent;
59 import java.awt.event.MouseListener;
60 import java.beans.PropertyChangeEvent;
61 import java.beans.PropertyChangeListener;
62 import java.lang.Exception;
63 import javax.swing.Icon;
64 import javax.swing.JButton;
65 import javax.swing.JComponent;
66 import javax.swing.JPanel;
67 import javax.swing.JTabbedPane;
68 import javax.swing.JViewport;
69 import javax.swing.KeyStroke;
70 import javax.swing.SingleSelectionModel;
71 import javax.swing.SwingConstants;
72 import javax.swing.SwingUtilities;
73 import javax.swing.UIDefaults;
74 import javax.swing.UIManager;
75 import javax.swing.event.ChangeEvent;
76 import javax.swing.event.ChangeListener;
77 import javax.swing.plaf.ComponentUI;
78 import javax.swing.plaf.PanelUI;
79 import javax.swing.plaf.TabbedPaneUI;
80 import javax.swing.plaf.UIResource;
81 import javax.swing.plaf.ViewportUI;
82 import javax.swing.plaf.basic.BasicGraphicsUtils;
83 import javax.swing.text.View;
86 /**
87 * This is the Basic Look and Feel's UI delegate for JTabbedPane.
89 public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
91 /**
92 * A helper class that handles focus.
94 protected class FocusHandler extends FocusAdapter
96 /**
97 * This method is called when the component gains focus.
99 * @param e The FocusEvent.
101 public void focusGained(FocusEvent e)
103 // FIXME: Implement.
107 * This method is called when the component loses focus.
109 * @param e The FocusEvent.
111 public void focusLost(FocusEvent e)
113 // FIXME: Implement.
118 * A helper class for determining if mouse presses occur inside tabs and
119 * sets the index appropriately. In SCROLL_TAB_MODE, this class also
120 * handles the mouse clicks on the scrolling buttons.
122 protected class MouseHandler extends MouseAdapter
125 * This method is called when the mouse is pressed. The index cannot
126 * change to a tab that is not enabled.
128 * @param e The MouseEvent.
130 public void mousePressed(MouseEvent e)
132 int x = e.getX();
133 int y = e.getY();
134 int tabCount = tabPane.getTabCount();
136 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
138 if (e.getSource() == incrButton)
140 if (++currentScrollLocation >= tabCount)
141 currentScrollLocation = tabCount - 1;
142 if (currentScrollLocation == tabCount - 1)
143 incrButton.setEnabled(false);
144 else if (! decrButton.isEnabled())
145 decrButton.setEnabled(true);
146 tabPane.layout();
147 tabPane.repaint();
148 return;
150 else if (e.getSource() == decrButton)
152 if (--currentScrollLocation < 0)
153 currentScrollLocation = 0;
154 if (currentScrollLocation == 0)
155 decrButton.setEnabled(false);
156 else if (! incrButton.isEnabled())
157 incrButton.setEnabled(true);
158 tabPane.layout();
159 tabPane.repaint();
160 return;
164 int index = tabForCoordinate(tabPane, x, y);
166 // We need to check since there are areas where tabs cannot be
167 // e.g. in the inset area.
168 if (index != -1 && tabPane.isEnabledAt(index))
169 tabPane.setSelectedIndex(index);
174 * This class handles PropertyChangeEvents fired from the JTabbedPane.
176 protected class PropertyChangeHandler implements PropertyChangeListener
179 * This method is called whenever one of the properties of the JTabbedPane
180 * changes.
182 * @param e The PropertyChangeEvent.
184 public void propertyChange(PropertyChangeEvent e)
186 if (e.getPropertyName().equals(JTabbedPane.TAB_LAYOUT_POLICY_CHANGED_PROPERTY))
188 layoutManager = createLayoutManager();
190 tabPane.setLayout(layoutManager);
192 else if (e.getPropertyName().equals(JTabbedPane.TAB_PLACEMENT_CHANGED_PROPERTY)
193 && tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
195 incrButton = createIncreaseButton();
196 decrButton = createDecreaseButton();
198 tabPane.layout();
199 tabPane.repaint();
204 * A LayoutManager responsible for placing all the tabs and the visible
205 * component inside the JTabbedPane. This class is only used for
206 * WRAP_TAB_LAYOUT.
208 protected class TabbedPaneLayout implements LayoutManager
211 * This method is called when a component is added to the JTabbedPane.
213 * @param name The name of the component.
214 * @param comp The component being added.
216 public void addLayoutComponent(String name, Component comp)
218 // Do nothing.
222 * This method is called when the rectangles need to be calculated. It
223 * also fixes the size of the visible component.
225 public void calculateLayoutInfo()
227 calculateTabRects(tabPane.getTabPlacement(), tabPane.getTabCount());
229 if (tabPane.getSelectedIndex() != -1)
231 Component visible = getVisibleComponent();
232 Insets insets = getContentBorderInsets(tabPane.getTabPlacement());
233 visible.setBounds(contentRect.x + insets.left,
234 contentRect.y + insets.top,
235 contentRect.width - insets.left - insets.right,
236 contentRect.height - insets.top - insets.bottom);
241 * This method calculates the size of the the JTabbedPane.
243 * @param minimum Whether the JTabbedPane will try to be as small as it
244 * can.
246 * @return The desired size of the JTabbedPane.
248 protected Dimension calculateSize(boolean minimum)
250 int tabPlacement = tabPane.getTabPlacement();
251 int width = 0;
252 int height = 0;
254 int componentHeight = 0;
255 int componentWidth = 0;
256 Component c;
257 Dimension dims;
258 for (int i = 0; i < tabPane.getTabCount(); i++)
260 c = tabPane.getComponentAt(i);
261 if (c == null)
262 continue;
263 calcRect = c.getBounds();
264 dims = c.getPreferredSize();
265 if (dims != null)
267 componentHeight = Math.max(componentHeight, dims.height);
268 componentWidth = Math.max(componentWidth, dims.width);
271 Insets insets = tabPane.getInsets();
273 if (tabPlacement == SwingConstants.TOP
274 || tabPlacement == SwingConstants.BOTTOM)
276 width = calculateMaxTabWidth(tabPlacement) * tabPane.getTabCount();
277 calcRect = tabPane.getParent().getBounds();
278 width = Math.max(width, componentWidth);
280 int tabAreaHeight = preferredTabAreaHeight(tabPlacement, width);
281 height = tabAreaHeight + componentHeight;
283 else
285 height = calculateMaxTabHeight(tabPlacement) * tabPane.getTabCount();
286 calcRect = tabPane.getParent().getBounds();
287 height = Math.max(height, componentHeight);
289 int tabAreaWidth = preferredTabAreaWidth(tabPlacement, height);
290 width = tabAreaWidth + componentWidth;
293 return new Dimension(width, height);
296 // if tab placement is LEFT OR RIGHT, they share width.
297 // if tab placement is TOP OR BOTTOM, they share height
298 // PRE STEP: finds the default sizes for the labels as well as their locations.
299 // AND where they will be placed within the run system.
300 // 1. calls normalizeTab Runs.
301 // 2. calls rotate tab runs.
302 // 3. pads the tab runs.
303 // 4. pads the selected tab.
306 * This method is called to calculate the tab rectangles. This method
307 * will calculate the size and position of all rectangles (taking into
308 * account which ones should be in which tab run). It will pad them and
309 * normalize them as necessary.
311 * @param tabPlacement The JTabbedPane's tab placement.
312 * @param tabCount The run the current selection is in.
314 protected void calculateTabRects(int tabPlacement, int tabCount)
316 if (tabCount == 0)
317 return;
318 assureRectsCreated(tabCount);
320 FontMetrics fm = getFontMetrics();
321 SwingUtilities.calculateInnerArea(tabPane, calcRect);
322 Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
323 Insets insets = tabPane.getInsets();
324 int max = 0;
325 int runs = 0;
326 int start = getTabRunIndent(tabPlacement, 1);
327 if (tabPlacement == SwingConstants.TOP
328 || tabPlacement == SwingConstants.BOTTOM)
330 int maxHeight = calculateMaxTabHeight(tabPlacement);
332 calcRect.width -= tabAreaInsets.left + tabAreaInsets.right;
333 max = calcRect.width + tabAreaInsets.left + insets.left;
334 start += tabAreaInsets.left + insets.left;
335 int width = 0;
336 int runWidth = start;
338 for (int i = 0; i < tabCount; i++)
340 width = calculateTabWidth(tabPlacement, i, fm);
342 if (runWidth + width > max)
344 runWidth = tabAreaInsets.left + insets.left
345 + getTabRunIndent(tabPlacement, ++runs);
346 rects[i] = new Rectangle(runWidth,
347 insets.top + tabAreaInsets.top,
348 width, maxHeight);
349 runWidth += width;
350 if (runs > tabRuns.length - 1)
351 expandTabRunsArray();
352 tabRuns[runs] = i;
354 else
356 rects[i] = new Rectangle(runWidth,
357 insets.top + tabAreaInsets.top,
358 width, maxHeight);
359 runWidth += width;
362 runs++;
363 tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right;
364 tabAreaRect.height = runs * maxTabHeight
365 - (runs - 1) * tabRunOverlay
366 + tabAreaInsets.top + tabAreaInsets.bottom;
367 contentRect.width = tabAreaRect.width;
368 contentRect.height = tabPane.getHeight() - insets.top
369 - insets.bottom - tabAreaRect.height;
370 contentRect.x = insets.left;
371 tabAreaRect.x = insets.left;
372 if (tabPlacement == SwingConstants.BOTTOM)
374 contentRect.y = insets.top;
375 tabAreaRect.y = contentRect.y + contentRect.height;
377 else
379 tabAreaRect.y = insets.top;
380 contentRect.y = tabAreaRect.y + tabAreaRect.height;
383 else
385 int maxWidth = calculateMaxTabWidth(tabPlacement);
386 calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom;
387 max = calcRect.height + tabAreaInsets.top + insets.top;
389 int height = 0;
390 start += tabAreaInsets.top + insets.top;
391 int runHeight = start;
393 int fontHeight = fm.getHeight();
395 for (int i = 0; i < tabCount; i++)
397 height = calculateTabHeight(tabPlacement, i, fontHeight);
398 if (runHeight + height > max)
400 runHeight = tabAreaInsets.top + insets.top
401 + getTabRunIndent(tabPlacement, ++runs);
402 rects[i] = new Rectangle(insets.left + tabAreaInsets.left,
403 runHeight, maxWidth, height);
404 runHeight += height;
405 if (runs > tabRuns.length - 1)
406 expandTabRunsArray();
407 tabRuns[runs] = i;
409 else
411 rects[i] = new Rectangle(insets.left + tabAreaInsets.left,
412 runHeight, maxWidth, height);
413 runHeight += height;
416 runs++;
418 tabAreaRect.width = runs * maxTabWidth - (runs - 1) * tabRunOverlay
419 + tabAreaInsets.left + tabAreaInsets.right;
420 tabAreaRect.height = tabPane.getHeight() - insets.top
421 - insets.bottom;
422 tabAreaRect.y = insets.top;
423 contentRect.width = tabPane.getWidth() - insets.left - insets.right
424 - tabAreaRect.width;
425 contentRect.height = tabAreaRect.height;
426 contentRect.y = insets.top;
427 if (tabPlacement == SwingConstants.LEFT)
429 tabAreaRect.x = insets.left;
430 contentRect.x = tabAreaRect.x + tabAreaRect.width;
432 else
434 contentRect.x = insets.left;
435 tabAreaRect.x = contentRect.x + contentRect.width;
438 runCount = runs;
440 tabRuns[0] = 0;
441 normalizeTabRuns(tabPlacement, tabCount, start, max);
442 selectedRun = getRunForTab(tabCount, tabPane.getSelectedIndex());
443 if (shouldRotateTabRuns(tabPlacement))
444 rotateTabRuns(tabPlacement, selectedRun);
446 // Need to pad the runs and move them to the correct location.
447 for (int i = 0; i < runCount; i++)
449 int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1;
450 if (first == tabCount)
451 first = 0;
452 int last = lastTabInRun(tabCount, i);
453 if (shouldPadTabRun(tabPlacement, i))
454 padTabRun(tabPlacement, first, last, max);
456 // Done padding, now need to move it.
457 if (tabPlacement == SwingConstants.TOP && i > 0)
459 for (int j = first; j <= last; j++)
460 rects[j].y += (runCount - i) * maxTabHeight
461 + (runCount - i) * tabRunOverlay;
464 if (tabPlacement == SwingConstants.BOTTOM)
466 int height = tabPane.getBounds().height - insets.bottom
467 - tabAreaInsets.bottom;
468 int adjustment;
469 if (i == 0)
470 adjustment = height - maxTabHeight;
471 else
472 adjustment = height - (runCount - i + 1) * maxTabHeight
473 - (runCount - i) * tabRunOverlay;
475 for (int j = first; j <= last; j++)
476 rects[j].y = adjustment;
479 if (tabPlacement == SwingConstants.LEFT && i > 0)
481 for (int j = first; j <= last; j++)
482 rects[j].x += (runCount - i) * maxTabWidth
483 - (runCount - i) * tabRunOverlay;
486 if (tabPlacement == SwingConstants.RIGHT)
488 int width = tabPane.getBounds().width - insets.right
489 - tabAreaInsets.right;
490 int adjustment;
491 if (i == 0)
492 adjustment = width - maxTabWidth;
493 else
494 adjustment = width - (runCount - i + 1) * maxTabWidth
495 + (runCount - i) * tabRunOverlay;
497 for (int j = first; j <= last; j++)
498 rects[j].x = adjustment;
501 padSelectedTab(tabPlacement, tabPane.getSelectedIndex());
505 * This method is called when the JTabbedPane is laid out in
506 * WRAP_TAB_LAYOUT. It calls calculateLayoutInfo to find the positions
507 * of all its components.
509 * @param parent The Container to lay out.
511 public void layoutContainer(Container parent)
513 calculateLayoutInfo();
517 * This method returns the minimum layout size for the given container.
519 * @param parent The container that is being sized.
521 * @return The minimum size.
523 public Dimension minimumLayoutSize(Container parent)
525 return calculateSize(false);
528 // If there is more free space in an adjacent run AND the tab in the run can fit in the
529 // adjacent run, move it. This method is not perfect, it is merely an approximation.
530 // If you play around with Sun's JTabbedPane, you'll see that
531 // it does do some pretty strange things with regards to not moving tabs
532 // that should be moved.
533 // start = the x position where the tabs will begin
534 // max = the maximum position of where the tabs can go to (tabAreaInsets.left + the width of the tab area)
537 * This method tries to "even out" the number of tabs in each run based on
538 * their widths.
540 * @param tabPlacement The JTabbedPane's tab placement.
541 * @param tabCount The number of tabs.
542 * @param start The x position where the tabs will begin.
543 * @param max The maximum x position where the tab can run to.
545 protected void normalizeTabRuns(int tabPlacement, int tabCount, int start,
546 int max)
548 Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
549 if (tabPlacement == SwingUtilities.TOP
550 || tabPlacement == SwingUtilities.BOTTOM)
552 // We should only do this for runCount - 1, cause we can only shift that many times between
553 // runs.
554 for (int i = 1; i < runCount; i++)
556 Rectangle currRun = rects[lastTabInRun(tabCount, i)];
557 Rectangle nextRun = rects[lastTabInRun(tabCount, getNextTabRun(i))];
558 int spaceInCurr = currRun.x + currRun.width;
559 int spaceInNext = nextRun.x + nextRun.width;
561 int diffNow = spaceInCurr - spaceInNext;
562 int diffLater = (spaceInCurr - currRun.width)
563 - (spaceInNext + currRun.width);
564 while (Math.abs(diffLater) < Math.abs(diffNow)
565 && spaceInNext + currRun.width < max)
567 tabRuns[i]--;
568 spaceInNext += currRun.width;
569 spaceInCurr -= currRun.width;
570 currRun = rects[lastTabInRun(tabCount, i)];
571 diffNow = spaceInCurr - spaceInNext;
572 diffLater = (spaceInCurr - currRun.width)
573 - (spaceInNext + currRun.width);
576 // Fix the bounds.
577 int first = lastTabInRun(tabCount, i) + 1;
578 int last = lastTabInRun(tabCount, getNextTabRun(i));
579 int currX = tabAreaInsets.left;
580 for (int j = first; j <= last; j++)
582 rects[j].x = currX;
583 currX += rects[j].width;
587 else
589 for (int i = 1; i < runCount; i++)
591 Rectangle currRun = rects[lastTabInRun(tabCount, i)];
592 Rectangle nextRun = rects[lastTabInRun(tabCount, getNextTabRun(i))];
593 int spaceInCurr = currRun.y + currRun.height;
594 int spaceInNext = nextRun.y + nextRun.height;
596 int diffNow = spaceInCurr - spaceInNext;
597 int diffLater = (spaceInCurr - currRun.height)
598 - (spaceInNext + currRun.height);
599 while (Math.abs(diffLater) < Math.abs(diffNow)
600 && spaceInNext + currRun.height < max)
602 tabRuns[i]--;
603 spaceInNext += currRun.height;
604 spaceInCurr -= currRun.height;
605 currRun = rects[lastTabInRun(tabCount, i)];
606 diffNow = spaceInCurr - spaceInNext;
607 diffLater = (spaceInCurr - currRun.height)
608 - (spaceInNext + currRun.height);
611 int first = lastTabInRun(tabCount, i) + 1;
612 int last = lastTabInRun(tabCount, getNextTabRun(i));
613 int currY = tabAreaInsets.top;
614 for (int j = first; j <= last; j++)
616 rects[j].y = currY;
617 currY += rects[j].height;
624 * This method pads the tab at the selected index by the selected tab pad
625 * insets (so that it looks larger).
627 * @param tabPlacement The placement of the tabs.
628 * @param selectedIndex The selected index.
630 protected void padSelectedTab(int tabPlacement, int selectedIndex)
632 Insets insets = getSelectedTabPadInsets(tabPlacement);
633 rects[selectedIndex].x -= insets.left;
634 rects[selectedIndex].y -= insets.top;
635 rects[selectedIndex].width += insets.left + insets.right;
636 rects[selectedIndex].height += insets.top + insets.bottom;
639 // If the tabs on the run don't fill the width of the window, make it fit now.
640 // start = starting index of the run
641 // end = last index of the run
642 // max = tabAreaInsets.left + width (or equivalent)
643 // assert start <= end.
646 * This method makes each tab in the run larger so that the tabs expand
647 * to fill the runs width/height (depending on tabPlacement).
649 * @param tabPlacement The placement of the tabs.
650 * @param start The index of the first tab.
651 * @param end The last index of the tab
652 * @param max The amount of space in the run (width for TOP and BOTTOM
653 * tabPlacement).
655 protected void padTabRun(int tabPlacement, int start, int end, int max)
657 if (tabPlacement == SwingConstants.TOP
658 || tabPlacement == SwingConstants.BOTTOM)
660 int runWidth = rects[end].x + rects[end].width;
661 int spaceRemaining = max - runWidth;
662 int numTabs = end - start + 1;
664 // now divvy up the space.
665 int spaceAllocated = spaceRemaining / numTabs;
666 int currX = rects[start].x;
667 for (int i = start; i <= end; i++)
669 rects[i].x = currX;
670 rects[i].width += spaceAllocated;
671 currX += rects[i].width;
672 // This is used because since the spaceAllocated
673 // variable is an int, it rounds down. Sometimes,
674 // we don't fill an entire row, so we make it do
675 // so now.
676 if (i == end && rects[i].x + rects[i].width != max)
677 rects[i].width = max - rects[i].x;
680 else
682 int runHeight = rects[end].y + rects[end].height;
683 int spaceRemaining = max - runHeight;
684 int numTabs = end - start + 1;
686 int spaceAllocated = spaceRemaining / numTabs;
687 int currY = rects[start].y;
688 for (int i = start; i <= end; i++)
690 rects[i].y = currY;
691 rects[i].height += spaceAllocated;
692 currY += rects[i].height;
693 if (i == end && rects[i].y + rects[i].height != max)
694 rects[i].height = max - rects[i].y;
700 * This method returns the preferred layout size for the given container.
702 * @param parent The container to size.
704 * @return The preferred layout size.
706 public Dimension preferredLayoutSize(Container parent)
708 return calculateSize(false);
712 * This method returns the preferred tab height given a tabPlacement and
713 * width.
715 * @param tabPlacement The JTabbedPane's tab placement.
716 * @param width The expected width.
718 * @return The preferred tab area height.
720 protected int preferredTabAreaHeight(int tabPlacement, int width)
722 if (tabPane.getTabCount() == 0)
723 return calculateTabAreaHeight(tabPlacement, 0, 0);
725 int runs = 0;
726 int runWidth = 0;
727 int tabWidth = 0;
729 FontMetrics fm = getFontMetrics();
731 Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
732 Insets insets = tabPane.getInsets();
734 // Only interested in width, this is a messed up rectangle now.
735 width -= tabAreaInsets.left + tabAreaInsets.right + insets.left
736 + insets.right;
738 // The reason why we can't use runCount:
739 // This method is only called to calculate the size request
740 // for the tabbedPane. However, this size request is dependent on
741 // our desired width. We need to find out what the height would
742 // be IF we got our desired width.
743 for (int i = 0; i < tabPane.getTabCount(); i++)
745 tabWidth = calculateTabWidth(tabPlacement, i, fm);
746 if (runWidth + tabWidth > width)
748 runWidth = tabWidth;
749 runs++;
751 else
752 runWidth += tabWidth;
754 runs++;
756 int maxTabHeight = calculateMaxTabHeight(tabPlacement);
757 int tabAreaHeight = calculateTabAreaHeight(tabPlacement, runs,
758 maxTabHeight);
759 return tabAreaHeight;
763 * This method calculates the preferred tab area width given a tab
764 * placement and height.
766 * @param tabPlacement The JTabbedPane's tab placement.
767 * @param height The expected height.
769 * @return The preferred tab area width.
771 protected int preferredTabAreaWidth(int tabPlacement, int height)
773 if (tabPane.getTabCount() == 0)
774 return calculateTabAreaHeight(tabPlacement, 0, 0);
776 int runs = 0;
777 int runHeight = 0;
778 int tabHeight = 0;
780 FontMetrics fm = getFontMetrics();
782 Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
783 Insets insets = tabPane.getInsets();
785 height -= tabAreaInsets.top + tabAreaInsets.bottom + insets.top
786 + insets.bottom;
787 int fontHeight = fm.getHeight();
789 for (int i = 0; i < tabPane.getTabCount(); i++)
791 tabHeight = calculateTabHeight(tabPlacement, i, fontHeight);
792 if (runHeight + tabHeight > height)
794 runHeight = tabHeight;
795 runs++;
797 else
798 runHeight += tabHeight;
800 runs++;
802 int maxTabWidth = calculateMaxTabWidth(tabPlacement);
803 int tabAreaWidth = calculateTabAreaWidth(tabPlacement, runs, maxTabWidth);
804 return tabAreaWidth;
808 * This method rotates the places each run in the correct place the
809 * tabRuns array. See the comment for tabRuns for how the runs are placed
810 * in the array.
812 * @param tabPlacement The JTabbedPane's tab placement.
813 * @param selectedRun The run the current selection is in.
815 protected void rotateTabRuns(int tabPlacement, int selectedRun)
817 if (selectedRun == 1 || selectedRun == -1)
818 return;
819 int[] newTabRuns = new int[tabRuns.length];
820 int currentRun = selectedRun;
821 int i = 1;
824 newTabRuns[i] = tabRuns[currentRun];
825 currentRun = getNextTabRun(currentRun);
826 i++;
828 while (i < runCount);
829 if (runCount > 1)
830 newTabRuns[0] = tabRuns[currentRun];
832 tabRuns = newTabRuns;
833 BasicTabbedPaneUI.this.selectedRun = 1;
837 * This method is called when a component is removed from the
838 * JTabbedPane.
840 * @param comp The component removed.
842 public void removeLayoutComponent(Component comp)
844 // Do nothing.
849 * This class acts as the LayoutManager for the JTabbedPane in
850 * SCROLL_TAB_MODE.
852 private class TabbedPaneScrollLayout extends TabbedPaneLayout
855 * This method returns the preferred layout size for the given container.
857 * @param parent The container to calculate a size for.
859 * @return The preferred layout size.
861 public Dimension preferredLayoutSize(Container parent)
863 return super.calculateSize(true);
867 * This method returns the minimum layout size for the given container.
869 * @param parent The container to calculate a size for.
871 * @return The minimum layout size.
873 public Dimension minimumLayoutSize(Container parent)
875 return super.calculateSize(true);
879 * This method calculates the tab area height given a desired width.
881 * @param tabPlacement The JTabbedPane's tab placement.
882 * @param width The expected width.
884 * @return The tab area height given the width.
886 protected int preferredTabAreaHeight(int tabPlacement, int width)
888 if (tabPane.getTabCount() == 0)
889 return calculateTabAreaHeight(tabPlacement, 0, 0);
891 int runs = 1;
893 int maxTabHeight = calculateMaxTabHeight(tabPlacement);
894 int tabAreaHeight = calculateTabAreaHeight(tabPlacement, runs,
895 maxTabHeight);
896 return tabAreaHeight;
900 * This method calculates the tab area width given a desired height.
902 * @param tabPlacement The JTabbedPane's tab placement.
903 * @param height The expected height.
905 * @return The tab area width given the height.
907 protected int preferredTabAreaWidth(int tabPlacement, int height)
909 if (tabPane.getTabCount() == 0)
910 return calculateTabAreaHeight(tabPlacement, 0, 0);
912 int runs = 1;
914 int maxTabWidth = calculateMaxTabWidth(tabPlacement);
915 int tabAreaWidth = calculateTabAreaWidth(tabPlacement, runs, maxTabWidth);
916 return tabAreaWidth;
920 * This method is called to calculate the tab rectangles. This method
921 * will calculate the size and position of all rectangles (taking into
922 * account which ones should be in which tab run). It will pad them and
923 * normalize them as necessary.
925 * @param tabPlacement The JTabbedPane's tab placement.
926 * @param tabCount The number of tabs.
928 protected void calculateTabRects(int tabPlacement, int tabCount)
930 if (tabCount == 0)
931 return;
932 assureRectsCreated(tabCount);
934 FontMetrics fm = getFontMetrics();
935 SwingUtilities.calculateInnerArea(tabPane, calcRect);
936 Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
937 Insets insets = tabPane.getInsets();
938 int max = 0;
939 int runs = 1;
940 int start = 0;
941 int top = 0;
942 if (tabPlacement == SwingConstants.TOP
943 || tabPlacement == SwingConstants.BOTTOM)
945 int maxHeight = calculateMaxTabHeight(tabPlacement);
946 calcRect.width -= tabAreaInsets.left + tabAreaInsets.right;
947 max = calcRect.width + tabAreaInsets.left + insets.left;
948 start = tabAreaInsets.left + insets.left;
949 int width = 0;
950 int runWidth = start;
951 top = insets.top + tabAreaInsets.top;
952 for (int i = 0; i < tabCount; i++)
954 width = calculateTabWidth(tabPlacement, i, fm);
956 rects[i] = new Rectangle(runWidth, top, width, maxHeight);
957 runWidth += width;
959 tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right;
960 tabAreaRect.height = runs * maxTabHeight
961 - (runs - 1) * tabRunOverlay
962 + tabAreaInsets.top + tabAreaInsets.bottom;
963 contentRect.width = tabAreaRect.width;
964 contentRect.height = tabPane.getHeight() - insets.top
965 - insets.bottom - tabAreaRect.height;
966 contentRect.x = insets.left;
967 tabAreaRect.x = insets.left;
968 if (tabPlacement == SwingConstants.BOTTOM)
970 contentRect.y = insets.top;
971 tabAreaRect.y = contentRect.y + contentRect.height;
973 else
975 tabAreaRect.y = insets.top;
976 contentRect.y = tabAreaRect.y + tabAreaRect.height;
979 else
981 int maxWidth = calculateMaxTabWidth(tabPlacement);
983 calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom;
984 max = calcRect.height + tabAreaInsets.top;
985 int height = 0;
986 start = tabAreaInsets.top + insets.top;
987 int runHeight = start;
988 int fontHeight = fm.getHeight();
989 top = insets.left + tabAreaInsets.left;
990 for (int i = 0; i < tabCount; i++)
992 height = calculateTabHeight(tabPlacement, i, fontHeight);
993 rects[i] = new Rectangle(top, runHeight, maxWidth, height);
994 runHeight += height;
996 tabAreaRect.width = runs * maxTabWidth - (runs - 1) * tabRunOverlay
997 + tabAreaInsets.left + tabAreaInsets.right;
998 tabAreaRect.height = tabPane.getHeight() - insets.top
999 - insets.bottom;
1000 tabAreaRect.y = insets.top;
1001 contentRect.width = tabPane.getWidth() - insets.left - insets.right
1002 - tabAreaRect.width;
1003 contentRect.height = tabAreaRect.height;
1004 contentRect.y = insets.top;
1005 if (tabPlacement == SwingConstants.LEFT)
1007 tabAreaRect.x = insets.left;
1008 contentRect.x = tabAreaRect.x + tabAreaRect.width;
1010 else
1012 contentRect.x = insets.left;
1013 tabAreaRect.x = contentRect.x + contentRect.width;
1016 runCount = runs;
1018 padSelectedTab(tabPlacement, tabPane.getSelectedIndex());
1022 * This method is called when the JTabbedPane is laid out in
1023 * SCROLL_TAB_LAYOUT. It finds the position for all components in the
1024 * JTabbedPane.
1026 * @param pane The JTabbedPane to be laid out.
1028 public void layoutContainer(Container pane)
1030 super.layoutContainer(pane);
1031 int tabCount = tabPane.getTabCount();
1032 if (tabCount == 0)
1033 return;
1034 int tabPlacement = tabPane.getTabPlacement();
1035 incrButton.hide();
1036 decrButton.hide();
1037 if (tabPlacement == SwingConstants.TOP
1038 || tabPlacement == SwingConstants.BOTTOM)
1040 if (tabAreaRect.x + tabAreaRect.width < rects[tabCount - 1].x
1041 + rects[tabCount - 1].width)
1043 Dimension incrDims = incrButton.getPreferredSize();
1044 Dimension decrDims = decrButton.getPreferredSize();
1046 decrButton.setBounds(tabAreaRect.x + tabAreaRect.width
1047 - incrDims.width - decrDims.width,
1048 tabAreaRect.y, decrDims.width,
1049 tabAreaRect.height);
1050 incrButton.setBounds(tabAreaRect.x + tabAreaRect.width
1051 - incrDims.width, tabAreaRect.y,
1052 decrDims.width, tabAreaRect.height);
1054 tabAreaRect.width -= decrDims.width + incrDims.width;
1055 incrButton.show();
1056 decrButton.show();
1060 if (tabPlacement == SwingConstants.LEFT
1061 || tabPlacement == SwingConstants.RIGHT)
1063 if (tabAreaRect.y + tabAreaRect.height < rects[tabCount - 1].y
1064 + rects[tabCount - 1].height)
1066 Dimension incrDims = incrButton.getPreferredSize();
1067 Dimension decrDims = decrButton.getPreferredSize();
1069 decrButton.setBounds(tabAreaRect.x,
1070 tabAreaRect.y + tabAreaRect.height
1071 - incrDims.height - decrDims.height,
1072 tabAreaRect.width, decrDims.height);
1073 incrButton.setBounds(tabAreaRect.x,
1074 tabAreaRect.y + tabAreaRect.height
1075 - incrDims.height, tabAreaRect.width,
1076 incrDims.height);
1078 tabAreaRect.height -= decrDims.height + incrDims.height;
1079 incrButton.show();
1080 decrButton.show();
1083 viewport.setBounds(tabAreaRect.x, tabAreaRect.y, tabAreaRect.width,
1084 tabAreaRect.height);
1085 int tabC = tabPane.getTabCount() - 1;
1086 if (tabCount > 0)
1088 int w = Math.max(rects[tabC].width + rects[tabC].x, tabAreaRect.width);
1089 int h = Math.max(rects[tabC].height, tabAreaRect.height);
1090 Point p = findPointForIndex(currentScrollLocation);
1092 // we want to cover that entire space so that borders that run under
1093 // the tab area don't show up when we move the viewport around.
1094 panel.setBounds(0, 0, w + p.x, h + p.y);
1096 viewport.setViewPosition(findPointForIndex(currentScrollLocation));
1101 * This class handles ChangeEvents from the JTabbedPane.
1103 protected class TabSelectionHandler implements ChangeListener
1106 * This method is called whenever a ChangeEvent is fired from the
1107 * JTabbedPane.
1109 * @param e The ChangeEvent fired.
1111 public void stateChanged(ChangeEvent e)
1113 selectedRun = getRunForTab(tabPane.getTabCount(),
1114 tabPane.getSelectedIndex());
1115 tabPane.layout();
1116 tabPane.repaint();
1121 * This helper class is a JPanel that fits inside the ScrollViewport. This
1122 * panel's sole job is to paint the tab rectangles inside the viewport so
1123 * that it's clipped correctly.
1125 private class ScrollingPanel extends JPanel
1128 * This is a private UI class for our panel.
1130 private class ScrollingPanelUI extends BasicPanelUI
1133 * This method overrides the default paint method. It paints the tab
1134 * rectangles for the JTabbedPane in the panel.
1136 * @param g The Graphics object to paint with.
1137 * @param c The JComponent to paint.
1139 public void paint(Graphics g, JComponent c)
1141 paintTabArea(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex());
1146 * This method overrides the updateUI method. It makes the default UI for
1147 * this ScrollingPanel to be a ScrollingPanelUI.
1149 public void updateUI()
1151 setUI((PanelUI) new ScrollingPanelUI());
1156 * This is a helper class that paints the panel that paints tabs. This
1157 * custom JViewport is used so that the tabs painted in the panel will be
1158 * clipped. This class implements UIResource so tabs are not added when
1159 * this objects of this class are added to the JTabbedPane.
1161 private class ScrollingViewport extends JViewport implements UIResource
1164 * This method is temporary until the viewport layouts are implemented.
1165 * Need it because the flow layout it currently moves our panel around.
1167 * @param g The graphics object to paint with.
1169 public void paint(Graphics g)
1171 // FIXME: Remove this as well.
1172 int tabC = tabPane.getTabCount() - 1;
1173 if (tabC + 1 > 0)
1175 int w = Math.max(rects[tabC].width + rects[tabC].x, tabAreaRect.width);
1176 int h = Math.max(rects[tabC].height, tabAreaRect.height);
1177 Point p = findPointForIndex(currentScrollLocation);
1179 // we want to cover that entire space so that borders that run under
1180 // the tab area don't show up when we move the viewport around.
1181 panel.setBounds(0, 0, w + p.x, h + p.y);
1184 // FIXME: Remove when ViewportLayout is done.
1185 setViewPosition(findPointForIndex(currentScrollLocation));
1186 super.paint(g);
1191 * This is a helper class that implements UIResource so it is not added as a
1192 * tab when an object of this class is added to the JTabbedPane.
1194 private static class ScrollingButton extends BasicArrowButton
1195 implements UIResource
1198 * Creates a ScrollingButton given the direction.
1200 * @param dir The direction to point in.
1202 public ScrollingButton(int dir)
1204 super(dir);
1208 /** The button that increments the current scroll location. */
1209 private transient ScrollingButton incrButton;
1211 /** The button that decrements the current scroll location. */
1212 private transient ScrollingButton decrButton;
1214 /** The viewport used to display the tabs. */
1215 private transient ScrollingViewport viewport;
1217 /** The panel inside the viewport that paints the tabs. */
1218 private transient ScrollingPanel panel;
1220 /** The starting visible tab in the run in SCROLL_TAB_MODE. */
1221 private transient int currentScrollLocation;
1223 /** A reusable rectangle. */
1224 protected Rectangle calcRect;
1226 /** An array of Rectangles keeping track of the tabs' area and position. */
1227 protected Rectangle[] rects;
1229 /** The insets around the content area. */
1230 protected Insets contentBorderInsets;
1232 /** The extra insets around the selected tab. */
1233 protected Insets selectedTabPadInsets;
1235 /** The insets around the tab area. */
1236 protected Insets tabAreaInsets;
1238 /** The insets around each and every tab. */
1239 protected Insets tabInsets;
1242 * The outer bottom and right edge color for both the tab and content
1243 * border.
1245 protected Color darkShadow;
1247 /** The color of the focus outline on the selected tab. */
1248 protected Color focus;
1250 /** FIXME: find a use for this. */
1251 protected Color highlight;
1253 /** The top and left edge color for both the tab and content border. */
1254 protected Color lightHighlight;
1256 /** The inner bottom and right edge color for the tab and content border. */
1257 protected Color shadow;
1259 /** The maximum tab height. */
1260 protected int maxTabHeight;
1262 /** The maximum tab width. */
1263 protected int maxTabWidth;
1265 /** The number of runs in the JTabbedPane. */
1266 protected int runCount;
1268 /** The index of the run that the selected index is in. */
1269 protected int selectedRun;
1271 /** The amount of space each run overlaps the previous by. */
1272 protected int tabRunOverlay;
1274 /** The gap between text and label */
1275 protected int textIconGap;
1277 // Keeps track of tab runs.
1278 // The organization of this array is as follows (lots of experimentation to
1279 // figure this out)
1280 // index 0 = furthest away from the component area (aka outer run)
1281 // index 1 = closest to component area (aka selected run)
1282 // index > 1 = listed in order leading from selected run to outer run.
1283 // each int in the array is the tab index + 1 (counting starts at 1)
1284 // for the last tab in the run. (same as the rects array)
1286 /** This array keeps track of which tabs are in which run. See above. */
1287 protected int[] tabRuns;
1289 /** Deprecated. This is the keystroke for moving down. */
1290 protected KeyStroke downKey;
1292 /** Deprecated. This is the keystroke for moving left. */
1293 protected KeyStroke leftKey;
1295 /** Deprecated. This is the keystroke for moving right. */
1296 protected KeyStroke rightKey;
1298 /** Deprecated. This is the keystroke for moving up. */
1299 protected KeyStroke upKey;
1301 /** The listener that listens for focus events. */
1302 protected FocusListener focusListener;
1304 /** The listener that listens for mouse events. */
1305 protected MouseListener mouseListener;
1307 /** The listener that listens for property change events. */
1308 protected PropertyChangeListener propertyChangeListener;
1310 /** The listener that listens for change events. */
1311 protected ChangeListener tabChangeListener;
1313 /** The tab pane that this UI paints. */
1314 protected JTabbedPane tabPane;
1316 /** The current layout manager for the tabPane. */
1317 private transient LayoutManager layoutManager;
1319 /** The rectangle that describes the tab area's position and size. */
1320 private transient Rectangle tabAreaRect;
1322 /** The rectangle that describes the content area's position and size. */
1323 private transient Rectangle contentRect;
1326 * Creates a new BasicTabbedPaneUI object.
1328 public BasicTabbedPaneUI()
1330 super();
1334 * This method creates a ScrollingButton that points in the appropriate
1335 * direction for an increasing button.
1337 * @return The increase ScrollingButton.
1339 private ScrollingButton createIncreaseButton()
1341 if (incrButton == null)
1342 incrButton = new ScrollingButton(SwingConstants.NORTH);
1343 if (tabPane.getTabPlacement() == SwingConstants.TOP
1344 || tabPane.getTabPlacement() == SwingConstants.BOTTOM)
1345 incrButton.setDirection(SwingConstants.EAST);
1346 else
1347 incrButton.setDirection(SwingConstants.SOUTH);
1348 return incrButton;
1352 * This method creates a ScrollingButton that points in the appropriate
1353 * direction for a decreasing button.
1355 * @return The decrease ScrollingButton.
1357 private ScrollingButton createDecreaseButton()
1359 if (decrButton == null)
1360 decrButton = new ScrollingButton(SwingConstants.SOUTH);
1361 if (tabPane.getTabPlacement() == SwingConstants.TOP
1362 || tabPane.getTabPlacement() == SwingConstants.BOTTOM)
1363 decrButton.setDirection(SwingConstants.WEST);
1364 else
1365 decrButton.setDirection(SwingConstants.NORTH);
1366 return decrButton;
1370 * This method finds the point to set the view position at given the index
1371 * of a tab. The tab will be the first visible tab in the run.
1373 * @param index The index of the first visible tab.
1375 * @return The position of the first visible tab.
1377 private Point findPointForIndex(int index)
1379 int tabPlacement = tabPane.getTabPlacement();
1380 int selectedIndex = tabPane.getSelectedIndex();
1381 Insets insets = getSelectedTabPadInsets(tabPlacement);
1382 int w = 0;
1383 int h = 0;
1385 if (tabPlacement == TOP || tabPlacement == BOTTOM)
1387 if (index > 0)
1389 w += rects[index - 1].x + rects[index - 1].width;
1390 if (index > selectedIndex)
1391 w -= insets.left + insets.right;
1395 else
1397 if (index > 0)
1399 h += rects[index - 1].y + rects[index - 1].height;
1400 if (index > selectedIndex)
1401 h -= insets.top + insets.bottom;
1405 Point p = new Point(w, h);
1406 return p;
1410 * This method creates a new BasicTabbedPaneUI.
1412 * @param c The JComponent to create a UI for.
1414 * @return A new BasicTabbedPaneUI.
1416 public static ComponentUI createUI(JComponent c)
1418 return new BasicTabbedPaneUI();
1422 * This method installs the UI for the given JComponent.
1424 * @param c The JComponent to install the UI for.
1426 public void installUI(JComponent c)
1428 super.installUI(c);
1429 if (c instanceof JTabbedPane)
1431 tabPane = (JTabbedPane) c;
1433 installComponents();
1434 installDefaults();
1435 installListeners();
1436 installKeyboardActions();
1438 layoutManager = createLayoutManager();
1439 tabPane.setLayout(layoutManager);
1440 tabPane.layout();
1445 * This method uninstalls the UI for the given JComponent.
1447 * @param c The JComponent to uninstall the UI for.
1449 public void uninstallUI(JComponent c)
1451 layoutManager = null;
1453 uninstallKeyboardActions();
1454 uninstallListeners();
1455 uninstallDefaults();
1456 uninstallComponents();
1458 tabPane = null;
1462 * This method creates the appropriate layout manager for the JTabbedPane's
1463 * current tab layout policy. If the tab layout policy is
1464 * SCROLL_TAB_LAYOUT, then all the associated components that need to be
1465 * created will be done so now.
1467 * @return A layout manager given the tab layout policy.
1469 public LayoutManager createLayoutManager()
1471 if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT)
1472 return new TabbedPaneLayout();
1473 else
1475 incrButton = createIncreaseButton();
1476 decrButton = createDecreaseButton();
1477 viewport = new ScrollingViewport();
1478 panel = new ScrollingPanel();
1479 viewport.setView(panel);
1480 tabPane.add(incrButton);
1481 tabPane.add(decrButton);
1482 tabPane.add(viewport);
1483 currentScrollLocation = 0;
1484 decrButton.setEnabled(false);
1485 panel.addMouseListener(mouseListener);
1486 incrButton.addMouseListener(mouseListener);
1487 decrButton.addMouseListener(mouseListener);
1488 viewport.setBackground(Color.LIGHT_GRAY);
1490 return new TabbedPaneScrollLayout();
1495 * This method installs components for this JTabbedPane.
1497 protected void installComponents()
1499 // Nothing to be done.
1503 * This method uninstalls components for this JTabbedPane.
1505 protected void uninstallComponents()
1507 // Nothing to be done.
1511 * This method installs defaults for the Look and Feel.
1513 protected void installDefaults()
1515 UIDefaults defaults = UIManager.getLookAndFeelDefaults();
1517 tabPane.setFont(defaults.getFont("TabbedPane.font"));
1518 tabPane.setForeground(defaults.getColor("TabbedPane.foreground"));
1519 tabPane.setBackground(defaults.getColor("TabbedPane.background"));
1521 highlight = defaults.getColor("TabbedPane.highlight");
1522 lightHighlight = defaults.getColor("TabbedPane.lightHighlight");
1524 shadow = defaults.getColor("TabbedPane.shadow");
1525 darkShadow = defaults.getColor("TabbedPane.darkShadow");
1527 focus = defaults.getColor("TabbedPane.focus");
1529 textIconGap = defaults.getInt("TabbedPane.textIconGap");
1530 tabRunOverlay = defaults.getInt("TabbedPane.tabRunOverlay");
1532 tabInsets = defaults.getInsets("TabbedPane.tabbedPaneTabInsets");
1533 selectedTabPadInsets = defaults.getInsets("TabbedPane.tabbedPaneTabPadInsets");
1534 tabAreaInsets = defaults.getInsets("TabbedPane.tabbedPaneTabAreaInsets");
1535 contentBorderInsets = defaults.getInsets("TabbedPane.tabbedPaneContentBorderInsets");
1537 calcRect = new Rectangle();
1538 tabRuns = new int[10];
1539 tabAreaRect = new Rectangle();
1540 contentRect = new Rectangle();
1544 * This method uninstalls defaults for the Look and Feel.
1546 protected void uninstallDefaults()
1548 calcRect = null;
1549 tabAreaRect = null;
1550 contentRect = null;
1551 tabRuns = null;
1553 contentBorderInsets = null;
1554 tabAreaInsets = null;
1555 selectedTabPadInsets = null;
1556 tabInsets = null;
1558 focus = null;
1559 darkShadow = null;
1560 shadow = null;
1561 lightHighlight = null;
1562 highlight = null;
1564 tabPane.setBackground(null);
1565 tabPane.setForeground(null);
1566 tabPane.setFont(null);
1570 * This method creates and installs the listeners for this UI.
1572 protected void installListeners()
1574 mouseListener = createMouseListener();
1575 tabChangeListener = createChangeListener();
1576 propertyChangeListener = createPropertyChangeListener();
1577 focusListener = createFocusListener();
1579 tabPane.addMouseListener(mouseListener);
1580 tabPane.addChangeListener(tabChangeListener);
1581 tabPane.addPropertyChangeListener(propertyChangeListener);
1582 tabPane.addFocusListener(focusListener);
1586 * This method removes and nulls the listeners for this UI.
1588 protected void uninstallListeners()
1590 tabPane.removeFocusListener(focusListener);
1591 tabPane.removePropertyChangeListener(propertyChangeListener);
1592 tabPane.removeChangeListener(tabChangeListener);
1593 tabPane.removeMouseListener(mouseListener);
1595 focusListener = null;
1596 propertyChangeListener = null;
1597 tabChangeListener = null;
1598 mouseListener = null;
1602 * This method creates a new MouseListener.
1604 * @return A new MouseListener.
1606 protected MouseListener createMouseListener()
1608 return new MouseHandler();
1612 * This method creates a new FocusListener.
1614 * @return A new FocusListener.
1616 protected FocusListener createFocusListener()
1618 return new FocusHandler();
1622 * This method creates a new ChangeListener.
1624 * @return A new ChangeListener.
1626 protected ChangeListener createChangeListener()
1628 return new TabSelectionHandler();
1632 * This method creates a new PropertyChangeListener.
1634 * @return A new PropertyChangeListener.
1636 protected PropertyChangeListener createPropertyChangeListener()
1638 return new PropertyChangeHandler();
1642 * This method installs keyboard actions for the JTabbedPane.
1644 protected void installKeyboardActions()
1646 // FIXME: Implement.
1650 * This method uninstalls keyboard actions for the JTabbedPane.
1652 protected void uninstallKeyboardActions()
1654 // FIXME: Implement.
1658 * This method returns the preferred size of the JTabbedPane.
1660 * @param c The JComponent to find a size for.
1662 * @return The preferred size.
1664 public Dimension getPreferredSize(JComponent c)
1666 return layoutManager.preferredLayoutSize(tabPane);
1670 * This method returns the minimum size of the JTabbedPane.
1672 * @param c The JComponent to find a size for.
1674 * @return The minimum size.
1676 public Dimension getMinimumSize(JComponent c)
1678 return layoutManager.minimumLayoutSize(tabPane);
1682 * This method returns the maximum size of the JTabbedPane.
1684 * @param c The JComponent to find a size for.
1686 * @return The maximum size.
1688 public Dimension getMaximumSize(JComponent c)
1690 return getPreferredSize(c);
1694 * This method paints the JTabbedPane.
1696 * @param g The Graphics object to paint with.
1697 * @param c The JComponent to paint.
1699 public void paint(Graphics g, JComponent c)
1701 if (tabPane.getTabCount() == 0)
1702 return;
1703 if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT)
1704 paintTabArea(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex());
1705 paintContentBorder(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex());
1709 * This method paints the tab area. This includes painting the rectangles
1710 * that make up the tabs.
1712 * @param g The Graphics object to paint with.
1713 * @param tabPlacement The JTabbedPane's tab placement.
1714 * @param selectedIndex The selected index.
1716 protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex)
1718 Rectangle ir = new Rectangle();
1719 Rectangle tr = new Rectangle();
1721 // Please note: the ordering of the painting is important.
1722 // we WANT to paint the outermost run first and then work our way in.
1723 int tabCount = tabPane.getTabCount();
1724 int currRun = 1;
1725 if (tabCount < 1)
1726 return;
1728 if (runCount > 1)
1729 currRun = 0;
1730 for (int i = 0; i < runCount; i++)
1732 int first = lastTabInRun(tabCount, getPreviousTabRun(currRun)) + 1;
1733 if (first == tabCount)
1734 first = 0;
1735 int last = lastTabInRun(tabCount, currRun);
1736 for (int j = first; j <= last; j++)
1738 if (j != selectedIndex)
1739 paintTab(g, tabPlacement, rects, j, ir, tr);
1741 currRun = getNextTabRun(currRun);
1743 paintTab(g, tabPlacement, rects, selectedIndex, ir, tr);
1747 * This method paints an individual tab.
1749 * @param g The Graphics object to paint with.
1750 * @param tabPlacement The JTabbedPane's tab placement.
1751 * @param rects The array of rectangles that keep the size and position of
1752 * the tabs.
1753 * @param tabIndex The tab index to paint.
1754 * @param iconRect The rectangle to use for the icon.
1755 * @param textRect The rectangle to use for the text.
1757 protected void paintTab(Graphics g, int tabPlacement, Rectangle[] rects,
1758 int tabIndex, Rectangle iconRect, Rectangle textRect)
1760 FontMetrics fm = getFontMetrics();
1761 Icon icon = getIconForTab(tabIndex);
1762 String title = tabPane.getTitleAt(tabIndex);
1763 boolean isSelected = tabIndex == tabPane.getSelectedIndex();
1764 calcRect = getTabBounds(tabPane, tabIndex);
1766 int x = calcRect.x;
1767 int y = calcRect.y;
1768 int w = calcRect.width;
1769 int h = calcRect.height;
1770 if (getRunForTab(tabPane.getTabCount(), tabIndex) == 1)
1772 Insets insets = getTabAreaInsets(tabPlacement);
1773 switch (tabPlacement)
1775 case TOP:
1776 h += insets.bottom;
1777 break;
1778 case LEFT:
1779 w += insets.right;
1780 break;
1781 case BOTTOM:
1782 y -= insets.top;
1783 h += insets.top;
1784 break;
1785 case RIGHT:
1786 x -= insets.left;
1787 w += insets.left;
1788 break;
1792 layoutLabel(tabPlacement, fm, tabIndex, title, icon, calcRect, iconRect,
1793 textRect, isSelected);
1794 paintTabBackground(g, tabPlacement, tabIndex, x, y, w, h, isSelected);
1795 paintTabBorder(g, tabPlacement, tabIndex, x, y, w, h, isSelected);
1797 // FIXME: Paint little folding corner and jagged edge clipped tab.
1798 if (icon != null)
1799 paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected);
1800 if (title == null || ! title.equals(""))
1801 paintText(g, tabPlacement, tabPane.getFont(), fm, tabIndex, title,
1802 textRect, isSelected);
1806 * This method lays out the tab and finds the location to paint the icon
1807 * and text.
1809 * @param tabPlacement The JTabbedPane's tab placement.
1810 * @param metrics The font metrics for the font to paint with.
1811 * @param tabIndex The tab index to paint.
1812 * @param title The string painted.
1813 * @param icon The icon painted.
1814 * @param tabRect The tab bounds.
1815 * @param iconRect The calculated icon bounds.
1816 * @param textRect The calculated text bounds.
1817 * @param isSelected Whether this tab is selected.
1819 protected void layoutLabel(int tabPlacement, FontMetrics metrics,
1820 int tabIndex, String title, Icon icon,
1821 Rectangle tabRect, Rectangle iconRect,
1822 Rectangle textRect, boolean isSelected)
1824 SwingUtilities.layoutCompoundLabel(metrics, title, icon,
1825 SwingConstants.CENTER,
1826 SwingConstants.CENTER,
1827 SwingConstants.CENTER,
1828 SwingConstants.CENTER, tabRect,
1829 iconRect, textRect, textIconGap);
1831 int shiftX = getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
1832 int shiftY = getTabLabelShiftY(tabPlacement, tabIndex, isSelected);
1834 iconRect.x += shiftX;
1835 iconRect.y += shiftY;
1837 textRect.x += shiftX;
1838 textRect.y += shiftY;
1842 * This method paints the icon.
1844 * @param g The Graphics object to paint.
1845 * @param tabPlacement The JTabbedPane's tab placement.
1846 * @param tabIndex The tab index to paint.
1847 * @param icon The icon to paint.
1848 * @param iconRect The bounds of the icon.
1849 * @param isSelected Whether this tab is selected.
1851 protected void paintIcon(Graphics g, int tabPlacement, int tabIndex,
1852 Icon icon, Rectangle iconRect, boolean isSelected)
1854 icon.paintIcon(tabPane, g, iconRect.x, iconRect.y);
1858 * This method paints the text for the given tab.
1860 * @param g The Graphics object to paint with.
1861 * @param tabPlacement The JTabbedPane's tab placement.
1862 * @param font The font to paint with.
1863 * @param metrics The fontmetrics of the given font.
1864 * @param tabIndex The tab index.
1865 * @param title The string to paint.
1866 * @param textRect The bounds of the string.
1867 * @param isSelected Whether this tab is selected.
1869 protected void paintText(Graphics g, int tabPlacement, Font font,
1870 FontMetrics metrics, int tabIndex, String title,
1871 Rectangle textRect, boolean isSelected)
1873 View textView = getTextViewForTab(tabIndex);
1874 if (textView != null)
1876 textView.paint(g, textRect);
1877 return;
1880 Color fg = tabPane.getForegroundAt(tabIndex);
1881 if (fg == null)
1882 fg = tabPane.getForeground();
1883 Color bg = tabPane.getBackgroundAt(tabIndex);
1884 if (bg == null)
1885 bg = tabPane.getBackground();
1887 Color saved_color = g.getColor();
1888 Font f = g.getFont();
1889 g.setFont(font);
1891 if (tabPane.isEnabledAt(tabIndex))
1893 g.setColor(fg);
1895 int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
1897 if (mnemIndex != -1)
1898 BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
1899 textRect.x,
1900 textRect.y
1901 + metrics.getAscent());
1902 else
1903 g.drawString(title, textRect.x, textRect.y + metrics.getAscent());
1905 else
1907 g.setColor(bg.brighter());
1909 int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
1911 if (mnemIndex != -1)
1912 BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
1913 textRect.x, textRect.y);
1914 else
1915 g.drawString(title, textRect.x, textRect.y);
1917 g.setColor(bg.darker());
1918 if (mnemIndex != -1)
1919 BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
1920 textRect.x + 1,
1921 textRect.y + 1);
1922 else
1923 g.drawString(title, textRect.x + 1, textRect.y + 1);
1926 g.setColor(saved_color);
1927 g.setFont(f);
1931 * This method returns how much the label for the tab should shift in the X
1932 * direction.
1934 * @param tabPlacement The JTabbedPane's tab placement.
1935 * @param tabIndex The tab index being painted.
1936 * @param isSelected Whether this tab is selected.
1938 * @return The amount the label should shift by in the X direction.
1940 protected int getTabLabelShiftX(int tabPlacement, int tabIndex,
1941 boolean isSelected)
1943 // No reason to shift.
1944 return 0;
1948 * This method returns how much the label for the tab should shift in the Y
1949 * direction.
1951 * @param tabPlacement The JTabbedPane's tab placement.
1952 * @param tabIndex The tab index being painted.
1953 * @param isSelected Whether this tab is selected.
1955 * @return The amount the label should shift by in the Y direction.
1957 protected int getTabLabelShiftY(int tabPlacement, int tabIndex,
1958 boolean isSelected)
1960 // No reason to shift.
1961 return 0;
1965 * This method paints the focus rectangle around the selected tab.
1967 * @param g The Graphics object to paint with.
1968 * @param tabPlacement The JTabbedPane's tab placement.
1969 * @param rects The array of rectangles keeping track of size and position.
1970 * @param tabIndex The tab index.
1971 * @param iconRect The icon bounds.
1972 * @param textRect The text bounds.
1973 * @param isSelected Whether this tab is selected.
1975 protected void paintFocusIndicator(Graphics g, int tabPlacement,
1976 Rectangle[] rects, int tabIndex,
1977 Rectangle iconRect, Rectangle textRect,
1978 boolean isSelected)
1980 Color saved = g.getColor();
1981 calcRect = iconRect.union(textRect);
1983 g.setColor(focus);
1985 g.drawRect(calcRect.x, calcRect.y, calcRect.width, calcRect.height);
1987 g.setColor(saved);
1991 * This method paints the border for an individual tab.
1993 * @param g The Graphics object to paint with.
1994 * @param tabPlacement The JTabbedPane's tab placement.
1995 * @param tabIndex The tab index.
1996 * @param x The x position of the tab.
1997 * @param y The y position of the tab.
1998 * @param w The width of the tab.
1999 * @param h The height of the tab.
2000 * @param isSelected Whether the tab is selected.
2002 protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex,
2003 int x, int y, int w, int h, boolean isSelected)
2005 Color saved = g.getColor();
2007 if (! isSelected || tabPlacement != SwingConstants.TOP)
2009 g.setColor(shadow);
2010 g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1);
2011 g.setColor(darkShadow);
2012 g.drawLine(x, y + h, x + w, y + h);
2015 if (! isSelected || tabPlacement != SwingConstants.LEFT)
2017 g.setColor(darkShadow);
2018 g.drawLine(x + w, y, x + w, y + h);
2019 g.setColor(shadow);
2020 g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1);
2023 if (! isSelected || tabPlacement != SwingConstants.RIGHT)
2025 g.setColor(lightHighlight);
2026 g.drawLine(x, y, x, y + h);
2029 if (! isSelected || tabPlacement != SwingConstants.BOTTOM)
2031 g.setColor(lightHighlight);
2032 g.drawLine(x, y, x + w, y);
2035 g.setColor(saved);
2039 * This method paints the background for an individual tab.
2041 * @param g The Graphics object to paint with.
2042 * @param tabPlacement The JTabbedPane's tab placement.
2043 * @param tabIndex The tab index.
2044 * @param x The x position of the tab.
2045 * @param y The y position of the tab.
2046 * @param w The width of the tab.
2047 * @param h The height of the tab.
2048 * @param isSelected Whether the tab is selected.
2050 protected void paintTabBackground(Graphics g, int tabPlacement,
2051 int tabIndex, int x, int y, int w, int h,
2052 boolean isSelected)
2054 Color saved = g.getColor();
2055 if (isSelected)
2056 g.setColor(Color.LIGHT_GRAY);
2057 else
2059 Color bg = tabPane.getBackgroundAt(tabIndex);
2060 if (bg == null)
2061 bg = tabPane.getBackground();
2062 g.setColor(bg);
2065 g.fillRect(x, y, w, h);
2067 g.setColor(saved);
2071 * This method paints the border around the content area.
2073 * @param g The Graphics object to paint with.
2074 * @param tabPlacement The JTabbedPane's tab placement.
2075 * @param selectedIndex The index of the selected tab.
2077 protected void paintContentBorder(Graphics g, int tabPlacement,
2078 int selectedIndex)
2080 Insets insets = getContentBorderInsets(tabPlacement);
2081 int x = contentRect.x;
2082 int y = contentRect.y;
2083 int w = contentRect.width;
2084 int h = contentRect.height;
2085 paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2086 paintContentBorderLeftEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2087 paintContentBorderBottomEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2088 paintContentBorderRightEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2092 * This method paints the top edge of the content border.
2094 * @param g The Graphics object to paint with.
2095 * @param tabPlacement The JTabbedPane's tab placement.
2096 * @param selectedIndex The selected tab index.
2097 * @param x The x coordinate for the content area.
2098 * @param y The y coordinate for the content area.
2099 * @param w The width of the content area.
2100 * @param h The height of the content area.
2102 protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
2103 int selectedIndex, int x, int y,
2104 int w, int h)
2106 Color saved = g.getColor();
2107 g.setColor(lightHighlight);
2109 int startgap = rects[selectedIndex].x;
2110 int endgap = rects[selectedIndex].x + rects[selectedIndex].width;
2112 int diff = 0;
2114 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
2116 Point p = findPointForIndex(currentScrollLocation);
2117 diff = p.x;
2120 if (tabPlacement == SwingConstants.TOP)
2122 g.drawLine(x, y, startgap - diff, y);
2123 g.drawLine(endgap - diff, y, x + w, y);
2125 else
2126 g.drawLine(x, y, x + w, y);
2128 g.setColor(saved);
2132 * This method paints the left edge of the content border.
2134 * @param g The Graphics object to paint with.
2135 * @param tabPlacement The JTabbedPane's tab placement.
2136 * @param selectedIndex The selected tab index.
2137 * @param x The x coordinate for the content area.
2138 * @param y The y coordinate for the content area.
2139 * @param w The width of the content area.
2140 * @param h The height of the content area.
2142 protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
2143 int selectedIndex, int x, int y,
2144 int w, int h)
2146 Color saved = g.getColor();
2147 g.setColor(lightHighlight);
2149 int startgap = rects[selectedIndex].y;
2150 int endgap = rects[selectedIndex].y + rects[selectedIndex].height;
2152 int diff = 0;
2154 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
2156 Point p = findPointForIndex(currentScrollLocation);
2157 diff = p.y;
2160 if (tabPlacement == SwingConstants.LEFT)
2162 g.drawLine(x, y, x, startgap - diff);
2163 g.drawLine(x, endgap - diff, x, y + h);
2165 else
2166 g.drawLine(x, y, x, y + h);
2168 g.setColor(saved);
2172 * This method paints the bottom edge of the content border.
2174 * @param g The Graphics object to paint with.
2175 * @param tabPlacement The JTabbedPane's tab placement.
2176 * @param selectedIndex The selected tab index.
2177 * @param x The x coordinate for the content area.
2178 * @param y The y coordinate for the content area.
2179 * @param w The width of the content area.
2180 * @param h The height of the content area.
2182 protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
2183 int selectedIndex, int x, int y,
2184 int w, int h)
2186 Color saved = g.getColor();
2188 int startgap = rects[selectedIndex].x;
2189 int endgap = rects[selectedIndex].x + rects[selectedIndex].width;
2191 int diff = 0;
2193 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
2195 Point p = findPointForIndex(currentScrollLocation);
2196 diff = p.x;
2199 if (tabPlacement == SwingConstants.BOTTOM)
2201 g.setColor(shadow);
2202 g.drawLine(x + 1, y + h - 1, startgap - diff, y + h - 1);
2203 g.drawLine(endgap - diff, y + h - 1, x + w - 1, y + h - 1);
2205 g.setColor(darkShadow);
2206 g.drawLine(x, y + h, startgap - diff, y + h);
2207 g.drawLine(endgap - diff, y + h, x + w, y + h);
2209 else
2211 g.setColor(shadow);
2212 g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1);
2213 g.setColor(darkShadow);
2214 g.drawLine(x, y + h, x + w, y + h);
2217 g.setColor(saved);
2221 * This method paints the right edge of the content border.
2223 * @param g The Graphics object to paint with.
2224 * @param tabPlacement The JTabbedPane's tab placement.
2225 * @param selectedIndex The selected tab index.
2226 * @param x The x coordinate for the content area.
2227 * @param y The y coordinate for the content area.
2228 * @param w The width of the content area.
2229 * @param h The height of the content area.
2231 protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
2232 int selectedIndex, int x, int y,
2233 int w, int h)
2235 Color saved = g.getColor();
2236 int startgap = rects[selectedIndex].y;
2237 int endgap = rects[selectedIndex].y + rects[selectedIndex].height;
2239 int diff = 0;
2240 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
2242 Point p = findPointForIndex(currentScrollLocation);
2243 diff = p.y;
2246 if (tabPlacement == SwingConstants.RIGHT)
2248 g.setColor(shadow);
2249 g.drawLine(x + w - 1, y + 1, x + w - 1, startgap - diff);
2250 g.drawLine(x + w - 1, endgap - diff, x + w - 1, y + h - 1);
2252 g.setColor(darkShadow);
2253 g.drawLine(x + w, y, x + w, startgap - diff);
2254 g.drawLine(x + w, endgap - diff, x + w, y + h);
2256 else
2258 g.setColor(shadow);
2259 g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1);
2260 g.setColor(darkShadow);
2261 g.drawLine(x + w, y, x + w, y + h);
2264 g.setColor(saved);
2268 * This method returns the tab bounds for the given index.
2270 * @param pane The JTabbedPane.
2271 * @param i The index to look for.
2273 * @return The bounds of the tab with the given index.
2275 public Rectangle getTabBounds(JTabbedPane pane, int i)
2277 return rects[i];
2281 * This method returns the number of runs.
2283 * @param pane The JTabbedPane.
2285 * @return The number of runs.
2287 public int getTabRunCount(JTabbedPane pane)
2289 return runCount;
2293 * This method returns the tab index given a coordinate.
2295 * @param pane The JTabbedPane.
2296 * @param x The x coordinate.
2297 * @param y The y coordinate.
2299 * @return The tab index that the coordinate lands in.
2301 public int tabForCoordinate(JTabbedPane pane, int x, int y)
2303 Point p = new Point(x, y);
2304 int tabCount = tabPane.getTabCount();
2305 int currRun = 1;
2306 for (int i = 0; i < runCount; i++)
2308 int first = lastTabInRun(tabCount, getPreviousTabRun(currRun)) + 1;
2309 if (first == tabCount)
2310 first = 0;
2311 int last = lastTabInRun(tabCount, currRun);
2312 for (int j = first; j <= last; j++)
2314 if (getTabBounds(pane, j).contains(p))
2315 return j;
2317 currRun = getNextTabRun(currRun);
2319 return -1;
2323 * This method returns the tab bounds in the given rectangle.
2325 * @param tabIndex The index to get bounds for.
2326 * @param dest The rectangle to store bounds in.
2328 * @return The rectangle passed in.
2330 protected Rectangle getTabBounds(int tabIndex, Rectangle dest)
2332 dest.setBounds(getTabBounds(tabPane, tabIndex));
2333 return dest;
2337 * This method returns the component that is shown in the content area.
2339 * @return The component that is shown in the content area.
2341 protected Component getVisibleComponent()
2343 return tabPane.getComponentAt(tabPane.getSelectedIndex());
2347 * This method sets the visible component.
2349 * @param component The component to be set visible.
2351 protected void setVisibleComponent(Component component)
2353 component.setVisible(true);
2354 tabPane.setSelectedComponent(component);
2358 * This method assures that enough rectangles are created given the
2359 * tabCount. The old array is copied to the new one.
2361 * @param tabCount The number of tabs.
2363 protected void assureRectsCreated(int tabCount)
2365 if (rects == null)
2366 rects = new Rectangle[tabCount];
2367 if (tabCount == rects.length)
2368 return;
2369 else
2371 int numToCopy = Math.min(tabCount, rects.length);
2372 Rectangle[] tmp = new Rectangle[tabCount];
2373 System.arraycopy(rects, 0, tmp, 0, numToCopy);
2374 rects = tmp;
2379 * This method expands the tabRuns array to give it more room. The old array
2380 * is copied to the new one.
2382 protected void expandTabRunsArray()
2384 // This method adds another 10 index positions to the tabRuns array.
2385 if (tabRuns == null)
2386 tabRuns = new int[10];
2387 else
2389 int[] newRuns = new int[tabRuns.length + 10];
2390 System.arraycopy(tabRuns, 0, newRuns, 0, tabRuns.length);
2391 tabRuns = newRuns;
2396 * This method returns which run a particular tab belongs to.
2398 * @param tabCount The number of tabs.
2399 * @param tabIndex The tab to find.
2401 * @return The tabRuns index that it belongs to.
2403 protected int getRunForTab(int tabCount, int tabIndex)
2405 if (runCount == 1 && tabIndex < tabCount && tabIndex >= 0)
2406 return 1;
2407 for (int i = 0; i < runCount; i++)
2409 int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1;
2410 if (first == tabCount)
2411 first = 0;
2412 int last = lastTabInRun(tabCount, i);
2413 if (last >= tabIndex && first <= tabIndex)
2414 return i;
2416 return -1;
2420 * This method returns the index of the last tab in a run.
2422 * @param tabCount The number of tabs.
2423 * @param run The run to check.
2425 * @return The last tab in the given run.
2427 protected int lastTabInRun(int tabCount, int run)
2429 if (tabRuns[run] == 0)
2430 return tabCount - 1;
2431 else
2432 return tabRuns[run] - 1;
2436 * This method returns the tab run overlay.
2438 * @param tabPlacement The JTabbedPane's tab placement.
2440 * @return The tab run overlay.
2442 protected int getTabRunOverlay(int tabPlacement)
2444 return tabRunOverlay;
2448 * This method returns the tab run indent. It is used in WRAP_TAB_LAYOUT and
2449 * makes each tab run start indented by a certain amount.
2451 * @param tabPlacement The JTabbedPane's tab placement.
2452 * @param run The run to get indent for.
2454 * @return The amount a run should be indented.
2456 protected int getTabRunIndent(int tabPlacement, int run)
2458 return 0;
2462 * This method returns whether a tab run should be padded.
2464 * @param tabPlacement The JTabbedPane's tab placement.
2465 * @param run The run to check.
2467 * @return Whether the given run should be padded.
2469 protected boolean shouldPadTabRun(int tabPlacement, int run)
2471 return true;
2475 * This method returns whether the tab runs should be rotated.
2477 * @param tabPlacement The JTabbedPane's tab placement.
2479 * @return Whether runs should be rotated.
2481 protected boolean shouldRotateTabRuns(int tabPlacement)
2483 return true;
2487 * This method returns an icon for the tab. If the tab is disabled, it
2488 * should return the disabledIcon. If it is enabled, then it should return
2489 * the default icon.
2491 * @param tabIndex The tab index to get an icon for.
2493 * @return The icon for the tab index.
2495 protected Icon getIconForTab(int tabIndex)
2497 if (tabPane.isEnabledAt(tabIndex))
2498 return tabPane.getIconAt(tabIndex);
2499 else
2500 return tabPane.getDisabledIconAt(tabIndex);
2504 * This method returns a view that can paint the text for the label.
2506 * @param tabIndex The tab index to get a view for.
2508 * @return The view for the tab index.
2510 protected View getTextViewForTab(int tabIndex)
2512 return null;
2516 * This method returns the tab height, including insets, for the given index
2517 * and fontheight.
2519 * @param tabPlacement The JTabbedPane's tab placement.
2520 * @param tabIndex The index of the tab to calculate.
2521 * @param fontHeight The font height.
2523 * @return This tab's height.
2525 protected int calculateTabHeight(int tabPlacement, int tabIndex,
2526 int fontHeight)
2528 Icon icon = getIconForTab(tabIndex);
2529 Insets insets = getTabInsets(tabPlacement, tabIndex);
2531 if (icon != null)
2533 Rectangle vr = new Rectangle();
2534 Rectangle ir = new Rectangle();
2535 Rectangle tr = new Rectangle();
2536 layoutLabel(tabPlacement, getFontMetrics(), tabIndex,
2537 tabPane.getTitleAt(tabIndex), icon, vr, ir, tr,
2538 tabIndex == tabPane.getSelectedIndex());
2539 calcRect = tr.union(ir);
2541 else
2542 calcRect.height = fontHeight;
2544 calcRect.height += insets.top + insets.bottom;
2545 return calcRect.height;
2549 * This method returns the max tab height.
2551 * @param tabPlacement The JTabbedPane's tab placement.
2553 * @return The maximum tab height.
2555 protected int calculateMaxTabHeight(int tabPlacement)
2557 maxTabHeight = 0;
2559 FontMetrics fm = getFontMetrics();
2560 int fontHeight = fm.getHeight();
2562 for (int i = 0; i < tabPane.getTabCount(); i++)
2563 maxTabHeight = Math.max(calculateTabHeight(tabPlacement, i, fontHeight),
2564 maxTabHeight);
2566 return maxTabHeight;
2570 * This method calculates the tab width, including insets, for the given tab
2571 * index and font metrics.
2573 * @param tabPlacement The JTabbedPane's tab placement.
2574 * @param tabIndex The tab index to calculate for.
2575 * @param metrics The font's metrics.
2577 * @return The tab width for the given index.
2579 protected int calculateTabWidth(int tabPlacement, int tabIndex,
2580 FontMetrics metrics)
2582 Icon icon = getIconForTab(tabIndex);
2583 Insets insets = getTabInsets(tabPlacement, tabIndex);
2585 if (icon != null)
2587 Rectangle vr = new Rectangle();
2588 Rectangle ir = new Rectangle();
2589 Rectangle tr = new Rectangle();
2590 layoutLabel(tabPlacement, getFontMetrics(), tabIndex,
2591 tabPane.getTitleAt(tabIndex), icon, vr, ir, tr,
2592 tabIndex == tabPane.getSelectedIndex());
2593 calcRect = tr.union(ir);
2595 else
2596 calcRect.width = metrics.stringWidth(tabPane.getTitleAt(tabIndex));
2598 calcRect.width += insets.left + insets.right;
2599 return calcRect.width;
2603 * This method calculates the max tab width.
2605 * @param tabPlacement The JTabbedPane's tab placement.
2607 * @return The maximum tab width.
2609 protected int calculateMaxTabWidth(int tabPlacement)
2611 maxTabWidth = 0;
2613 FontMetrics fm = getFontMetrics();
2615 for (int i = 0; i < tabPane.getTabCount(); i++)
2616 maxTabWidth = Math.max(calculateTabWidth(tabPlacement, i, fm),
2617 maxTabWidth);
2619 return maxTabWidth;
2623 * This method calculates the tab area height, including insets, for the
2624 * given amount of runs and tab height.
2626 * @param tabPlacement The JTabbedPane's tab placement.
2627 * @param horizRunCount The number of runs.
2628 * @param maxTabHeight The max tab height.
2630 * @return The tab area height.
2632 protected int calculateTabAreaHeight(int tabPlacement, int horizRunCount,
2633 int maxTabHeight)
2635 Insets insets = getTabAreaInsets(tabPlacement);
2636 int tabAreaHeight = horizRunCount * maxTabHeight
2637 - (horizRunCount - 1) * tabRunOverlay;
2639 tabAreaHeight += insets.top + insets.bottom;
2641 return tabAreaHeight;
2645 * This method calculates the tab area width, including insets, for the
2646 * given amount of runs and tab width.
2648 * @param tabPlacement The JTabbedPane's tab placement.
2649 * @param vertRunCount The number of runs.
2650 * @param maxTabWidth The max tab width.
2652 * @return The tab area width.
2654 protected int calculateTabAreaWidth(int tabPlacement, int vertRunCount,
2655 int maxTabWidth)
2657 Insets insets = getTabAreaInsets(tabPlacement);
2658 int tabAreaWidth = vertRunCount * maxTabWidth
2659 - (vertRunCount - 1) * tabRunOverlay;
2661 tabAreaWidth += insets.left + insets.right;
2663 return tabAreaWidth;
2667 * This method returns the tab insets appropriately rotated.
2669 * @param tabPlacement The JTabbedPane's tab placement.
2670 * @param tabIndex The tab index.
2672 * @return The tab insets for the given index.
2674 protected Insets getTabInsets(int tabPlacement, int tabIndex)
2676 Insets target = new Insets(0, 0, 0, 0);
2677 rotateInsets(tabInsets, target, tabPlacement);
2678 return target;
2682 * This method returns the selected tab pad insets appropriately rotated.
2684 * @param tabPlacement The JTabbedPane's tab placement.
2686 * @return The selected tab pad insets.
2688 protected Insets getSelectedTabPadInsets(int tabPlacement)
2690 Insets target = new Insets(0, 0, 0, 0);
2691 rotateInsets(selectedTabPadInsets, target, tabPlacement);
2692 return target;
2696 * This method returns the tab area insets appropriately rotated.
2698 * @param tabPlacement The JTabbedPane's tab placement.
2700 * @return The tab area insets.
2702 protected Insets getTabAreaInsets(int tabPlacement)
2704 Insets target = new Insets(0, 0, 0, 0);
2705 rotateInsets(tabAreaInsets, target, tabPlacement);
2706 return target;
2710 * This method returns the content border insets appropriately rotated.
2712 * @param tabPlacement The JTabbedPane's tab placement.
2714 * @return The content border insets.
2716 protected Insets getContentBorderInsets(int tabPlacement)
2718 Insets target = new Insets(0, 0, 0, 0);
2719 rotateInsets(contentBorderInsets, target, tabPlacement);
2720 return target;
2724 * This method returns the fontmetrics for the font of the JTabbedPane.
2726 * @return The font metrics for the JTabbedPane.
2728 protected FontMetrics getFontMetrics()
2730 FontMetrics fm = tabPane.getToolkit().getFontMetrics(tabPane.getFont());
2731 return fm;
2735 * This method navigates from the selected tab into the given direction. As
2736 * a result, a new tab will be selected (if possible).
2738 * @param direction The direction to navigate in.
2740 protected void navigateSelectedTab(int direction)
2742 int tabPlacement = tabPane.getTabPlacement();
2743 if (tabPlacement == SwingConstants.TOP
2744 || tabPlacement == SwingConstants.BOTTOM)
2746 if (direction == SwingConstants.WEST)
2747 selectPreviousTabInRun(tabPane.getSelectedIndex());
2748 else if (direction == SwingConstants.EAST)
2749 selectNextTabInRun(tabPane.getSelectedIndex());
2751 else
2753 int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(),
2754 tabPane.getSelectedIndex(),
2755 (tabPlacement == SwingConstants.RIGHT)
2756 ? true : false);
2757 selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(),
2758 offset);
2761 if (tabPlacement == SwingConstants.LEFT
2762 || tabPlacement == SwingConstants.RIGHT)
2764 if (direction == SwingConstants.NORTH)
2765 selectPreviousTabInRun(tabPane.getSelectedIndex());
2766 else if (direction == SwingConstants.SOUTH)
2767 selectNextTabInRun(tabPane.getSelectedIndex());
2768 else
2770 int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(),
2771 tabPane.getSelectedIndex(),
2772 (tabPlacement == SwingConstants.RIGHT)
2773 ? true : false);
2774 selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(),
2775 offset);
2781 * This method selects the next tab in the run.
2783 * @param current The current selected index.
2785 protected void selectNextTabInRun(int current)
2787 tabPane.setSelectedIndex(getNextTabIndexInRun(tabPane.getTabCount(),
2788 current));
2792 * This method selects the previous tab in the run.
2794 * @param current The current selected index.
2796 protected void selectPreviousTabInRun(int current)
2798 tabPane.setSelectedIndex(getPreviousTabIndexInRun(tabPane.getTabCount(),
2799 current));
2803 * This method selects the next tab (regardless of runs).
2805 * @param current The current selected index.
2807 protected void selectNextTab(int current)
2809 tabPane.setSelectedIndex(getNextTabIndex(current));
2813 * This method selects the previous tab (regardless of runs).
2815 * @param current The current selected index.
2817 protected void selectPreviousTab(int current)
2819 tabPane.setSelectedIndex(getPreviousTabIndex(current));
2823 * This method selects the correct tab given an offset from the current tab
2824 * index. If the tab placement is TOP or BOTTOM, the offset will be in the
2825 * y direction, otherwise, it will be in the x direction. A new coordinate
2826 * will be found by adding the offset to the current location of the tab.
2827 * The tab that the new location will be selected.
2829 * @param tabPlacement The JTabbedPane's tab placement.
2830 * @param tabIndex The tab to start from.
2831 * @param offset The coordinate offset.
2833 protected void selectAdjacentRunTab(int tabPlacement, int tabIndex,
2834 int offset)
2836 int x = rects[tabIndex].x + rects[tabIndex].width / 2;
2837 int y = rects[tabIndex].y + rects[tabIndex].height / 2;
2839 switch (tabPlacement)
2841 case SwingConstants.TOP:
2842 case SwingConstants.BOTTOM:
2843 y += offset;
2844 break;
2845 case SwingConstants.RIGHT:
2846 case SwingConstants.LEFT:
2847 x += offset;
2848 break;
2851 int index = tabForCoordinate(tabPane, x, y);
2852 if (index != -1)
2853 tabPane.setSelectedIndex(index);
2856 // This method is called when you press up/down to cycle through tab runs.
2857 // it returns the distance (between the two runs' x/y position.
2858 // where one run is the current selected run and the other run is the run in the
2859 // direction of the scroll (dictated by the forward flag)
2860 // the offset is an absolute value of the difference
2863 * This method calculates the offset distance for use in
2864 * selectAdjacentRunTab. The offset returned will be a difference in the y
2865 * coordinate between the run in the desired direction and the current run
2866 * (for tabPlacement in TOP or BOTTOM). Use x coordinate for LEFT and
2867 * RIGHT.
2869 * @param tabPlacement The JTabbedPane's tab placement.
2870 * @param tabCount The number of tabs.
2871 * @param tabIndex The starting index.
2872 * @param forward If forward, the run in the desired direction will be the
2873 * next run.
2875 * @return The offset between the two runs.
2877 protected int getTabRunOffset(int tabPlacement, int tabCount, int tabIndex,
2878 boolean forward)
2880 int currRun = getRunForTab(tabCount, tabIndex);
2881 int offset;
2882 int nextRun = (forward) ? getNextTabRun(currRun) : getPreviousTabRun(currRun);
2883 if (tabPlacement == SwingConstants.TOP
2884 || tabPlacement == SwingConstants.BOTTOM)
2885 offset = rects[lastTabInRun(tabCount, nextRun)].y
2886 - rects[lastTabInRun(tabCount, currRun)].y;
2887 else
2888 offset = rects[lastTabInRun(tabCount, nextRun)].x
2889 - rects[lastTabInRun(tabCount, currRun)].x;
2890 return offset;
2894 * This method returns the previous tab index.
2896 * @param base The index to start from.
2898 * @return The previous tab index.
2900 protected int getPreviousTabIndex(int base)
2902 base--;
2903 if (base < 0)
2904 return tabPane.getTabCount() - 1;
2905 return base;
2909 * This method returns the next tab index.
2911 * @param base The index to start from.
2913 * @return The next tab index.
2915 protected int getNextTabIndex(int base)
2917 base++;
2918 if (base == tabPane.getTabCount())
2919 return 0;
2920 return base;
2924 * This method returns the next tab index in the run. If the next index is
2925 * out of this run, it will return the starting tab index for the run.
2927 * @param tabCount The number of tabs.
2928 * @param base The index to start from.
2930 * @return The next tab index in the run.
2932 protected int getNextTabIndexInRun(int tabCount, int base)
2934 int index = getNextTabIndex(base);
2935 int run = getRunForTab(tabCount, base);
2936 if (index == lastTabInRun(tabCount, run) + 1)
2937 index = lastTabInRun(tabCount, getPreviousTabRun(run)) + 1;
2938 return getNextTabIndex(base);
2942 * This method returns the previous tab index in the run. If the previous
2943 * index is out of this run, it will return the last index for the run.
2945 * @param tabCount The number of tabs.
2946 * @param base The index to start from.
2948 * @return The previous tab index in the run.
2950 protected int getPreviousTabIndexInRun(int tabCount, int base)
2952 int index = getPreviousTabIndex(base);
2953 int run = getRunForTab(tabCount, base);
2954 if (index == lastTabInRun(tabCount, getPreviousTabRun(run)))
2955 index = lastTabInRun(tabCount, run);
2956 return getPreviousTabIndex(base);
2960 * This method returns the index of the previous run.
2962 * @param baseRun The run to start from.
2964 * @return The index of the previous run.
2966 protected int getPreviousTabRun(int baseRun)
2968 if (getTabRunCount(tabPane) == 1)
2969 return 1;
2971 int prevRun = --baseRun;
2972 if (prevRun < 0)
2973 prevRun = getTabRunCount(tabPane) - 1;
2974 return prevRun;
2978 * This method returns the index of the next run.
2980 * @param baseRun The run to start from.
2982 * @return The index of the next run.
2984 protected int getNextTabRun(int baseRun)
2986 if (getTabRunCount(tabPane) == 1)
2987 return 1;
2989 int nextRun = ++baseRun;
2990 if (nextRun == getTabRunCount(tabPane))
2991 nextRun = 0;
2992 return nextRun;
2996 * This method rotates the insets given a direction to rotate them in.
2997 * Target placement should be one of TOP, LEFT, BOTTOM, RIGHT. The rotated
2998 * insets will be stored in targetInsets. Passing in TOP as the direction
2999 * does nothing. Passing in LEFT switches top and left, right and bottom.
3000 * Passing in BOTTOM switches top and bottom. Passing in RIGHT switches top
3001 * for left, left for bottom, bottom for right, and right for top.
3003 * @param topInsets The reference insets.
3004 * @param targetInsets An Insets object to store the new insets.
3005 * @param targetPlacement The rotation direction.
3007 protected static void rotateInsets(Insets topInsets, Insets targetInsets,
3008 int targetPlacement)
3010 // Sun's version will happily throw an NPE if params are null,
3011 // so I won't check it either.
3012 switch (targetPlacement)
3014 case SwingConstants.TOP:
3015 targetInsets.top = topInsets.top;
3016 targetInsets.left = topInsets.left;
3017 targetInsets.right = topInsets.right;
3018 targetInsets.bottom = topInsets.bottom;
3019 break;
3020 case SwingConstants.LEFT:
3021 targetInsets.left = topInsets.top;
3022 targetInsets.top = topInsets.left;
3023 targetInsets.right = topInsets.bottom;
3024 targetInsets.bottom = topInsets.right;
3025 break;
3026 case SwingConstants.BOTTOM:
3027 targetInsets.top = topInsets.bottom;
3028 targetInsets.bottom = topInsets.top;
3029 targetInsets.left = topInsets.left;
3030 targetInsets.right = topInsets.right;
3031 break;
3032 case SwingConstants.RIGHT:
3033 targetInsets.top = topInsets.left;
3034 targetInsets.left = topInsets.bottom;
3035 targetInsets.bottom = topInsets.right;
3036 targetInsets.right = topInsets.top;
3037 break;