Merge from mainline
[official-gcc.git] / libjava / classpath / javax / swing / plaf / basic / BasicTabbedPaneUI.java
bloba8f52cef617af695b0d92ba12e92e52add44e10a
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)
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., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 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 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;
45 import java.awt.Font;
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.LookAndFeel;
68 import javax.swing.SwingConstants;
69 import javax.swing.SwingUtilities;
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;
79 /**
80 * This is the Basic Look and Feel's UI delegate for JTabbedPane.
82 public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
84 /**
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
93 /**
94 * This method is called when the component gains focus.
96 * @param e The FocusEvent.
98 public void focusGained(FocusEvent e)
100 // FIXME: Implement.
104 * This method is called when the component loses focus.
106 * @param e The FocusEvent.
108 public void focusLost(FocusEvent e)
110 // FIXME: Implement.
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)
133 int x = e.getX();
134 int y = e.getY();
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;
144 int width = 0;
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();
154 tabPane.repaint();
155 return;
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();
166 tabPane.repaint();
167 return;
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();
178 tabPane.repaint();
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
193 * changes.
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();
211 tabPane.revalidate();
212 tabPane.repaint();
217 * A LayoutManager responsible for placing all the tabs and the visible
218 * component inside the JTabbedPane. This class is only used for
219 * WRAP_TAB_LAYOUT.
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)
235 // Do nothing.
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 assureRectsCreated(tabPane.getTabCount());
245 contentRect = SwingUtilities.calculateInnerArea(tabPane, contentRect);
247 calculateTabRects(tabPane.getTabPlacement(), tabPane.getTabCount());
249 if (tabPane.getSelectedIndex() != -1)
251 Component visible = getVisibleComponent();
252 Insets insets = getContentBorderInsets(tabPane.getTabPlacement());
253 if (visible != null)
254 visible.setBounds(contentRect.x + insets.left,
255 contentRect.y + insets.top,
256 contentRect.width - insets.left - insets.right,
257 contentRect.height - insets.top - insets.bottom);
262 * This method calculates the size of the the JTabbedPane.
264 * @param minimum Whether the JTabbedPane will try to be as small as it
265 * can.
267 * @return The desired size of the JTabbedPane.
269 protected Dimension calculateSize(boolean minimum)
271 int tabPlacement = tabPane.getTabPlacement();
272 int width = 0;
273 int height = 0;
275 int componentHeight = 0;
276 int componentWidth = 0;
277 Component c;
278 Dimension dims;
279 for (int i = 0; i < tabPane.getTabCount(); i++)
281 c = tabPane.getComponentAt(i);
282 if (c == null)
283 continue;
284 calcRect = c.getBounds();
285 dims = c.getPreferredSize();
286 if (dims != null)
288 componentHeight = Math.max(componentHeight, dims.height);
289 componentWidth = Math.max(componentWidth, dims.width);
292 if (tabPlacement == SwingConstants.TOP
293 || tabPlacement == SwingConstants.BOTTOM)
295 int min = calculateMaxTabWidth(tabPlacement);
296 width = Math.max(min, componentWidth);
298 int tabAreaHeight = preferredTabAreaHeight(tabPlacement, width);
299 height = tabAreaHeight + componentHeight;
301 else
303 int min = calculateMaxTabHeight(tabPlacement);
304 height = Math.max(min, componentHeight);
306 int tabAreaWidth = preferredTabAreaWidth(tabPlacement, height);
307 width = tabAreaWidth + componentWidth;
310 return new Dimension(width, height);
313 // if tab placement is LEFT OR RIGHT, they share width.
314 // if tab placement is TOP OR BOTTOM, they share height
315 // PRE STEP: finds the default sizes for the labels as well as their locations.
316 // AND where they will be placed within the run system.
317 // 1. calls normalizeTab Runs.
318 // 2. calls rotate tab runs.
319 // 3. pads the tab runs.
320 // 4. pads the selected tab.
323 * This method is called to calculate the tab rectangles. This method
324 * will calculate the size and position of all rectangles (taking into
325 * account which ones should be in which tab run). It will pad them and
326 * normalize them as necessary.
328 * @param tabPlacement The JTabbedPane's tab placement.
329 * @param tabCount The run the current selection is in.
331 protected void calculateTabRects(int tabPlacement, int tabCount)
333 if (tabCount == 0)
334 return;
336 FontMetrics fm = getFontMetrics();
337 SwingUtilities.calculateInnerArea(tabPane, calcRect);
338 Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
339 Insets insets = tabPane.getInsets();
340 int max = 0;
341 int runs = 0;
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;
351 int width = 0;
352 int runWidth = start;
354 for (int i = 0; i < tabCount; i++)
356 width = calculateTabWidth(tabPlacement, i, fm);
357 if (runWidth + width > max)
359 runWidth = tabAreaInsets.left + insets.left
360 + getTabRunIndent(tabPlacement, ++runs);
361 rects[i] = new Rectangle(runWidth,
362 insets.top + tabAreaInsets.top,
363 width, maxHeight);
364 runWidth += width;
365 if (runs > tabRuns.length - 1)
366 expandTabRunsArray();
367 tabRuns[runs] = i;
369 else
371 rects[i] = new Rectangle(runWidth,
372 insets.top + tabAreaInsets.top,
373 width, maxHeight);
374 runWidth += width;
377 runs++;
378 tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right;
379 tabAreaRect.height = runs * maxTabHeight
380 - (runs - 1) * tabRunOverlay
381 + tabAreaInsets.top + tabAreaInsets.bottom;
382 contentRect.width = tabAreaRect.width;
383 contentRect.height = tabPane.getHeight() - insets.top
384 - insets.bottom - tabAreaRect.height;
385 contentRect.x = insets.left;
386 tabAreaRect.x = insets.left;
387 if (tabPlacement == SwingConstants.BOTTOM)
389 contentRect.y = insets.top;
390 tabAreaRect.y = contentRect.y + contentRect.height;
392 else
394 tabAreaRect.y = insets.top;
395 contentRect.y = tabAreaRect.y + tabAreaRect.height;
398 else
400 int maxWidth = calculateMaxTabWidth(tabPlacement);
401 calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom;
402 max = calcRect.height + tabAreaInsets.top + insets.top;
404 int height = 0;
405 start += tabAreaInsets.top + insets.top;
406 int runHeight = start;
408 int fontHeight = fm.getHeight();
410 for (int i = 0; i < tabCount; i++)
412 height = calculateTabHeight(tabPlacement, i, fontHeight);
413 if (runHeight + height > max)
415 runHeight = tabAreaInsets.top + insets.top
416 + getTabRunIndent(tabPlacement, ++runs);
417 rects[i] = new Rectangle(insets.left + tabAreaInsets.left,
418 runHeight, maxWidth, height);
419 runHeight += height;
420 if (runs > tabRuns.length - 1)
421 expandTabRunsArray();
422 tabRuns[runs] = i;
424 else
426 rects[i] = new Rectangle(insets.left + tabAreaInsets.left,
427 runHeight, maxWidth, height);
428 runHeight += height;
431 runs++;
433 tabAreaRect.width = runs * maxTabWidth - (runs - 1) * tabRunOverlay
434 + tabAreaInsets.left + tabAreaInsets.right;
435 tabAreaRect.height = tabPane.getHeight() - insets.top
436 - insets.bottom;
437 tabAreaRect.y = insets.top;
438 contentRect.width = tabPane.getWidth() - insets.left - insets.right
439 - tabAreaRect.width;
440 contentRect.height = tabAreaRect.height;
441 contentRect.y = insets.top;
442 if (tabPlacement == SwingConstants.LEFT)
444 tabAreaRect.x = insets.left;
445 contentRect.x = tabAreaRect.x + tabAreaRect.width;
447 else
449 contentRect.x = insets.left;
450 tabAreaRect.x = contentRect.x + contentRect.width;
453 runCount = runs;
455 tabRuns[0] = 0;
456 normalizeTabRuns(tabPlacement, tabCount, start, max);
457 selectedRun = getRunForTab(tabCount, tabPane.getSelectedIndex());
458 if (shouldRotateTabRuns(tabPlacement))
459 rotateTabRuns(tabPlacement, selectedRun);
461 // Need to pad the runs and move them to the correct location.
462 for (int i = 0; i < runCount; i++)
464 int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1;
465 if (first == tabCount)
466 first = 0;
467 int last = lastTabInRun(tabCount, i);
468 if (shouldPadTabRun(tabPlacement, i))
469 padTabRun(tabPlacement, first, last, max);
471 // Done padding, now need to move it.
472 if (tabPlacement == SwingConstants.TOP && i > 0)
474 for (int j = first; j <= last; j++)
475 rects[j].y += (runCount - i) * maxTabHeight
476 - (runCount - i) * tabRunOverlay;
479 if (tabPlacement == SwingConstants.BOTTOM)
481 int height = tabPane.getBounds().height - insets.bottom
482 - tabAreaInsets.bottom;
483 int adjustment;
484 if (i == 0)
485 adjustment = height - maxTabHeight;
486 else
487 adjustment = height - (runCount - i + 1) * maxTabHeight
488 - (runCount - i) * tabRunOverlay;
490 for (int j = first; j <= last; j++)
491 rects[j].y = adjustment;
494 if (tabPlacement == SwingConstants.LEFT && i > 0)
496 for (int j = first; j <= last; j++)
497 rects[j].x += (runCount - i) * maxTabWidth
498 - (runCount - i) * tabRunOverlay;
501 if (tabPlacement == SwingConstants.RIGHT)
503 int width = tabPane.getBounds().width - insets.right
504 - tabAreaInsets.right;
505 int adjustment;
506 if (i == 0)
507 adjustment = width - maxTabWidth;
508 else
509 adjustment = width - (runCount - i + 1) * maxTabWidth
510 + (runCount - i) * tabRunOverlay;
512 for (int j = first; j <= last; j++)
513 rects[j].x = adjustment;
516 padSelectedTab(tabPlacement, tabPane.getSelectedIndex());
520 * This method is called when the JTabbedPane is laid out in
521 * WRAP_TAB_LAYOUT. It calls calculateLayoutInfo to find the positions
522 * of all its components.
524 * @param parent The Container to lay out.
526 public void layoutContainer(Container parent)
528 calculateLayoutInfo();
532 * This method returns the minimum layout size for the given container.
534 * @param parent The container that is being sized.
536 * @return The minimum size.
538 public Dimension minimumLayoutSize(Container parent)
540 return calculateSize(false);
543 // If there is more free space in an adjacent run AND the tab in the run can fit in the
544 // adjacent run, move it. This method is not perfect, it is merely an approximation.
545 // If you play around with Sun's JTabbedPane, you'll see that
546 // it does do some pretty strange things with regards to not moving tabs
547 // that should be moved.
548 // start = the x position where the tabs will begin
549 // max = the maximum position of where the tabs can go to (tabAreaInsets.left + the width of the tab area)
552 * This method tries to "even out" the number of tabs in each run based on
553 * their widths.
555 * @param tabPlacement The JTabbedPane's tab placement.
556 * @param tabCount The number of tabs.
557 * @param start The x position where the tabs will begin.
558 * @param max The maximum x position where the tab can run to.
560 protected void normalizeTabRuns(int tabPlacement, int tabCount, int start,
561 int max)
563 Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
564 if (tabPlacement == SwingUtilities.TOP
565 || tabPlacement == SwingUtilities.BOTTOM)
567 // We should only do this for runCount - 1, cause we can only shift that many times between
568 // runs.
569 for (int i = 1; i < runCount; i++)
571 Rectangle currRun = rects[lastTabInRun(tabCount, i)];
572 Rectangle nextRun = rects[lastTabInRun(tabCount, getNextTabRun(i))];
573 int spaceInCurr = currRun.x + currRun.width;
574 int spaceInNext = nextRun.x + nextRun.width;
576 int diffNow = spaceInCurr - spaceInNext;
577 int diffLater = (spaceInCurr - currRun.width)
578 - (spaceInNext + currRun.width);
579 while (Math.abs(diffLater) < Math.abs(diffNow)
580 && spaceInNext + currRun.width < max)
582 tabRuns[i]--;
583 spaceInNext += currRun.width;
584 spaceInCurr -= currRun.width;
585 currRun = rects[lastTabInRun(tabCount, i)];
586 diffNow = spaceInCurr - spaceInNext;
587 diffLater = (spaceInCurr - currRun.width)
588 - (spaceInNext + currRun.width);
591 // Fix the bounds.
592 int first = lastTabInRun(tabCount, i) + 1;
593 int last = lastTabInRun(tabCount, getNextTabRun(i));
594 int currX = tabAreaInsets.left;
595 for (int j = first; j <= last; j++)
597 rects[j].x = currX;
598 currX += rects[j].width;
602 else
604 for (int i = 1; i < runCount; i++)
606 Rectangle currRun = rects[lastTabInRun(tabCount, i)];
607 Rectangle nextRun = rects[lastTabInRun(tabCount, getNextTabRun(i))];
608 int spaceInCurr = currRun.y + currRun.height;
609 int spaceInNext = nextRun.y + nextRun.height;
611 int diffNow = spaceInCurr - spaceInNext;
612 int diffLater = (spaceInCurr - currRun.height)
613 - (spaceInNext + currRun.height);
614 while (Math.abs(diffLater) < Math.abs(diffNow)
615 && spaceInNext + currRun.height < max)
617 tabRuns[i]--;
618 spaceInNext += currRun.height;
619 spaceInCurr -= currRun.height;
620 currRun = rects[lastTabInRun(tabCount, i)];
621 diffNow = spaceInCurr - spaceInNext;
622 diffLater = (spaceInCurr - currRun.height)
623 - (spaceInNext + currRun.height);
626 int first = lastTabInRun(tabCount, i) + 1;
627 int last = lastTabInRun(tabCount, getNextTabRun(i));
628 int currY = tabAreaInsets.top;
629 for (int j = first; j <= last; j++)
631 rects[j].y = currY;
632 currY += rects[j].height;
639 * This method pads the tab at the selected index by the selected tab pad
640 * insets (so that it looks larger).
642 * @param tabPlacement The placement of the tabs.
643 * @param selectedIndex The selected index.
645 protected void padSelectedTab(int tabPlacement, int selectedIndex)
647 Insets insets = getSelectedTabPadInsets(tabPlacement);
648 rects[selectedIndex].x -= insets.left;
649 rects[selectedIndex].y -= insets.top;
650 rects[selectedIndex].width += insets.left + insets.right;
651 rects[selectedIndex].height += insets.top + insets.bottom;
654 // If the tabs on the run don't fill the width of the window, make it fit now.
655 // start = starting index of the run
656 // end = last index of the run
657 // max = tabAreaInsets.left + width (or equivalent)
658 // assert start <= end.
661 * This method makes each tab in the run larger so that the tabs expand
662 * to fill the runs width/height (depending on tabPlacement).
664 * @param tabPlacement The placement of the tabs.
665 * @param start The index of the first tab.
666 * @param end The last index of the tab
667 * @param max The amount of space in the run (width for TOP and BOTTOM
668 * tabPlacement).
670 protected void padTabRun(int tabPlacement, int start, int end, int max)
672 if (tabPlacement == SwingConstants.TOP
673 || tabPlacement == SwingConstants.BOTTOM)
675 int runWidth = rects[end].x + rects[end].width;
676 int spaceRemaining = max - runWidth;
677 int numTabs = end - start + 1;
679 // now divvy up the space.
680 int spaceAllocated = spaceRemaining / numTabs;
681 int currX = rects[start].x;
682 for (int i = start; i <= end; i++)
684 rects[i].x = currX;
685 rects[i].width += spaceAllocated;
686 currX += rects[i].width;
687 // This is used because since the spaceAllocated
688 // variable is an int, it rounds down. Sometimes,
689 // we don't fill an entire row, so we make it do
690 // so now.
691 if (i == end && rects[i].x + rects[i].width != max)
692 rects[i].width = max - rects[i].x;
695 else
697 int runHeight = rects[end].y + rects[end].height;
698 int spaceRemaining = max - runHeight;
699 int numTabs = end - start + 1;
701 int spaceAllocated = spaceRemaining / numTabs;
702 int currY = rects[start].y;
703 for (int i = start; i <= end; i++)
705 rects[i].y = currY;
706 rects[i].height += spaceAllocated;
707 currY += rects[i].height;
708 if (i == end && rects[i].y + rects[i].height != max)
709 rects[i].height = max - rects[i].y;
715 * This method returns the preferred layout size for the given container.
717 * @param parent The container to size.
719 * @return The preferred layout size.
721 public Dimension preferredLayoutSize(Container parent)
723 return calculateSize(false);
727 * This method returns the preferred tab height given a tabPlacement and
728 * width.
730 * @param tabPlacement The JTabbedPane's tab placement.
731 * @param width The expected width.
733 * @return The preferred tab area height.
735 protected int preferredTabAreaHeight(int tabPlacement, int width)
737 if (tabPane.getTabCount() == 0)
738 return calculateTabAreaHeight(tabPlacement, 0, 0);
740 int runs = 0;
741 int runWidth = 0;
742 int tabWidth = 0;
744 FontMetrics fm = getFontMetrics();
746 Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
747 Insets insets = tabPane.getInsets();
749 // Only interested in width, this is a messed up rectangle now.
750 width -= tabAreaInsets.left + tabAreaInsets.right + insets.left
751 + insets.right;
753 // The reason why we can't use runCount:
754 // This method is only called to calculate the size request
755 // for the tabbedPane. However, this size request is dependent on
756 // our desired width. We need to find out what the height would
757 // be IF we got our desired width.
758 for (int i = 0; i < tabPane.getTabCount(); i++)
760 tabWidth = calculateTabWidth(tabPlacement, i, fm);
761 if (runWidth + tabWidth > width)
763 runWidth = tabWidth;
764 runs++;
766 else
767 runWidth += tabWidth;
769 runs++;
771 int maxTabHeight = calculateMaxTabHeight(tabPlacement);
772 int tabAreaHeight = calculateTabAreaHeight(tabPlacement, runs,
773 maxTabHeight);
774 return tabAreaHeight;
778 * This method calculates the preferred tab area width given a tab
779 * placement and height.
781 * @param tabPlacement The JTabbedPane's tab placement.
782 * @param height The expected height.
784 * @return The preferred tab area width.
786 protected int preferredTabAreaWidth(int tabPlacement, int height)
788 if (tabPane.getTabCount() == 0)
789 return calculateTabAreaHeight(tabPlacement, 0, 0);
791 int runs = 0;
792 int runHeight = 0;
793 int tabHeight = 0;
795 FontMetrics fm = getFontMetrics();
797 Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
798 Insets insets = tabPane.getInsets();
800 height -= tabAreaInsets.top + tabAreaInsets.bottom + insets.top
801 + insets.bottom;
802 int fontHeight = fm.getHeight();
804 for (int i = 0; i < tabPane.getTabCount(); i++)
806 tabHeight = calculateTabHeight(tabPlacement, i, fontHeight);
807 if (runHeight + tabHeight > height)
809 runHeight = tabHeight;
810 runs++;
812 else
813 runHeight += tabHeight;
815 runs++;
817 int maxTabWidth = calculateMaxTabWidth(tabPlacement);
818 int tabAreaWidth = calculateTabAreaWidth(tabPlacement, runs, maxTabWidth);
819 return tabAreaWidth;
823 * This method rotates the places each run in the correct place the
824 * tabRuns array. See the comment for tabRuns for how the runs are placed
825 * in the array.
827 * @param tabPlacement The JTabbedPane's tab placement.
828 * @param selectedRun The run the current selection is in.
830 protected void rotateTabRuns(int tabPlacement, int selectedRun)
832 if (runCount == 1 || selectedRun == 1 || selectedRun == -1)
833 return;
834 int[] newTabRuns = new int[tabRuns.length];
835 int currentRun = selectedRun;
836 int i = 1;
839 newTabRuns[i] = tabRuns[currentRun];
840 currentRun = getNextTabRun(currentRun);
841 i++;
843 while (i < runCount);
844 if (runCount > 1)
845 newTabRuns[0] = tabRuns[currentRun];
847 tabRuns = newTabRuns;
848 BasicTabbedPaneUI.this.selectedRun = 1;
852 * This method is called when a component is removed from the
853 * JTabbedPane.
855 * @param comp The component removed.
857 public void removeLayoutComponent(Component comp)
859 // Do nothing.
864 * This class acts as the LayoutManager for the JTabbedPane in
865 * SCROLL_TAB_MODE.
867 private class TabbedPaneScrollLayout extends TabbedPaneLayout
870 * This method returns the preferred layout size for the given container.
872 * @param parent The container to calculate a size for.
874 * @return The preferred layout size.
876 public Dimension preferredLayoutSize(Container parent)
878 return super.calculateSize(true);
882 * This method returns the minimum layout size for the given container.
884 * @param parent The container to calculate a size for.
886 * @return The minimum layout size.
888 public Dimension minimumLayoutSize(Container parent)
890 return super.calculateSize(true);
894 * This method calculates the tab area height given a desired width.
896 * @param tabPlacement The JTabbedPane's tab placement.
897 * @param width The expected width.
899 * @return The tab area height given the width.
901 protected int preferredTabAreaHeight(int tabPlacement, int width)
903 if (tabPane.getTabCount() == 0)
904 return calculateTabAreaHeight(tabPlacement, 0, 0);
906 int runs = 1;
908 int maxTabHeight = calculateMaxTabHeight(tabPlacement);
909 int tabAreaHeight = calculateTabAreaHeight(tabPlacement, runs,
910 maxTabHeight);
911 return tabAreaHeight;
915 * This method calculates the tab area width given a desired height.
917 * @param tabPlacement The JTabbedPane's tab placement.
918 * @param height The expected height.
920 * @return The tab area width given the height.
922 protected int preferredTabAreaWidth(int tabPlacement, int height)
924 if (tabPane.getTabCount() == 0)
925 return calculateTabAreaHeight(tabPlacement, 0, 0);
927 int runs = 1;
929 int maxTabWidth = calculateMaxTabWidth(tabPlacement);
930 int tabAreaWidth = calculateTabAreaWidth(tabPlacement, runs, maxTabWidth);
931 return tabAreaWidth;
935 * This method is called to calculate the tab rectangles. This method
936 * will calculate the size and position of all rectangles (taking into
937 * account which ones should be in which tab run). It will pad them and
938 * normalize them as necessary.
940 * @param tabPlacement The JTabbedPane's tab placement.
941 * @param tabCount The number of tabs.
943 protected void calculateTabRects(int tabPlacement, int tabCount)
945 if (tabCount == 0)
946 return;
948 FontMetrics fm = getFontMetrics();
949 SwingUtilities.calculateInnerArea(tabPane, calcRect);
950 Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
951 Insets insets = tabPane.getInsets();
952 int runs = 1;
953 int start = 0;
954 int top = 0;
955 if (tabPlacement == SwingConstants.TOP
956 || tabPlacement == SwingConstants.BOTTOM)
958 int maxHeight = calculateMaxTabHeight(tabPlacement);
959 calcRect.width -= tabAreaInsets.left + tabAreaInsets.right;
960 start = tabAreaInsets.left + insets.left;
961 int width = 0;
962 int runWidth = start;
963 top = insets.top + tabAreaInsets.top;
964 for (int i = 0; i < tabCount; i++)
966 width = calculateTabWidth(tabPlacement, i, fm);
968 rects[i] = new Rectangle(runWidth, top, width, maxHeight);
969 runWidth += width;
971 tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right;
972 tabAreaRect.height = runs * maxTabHeight
973 - (runs - 1) * tabRunOverlay
974 + tabAreaInsets.top + tabAreaInsets.bottom;
975 contentRect.width = tabAreaRect.width;
976 contentRect.height = tabPane.getHeight() - insets.top
977 - insets.bottom - tabAreaRect.height;
978 contentRect.x = insets.left;
979 tabAreaRect.x = insets.left;
980 if (tabPlacement == SwingConstants.BOTTOM)
982 contentRect.y = insets.top;
983 tabAreaRect.y = contentRect.y + contentRect.height;
985 else
987 tabAreaRect.y = insets.top;
988 contentRect.y = tabAreaRect.y + tabAreaRect.height;
991 else
993 int maxWidth = calculateMaxTabWidth(tabPlacement);
995 calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom;
996 int height = 0;
997 start = tabAreaInsets.top + insets.top;
998 int runHeight = start;
999 int fontHeight = fm.getHeight();
1000 top = insets.left + tabAreaInsets.left;
1001 for (int i = 0; i < tabCount; i++)
1003 height = calculateTabHeight(tabPlacement, i, fontHeight);
1004 rects[i] = new Rectangle(top, runHeight, maxWidth, height);
1005 runHeight += height;
1007 tabAreaRect.width = runs * maxTabWidth - (runs - 1) * tabRunOverlay
1008 + tabAreaInsets.left + tabAreaInsets.right;
1009 tabAreaRect.height = tabPane.getHeight() - insets.top
1010 - insets.bottom;
1011 tabAreaRect.y = insets.top;
1012 contentRect.width = tabPane.getWidth() - insets.left - insets.right
1013 - tabAreaRect.width;
1014 contentRect.height = tabAreaRect.height;
1015 contentRect.y = insets.top;
1016 if (tabPlacement == SwingConstants.LEFT)
1018 tabAreaRect.x = insets.left;
1019 contentRect.x = tabAreaRect.x + tabAreaRect.width;
1021 else
1023 contentRect.x = insets.left;
1024 tabAreaRect.x = contentRect.x + contentRect.width;
1027 runCount = runs;
1029 padSelectedTab(tabPlacement, tabPane.getSelectedIndex());
1033 * This method is called when the JTabbedPane is laid out in
1034 * SCROLL_TAB_LAYOUT. It finds the position for all components in the
1035 * JTabbedPane.
1037 * @param pane The JTabbedPane to be laid out.
1039 public void layoutContainer(Container pane)
1041 super.layoutContainer(pane);
1042 int tabCount = tabPane.getTabCount();
1043 Point p = null;
1044 if (tabCount == 0)
1045 return;
1046 int tabPlacement = tabPane.getTabPlacement();
1047 incrButton.setVisible(false);
1048 decrButton.setVisible(false);
1049 if (tabPlacement == SwingConstants.TOP
1050 || tabPlacement == SwingConstants.BOTTOM)
1052 if (tabAreaRect.x + tabAreaRect.width < rects[tabCount - 1].x
1053 + rects[tabCount - 1].width)
1055 Dimension incrDims = incrButton.getPreferredSize();
1056 Dimension decrDims = decrButton.getPreferredSize();
1058 decrButton.setBounds(tabAreaRect.x + tabAreaRect.width
1059 - incrDims.width - decrDims.width,
1060 tabAreaRect.y, decrDims.width,
1061 tabAreaRect.height);
1062 incrButton.setBounds(tabAreaRect.x + tabAreaRect.width
1063 - incrDims.width, tabAreaRect.y,
1064 decrDims.width, tabAreaRect.height);
1066 tabAreaRect.width -= decrDims.width + incrDims.width;
1067 incrButton.setVisible(true);
1068 decrButton.setVisible(true);
1072 if (tabPlacement == SwingConstants.LEFT
1073 || tabPlacement == SwingConstants.RIGHT)
1075 if (tabAreaRect.y + tabAreaRect.height < rects[tabCount - 1].y
1076 + rects[tabCount - 1].height)
1078 Dimension incrDims = incrButton.getPreferredSize();
1079 Dimension decrDims = decrButton.getPreferredSize();
1081 decrButton.setBounds(tabAreaRect.x,
1082 tabAreaRect.y + tabAreaRect.height
1083 - incrDims.height - decrDims.height,
1084 tabAreaRect.width, decrDims.height);
1085 incrButton.setBounds(tabAreaRect.x,
1086 tabAreaRect.y + tabAreaRect.height
1087 - incrDims.height, tabAreaRect.width,
1088 incrDims.height);
1090 tabAreaRect.height -= decrDims.height + incrDims.height;
1091 incrButton.setVisible(true);
1092 decrButton.setVisible(true);
1095 viewport.setBounds(tabAreaRect.x, tabAreaRect.y, tabAreaRect.width,
1096 tabAreaRect.height);
1097 int tabC = tabPane.getTabCount() - 1;
1098 if (tabCount > 0)
1100 int w = Math.max(rects[tabC].width + rects[tabC].x, tabAreaRect.width);
1101 int h = Math.max(rects[tabC].height, tabAreaRect.height);
1102 p = findPointForIndex(currentScrollLocation);
1104 // we want to cover that entire space so that borders that run under
1105 // the tab area don't show up when we move the viewport around.
1106 panel.setSize(w + p.x, h + p.y);
1108 viewport.setViewPosition(p);
1109 viewport.repaint();
1114 * This class handles ChangeEvents from the JTabbedPane.
1116 * @specnote Apparently this class was intended to be protected,
1117 * but was made public by a compiler bug and is now
1118 * public for compatibility.
1120 public class TabSelectionHandler implements ChangeListener
1123 * This method is called whenever a ChangeEvent is fired from the
1124 * JTabbedPane.
1126 * @param e The ChangeEvent fired.
1128 public void stateChanged(ChangeEvent e)
1130 selectedRun = getRunForTab(tabPane.getTabCount(),
1131 tabPane.getSelectedIndex());
1132 tabPane.revalidate();
1133 tabPane.repaint();
1138 * This helper class is a JPanel that fits inside the ScrollViewport. This
1139 * panel's sole job is to paint the tab rectangles inside the viewport so
1140 * that it's clipped correctly.
1142 private class ScrollingPanel extends JPanel
1145 * This is a private UI class for our panel.
1147 private class ScrollingPanelUI extends BasicPanelUI
1150 * This method overrides the default paint method. It paints the tab
1151 * rectangles for the JTabbedPane in the panel.
1153 * @param g The Graphics object to paint with.
1154 * @param c The JComponent to paint.
1156 public void paint(Graphics g, JComponent c)
1158 paintTabArea(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex());
1163 * This method overrides the updateUI method. It makes the default UI for
1164 * this ScrollingPanel to be a ScrollingPanelUI.
1166 public void updateUI()
1168 setUI((PanelUI) new ScrollingPanelUI());
1173 * This is a helper class that paints the panel that paints tabs. This
1174 * custom JViewport is used so that the tabs painted in the panel will be
1175 * clipped. This class implements UIResource so tabs are not added when
1176 * this objects of this class are added to the JTabbedPane.
1178 private class ScrollingViewport extends JViewport implements UIResource
1180 // TODO: Maybe remove this inner class.
1184 * This is a helper class that implements UIResource so it is not added as a
1185 * tab when an object of this class is added to the JTabbedPane.
1187 private class ScrollingButton extends BasicArrowButton implements UIResource
1190 * Creates a ScrollingButton given the direction.
1192 * @param dir The direction to point in.
1194 public ScrollingButton(int dir)
1196 super(dir);
1200 /** The button that increments the current scroll location.
1201 * This is package-private to avoid an accessor method. */
1202 transient ScrollingButton incrButton;
1204 /** The button that decrements the current scroll location.
1205 * This is package-private to avoid an accessor method. */
1206 transient ScrollingButton decrButton;
1208 /** The viewport used to display the tabs.
1209 * This is package-private to avoid an accessor method. */
1210 transient ScrollingViewport viewport;
1212 /** The panel inside the viewport that paints the tabs.
1213 * This is package-private to avoid an accessor method. */
1214 transient ScrollingPanel panel;
1216 /** The starting visible tab in the run in SCROLL_TAB_MODE.
1217 * This is package-private to avoid an accessor method. */
1218 transient int currentScrollLocation;
1220 /** A reusable rectangle. */
1221 protected Rectangle calcRect;
1223 /** An array of Rectangles keeping track of the tabs' area and position. */
1224 protected Rectangle[] rects;
1226 /** The insets around the content area. */
1227 protected Insets contentBorderInsets;
1229 /** The extra insets around the selected tab. */
1230 protected Insets selectedTabPadInsets;
1232 /** The insets around the tab area. */
1233 protected Insets tabAreaInsets;
1235 /** The insets around each and every tab. */
1236 protected Insets tabInsets;
1239 * The outer bottom and right edge color for both the tab and content
1240 * border.
1242 protected Color darkShadow;
1244 /** The color of the focus outline on the selected tab. */
1245 protected Color focus;
1247 /** FIXME: find a use for this. */
1248 protected Color highlight;
1250 /** The top and left edge color for both the tab and content border. */
1251 protected Color lightHighlight;
1253 /** The inner bottom and right edge color for the tab and content border. */
1254 protected Color shadow;
1256 /** The maximum tab height. */
1257 protected int maxTabHeight;
1259 /** The maximum tab width. */
1260 protected int maxTabWidth;
1262 /** The number of runs in the JTabbedPane. */
1263 protected int runCount;
1265 /** The index of the run that the selected index is in. */
1266 protected int selectedRun;
1268 /** The amount of space each run overlaps the previous by. */
1269 protected int tabRunOverlay;
1271 /** The gap between text and label */
1272 protected int textIconGap;
1274 // Keeps track of tab runs.
1275 // The organization of this array is as follows (lots of experimentation to
1276 // figure this out)
1277 // index 0 = furthest away from the component area (aka outer run)
1278 // index 1 = closest to component area (aka selected run)
1279 // index > 1 = listed in order leading from selected run to outer run.
1280 // each int in the array is the tab index + 1 (counting starts at 1)
1281 // for the last tab in the run. (same as the rects array)
1283 /** This array keeps track of which tabs are in which run. See above. */
1284 protected int[] tabRuns;
1287 * This is the keystroke for moving down.
1289 * @deprecated 1.3
1291 protected KeyStroke downKey;
1294 * This is the keystroke for moving left.
1296 * @deprecated 1.3
1298 protected KeyStroke leftKey;
1301 * This is the keystroke for moving right.
1303 * @deprecated 1.3
1305 protected KeyStroke rightKey;
1308 * This is the keystroke for moving up.
1310 * @deprecated 1.3
1312 protected KeyStroke upKey;
1314 /** The listener that listens for focus events. */
1315 protected FocusListener focusListener;
1317 /** The listener that listens for mouse events. */
1318 protected MouseListener mouseListener;
1320 /** The listener that listens for property change events. */
1321 protected PropertyChangeListener propertyChangeListener;
1323 /** The listener that listens for change events. */
1324 protected ChangeListener tabChangeListener;
1326 /** The tab pane that this UI paints. */
1327 protected JTabbedPane tabPane;
1329 /** The current layout manager for the tabPane.
1330 * This is package-private to avoid an accessor method. */
1331 transient LayoutManager layoutManager;
1333 /** The rectangle that describes the tab area's position and size.
1334 * This is package-private to avoid an accessor method. */
1335 transient Rectangle tabAreaRect;
1337 /** The rectangle that describes the content area's position and
1338 * size. This is package-private to avoid an accessor method. */
1339 transient Rectangle contentRect;
1342 * Creates a new BasicTabbedPaneUI object.
1344 public BasicTabbedPaneUI()
1346 super();
1347 rects = new Rectangle[0];
1348 tabRuns = new int[10];
1352 * This method creates a ScrollingButton that points in the appropriate
1353 * direction for an increasing button.
1354 * This is package-private to avoid an accessor method.
1356 * @return The increase ScrollingButton.
1358 ScrollingButton createIncreaseButton()
1360 if (incrButton == null)
1361 incrButton = new ScrollingButton(SwingConstants.NORTH);
1362 if (tabPane.getTabPlacement() == SwingConstants.TOP
1363 || tabPane.getTabPlacement() == SwingConstants.BOTTOM)
1364 incrButton.setDirection(SwingConstants.EAST);
1365 else
1366 incrButton.setDirection(SwingConstants.SOUTH);
1367 return incrButton;
1371 * This method creates a ScrollingButton that points in the appropriate
1372 * direction for a decreasing button.
1373 * This is package-private to avoid an accessor method.
1375 * @return The decrease ScrollingButton.
1377 ScrollingButton createDecreaseButton()
1379 if (decrButton == null)
1380 decrButton = new ScrollingButton(SwingConstants.SOUTH);
1381 if (tabPane.getTabPlacement() == SwingConstants.TOP
1382 || tabPane.getTabPlacement() == SwingConstants.BOTTOM)
1383 decrButton.setDirection(SwingConstants.WEST);
1384 else
1385 decrButton.setDirection(SwingConstants.NORTH);
1386 return decrButton;
1390 * This method finds the point to set the view position at given the index
1391 * of a tab. The tab will be the first visible tab in the run.
1392 * This is package-private to avoid an accessor method.
1394 * @param index The index of the first visible tab.
1396 * @return The position of the first visible tab.
1398 Point findPointForIndex(int index)
1400 int tabPlacement = tabPane.getTabPlacement();
1401 int selectedIndex = tabPane.getSelectedIndex();
1402 Insets insets = getSelectedTabPadInsets(tabPlacement);
1403 int w = 0;
1404 int h = 0;
1406 if (tabPlacement == TOP || tabPlacement == BOTTOM)
1408 if (index > 0)
1410 w += rects[index - 1].x + rects[index - 1].width;
1411 if (index > selectedIndex)
1412 w -= insets.left + insets.right;
1416 else
1418 if (index > 0)
1420 h += rects[index - 1].y + rects[index - 1].height;
1421 if (index > selectedIndex)
1422 h -= insets.top + insets.bottom;
1426 Point p = new Point(w, h);
1427 return p;
1431 * This method creates a new BasicTabbedPaneUI.
1433 * @param c The JComponent to create a UI for.
1435 * @return A new BasicTabbedPaneUI.
1437 public static ComponentUI createUI(JComponent c)
1439 return new BasicTabbedPaneUI();
1443 * This method installs the UI for the given JComponent.
1445 * @param c The JComponent to install the UI for.
1447 public void installUI(JComponent c)
1449 super.installUI(c);
1450 if (c instanceof JTabbedPane)
1452 tabPane = (JTabbedPane) c;
1454 installComponents();
1455 installDefaults();
1456 installListeners();
1457 installKeyboardActions();
1459 layoutManager = createLayoutManager();
1460 tabPane.setLayout(layoutManager);
1465 * This method uninstalls the UI for the given JComponent.
1467 * @param c The JComponent to uninstall the UI for.
1469 public void uninstallUI(JComponent c)
1471 layoutManager = null;
1473 uninstallKeyboardActions();
1474 uninstallListeners();
1475 uninstallDefaults();
1476 uninstallComponents();
1478 tabPane = null;
1482 * This method creates the appropriate layout manager for the JTabbedPane's
1483 * current tab layout policy. If the tab layout policy is
1484 * SCROLL_TAB_LAYOUT, then all the associated components that need to be
1485 * created will be done so now.
1487 * @return A layout manager given the tab layout policy.
1489 protected LayoutManager createLayoutManager()
1491 if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT)
1492 return new TabbedPaneLayout();
1493 else
1495 incrButton = createIncreaseButton();
1496 decrButton = createDecreaseButton();
1497 viewport = new ScrollingViewport();
1498 viewport.setLayout(null);
1499 panel = new ScrollingPanel();
1500 viewport.setView(panel);
1501 tabPane.add(incrButton);
1502 tabPane.add(decrButton);
1503 tabPane.add(viewport);
1504 currentScrollLocation = 0;
1505 decrButton.setEnabled(false);
1506 panel.addMouseListener(mouseListener);
1507 incrButton.addMouseListener(mouseListener);
1508 decrButton.addMouseListener(mouseListener);
1509 viewport.setBackground(Color.LIGHT_GRAY);
1511 return new TabbedPaneScrollLayout();
1516 * This method installs components for this JTabbedPane.
1518 protected void installComponents()
1520 // Nothing to be done.
1524 * This method uninstalls components for this JTabbedPane.
1526 protected void uninstallComponents()
1528 // Nothing to be done.
1532 * This method installs defaults for the Look and Feel.
1534 protected void installDefaults()
1536 LookAndFeel.installColorsAndFont(tabPane, "TabbedPane.background",
1537 "TabbedPane.foreground",
1538 "TabbedPane.font");
1539 tabPane.setOpaque(false);
1541 highlight = UIManager.getColor("TabbedPane.highlight");
1542 lightHighlight = UIManager.getColor("TabbedPane.lightHighlight");
1544 shadow = UIManager.getColor("TabbedPane.shadow");
1545 darkShadow = UIManager.getColor("TabbedPane.darkShadow");
1547 focus = UIManager.getColor("TabbedPane.focus");
1549 textIconGap = UIManager.getInt("TabbedPane.textIconGap");
1550 tabRunOverlay = UIManager.getInt("TabbedPane.tabRunOverlay");
1552 tabInsets = UIManager.getInsets("TabbedPane.tabInsets");
1553 selectedTabPadInsets = UIManager.getInsets("TabbedPane.tabbedPaneTabPadInsets");
1554 tabAreaInsets = UIManager.getInsets("TabbedPane.tabAreaInsets");
1555 contentBorderInsets = UIManager.getInsets("TabbedPane.tabbedPaneContentBorderInsets");
1557 calcRect = new Rectangle();
1558 tabRuns = new int[10];
1559 tabAreaRect = new Rectangle();
1560 contentRect = new Rectangle();
1564 * This method uninstalls defaults for the Look and Feel.
1566 protected void uninstallDefaults()
1568 calcRect = null;
1569 tabAreaRect = null;
1570 contentRect = null;
1571 tabRuns = null;
1573 contentBorderInsets = null;
1574 tabAreaInsets = null;
1575 selectedTabPadInsets = null;
1576 tabInsets = null;
1578 focus = null;
1579 darkShadow = null;
1580 shadow = null;
1581 lightHighlight = null;
1582 highlight = null;
1584 tabPane.setBackground(null);
1585 tabPane.setForeground(null);
1586 tabPane.setFont(null);
1590 * This method creates and installs the listeners for this UI.
1592 protected void installListeners()
1594 mouseListener = createMouseListener();
1595 tabChangeListener = createChangeListener();
1596 propertyChangeListener = createPropertyChangeListener();
1597 focusListener = createFocusListener();
1599 tabPane.addMouseListener(mouseListener);
1600 tabPane.addChangeListener(tabChangeListener);
1601 tabPane.addPropertyChangeListener(propertyChangeListener);
1602 tabPane.addFocusListener(focusListener);
1606 * This method removes and nulls the listeners for this UI.
1608 protected void uninstallListeners()
1610 tabPane.removeFocusListener(focusListener);
1611 tabPane.removePropertyChangeListener(propertyChangeListener);
1612 tabPane.removeChangeListener(tabChangeListener);
1613 tabPane.removeMouseListener(mouseListener);
1615 focusListener = null;
1616 propertyChangeListener = null;
1617 tabChangeListener = null;
1618 mouseListener = null;
1622 * This method creates a new MouseListener.
1624 * @return A new MouseListener.
1626 protected MouseListener createMouseListener()
1628 return new MouseHandler();
1632 * This method creates a new FocusListener.
1634 * @return A new FocusListener.
1636 protected FocusListener createFocusListener()
1638 return new FocusHandler();
1642 * This method creates a new ChangeListener.
1644 * @return A new ChangeListener.
1646 protected ChangeListener createChangeListener()
1648 return new TabSelectionHandler();
1652 * This method creates a new PropertyChangeListener.
1654 * @return A new PropertyChangeListener.
1656 protected PropertyChangeListener createPropertyChangeListener()
1658 return new PropertyChangeHandler();
1662 * This method installs keyboard actions for the JTabbedPane.
1664 protected void installKeyboardActions()
1666 // FIXME: Implement.
1670 * This method uninstalls keyboard actions for the JTabbedPane.
1672 protected void uninstallKeyboardActions()
1674 // FIXME: Implement.
1678 * This method returns the minimum size of the JTabbedPane.
1680 * @param c The JComponent to find a size for.
1682 * @return The minimum size.
1684 public Dimension getMinimumSize(JComponent c)
1686 return layoutManager.minimumLayoutSize(tabPane);
1690 * This method returns the maximum size of the JTabbedPane.
1692 * @param c The JComponent to find a size for.
1694 * @return The maximum size.
1696 public Dimension getMaximumSize(JComponent c)
1698 return new Dimension(Short.MAX_VALUE, Short.MAX_VALUE);
1702 * This method paints the JTabbedPane.
1704 * @param g The Graphics object to paint with.
1705 * @param c The JComponent to paint.
1707 public void paint(Graphics g, JComponent c)
1709 if (tabPane.getTabCount() == 0)
1710 return;
1711 if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT)
1712 paintTabArea(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex());
1713 paintContentBorder(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex());
1717 * This method paints the tab area. This includes painting the rectangles
1718 * that make up the tabs.
1720 * @param g The Graphics object to paint with.
1721 * @param tabPlacement The JTabbedPane's tab placement.
1722 * @param selectedIndex The selected index.
1724 protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex)
1726 Rectangle ir = new Rectangle();
1727 Rectangle tr = new Rectangle();
1729 boolean isScroll = tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT;
1731 // Please note: the ordering of the painting is important.
1732 // we WANT to paint the outermost run first and then work our way in.
1733 int tabCount = tabPane.getTabCount();
1734 int currRun = 1;
1736 if (tabCount > runCount)
1737 runCount = tabCount;
1739 if (tabCount < 1)
1740 return;
1742 if (runCount > 1)
1743 currRun = 0;
1744 for (int i = 0; i < runCount; i++)
1746 int first = lastTabInRun(tabCount, getPreviousTabRun(currRun)) + 1;
1747 if (isScroll)
1748 first = currentScrollLocation;
1749 else if (first == tabCount)
1750 first = 0;
1751 int last = lastTabInRun(tabCount, currRun);
1752 if (isScroll)
1754 for (int k = first; k < tabCount; k++)
1756 if (rects[k].x + rects[k].width - rects[first].x > viewport
1757 .getWidth())
1759 last = k;
1760 break;
1765 for (int j = first; j <= last; j++)
1767 if (j != selectedIndex || isScroll)
1768 paintTab(g, tabPlacement, rects, j, ir, tr);
1770 currRun = getPreviousTabRun(currRun);
1772 if (! isScroll)
1773 paintTab(g, tabPlacement, rects, selectedIndex, ir, tr);
1777 * This method paints an individual tab.
1779 * @param g The Graphics object to paint with.
1780 * @param tabPlacement The JTabbedPane's tab placement.
1781 * @param rects The array of rectangles that keep the size and position of
1782 * the tabs.
1783 * @param tabIndex The tab index to paint.
1784 * @param iconRect The rectangle to use for the icon.
1785 * @param textRect The rectangle to use for the text.
1787 protected void paintTab(Graphics g, int tabPlacement, Rectangle[] rects,
1788 int tabIndex, Rectangle iconRect, Rectangle textRect)
1790 FontMetrics fm = getFontMetrics();
1791 Icon icon = getIconForTab(tabIndex);
1792 String title = tabPane.getTitleAt(tabIndex);
1793 boolean isSelected = tabIndex == tabPane.getSelectedIndex();
1794 calcRect = getTabBounds(tabPane, tabIndex);
1796 int x = calcRect.x;
1797 int y = calcRect.y;
1798 int w = calcRect.width;
1799 int h = calcRect.height;
1800 if (getRunForTab(tabPane.getTabCount(), tabIndex) == 1)
1802 Insets insets = getTabAreaInsets(tabPlacement);
1803 switch (tabPlacement)
1805 case TOP:
1806 h += insets.bottom;
1807 break;
1808 case LEFT:
1809 w += insets.right;
1810 break;
1811 case BOTTOM:
1812 y -= insets.top;
1813 h += insets.top;
1814 break;
1815 case RIGHT:
1816 x -= insets.left;
1817 w += insets.left;
1818 break;
1822 layoutLabel(tabPlacement, fm, tabIndex, title, icon, calcRect, iconRect,
1823 textRect, isSelected);
1824 paintTabBackground(g, tabPlacement, tabIndex, x, y, w, h, isSelected);
1825 paintTabBorder(g, tabPlacement, tabIndex, x, y, w, h, isSelected);
1827 // FIXME: Paint little folding corner and jagged edge clipped tab.
1828 if (icon != null)
1829 paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected);
1830 if (title != null && ! title.equals(""))
1831 paintText(g, tabPlacement, tabPane.getFont(), fm, tabIndex, title,
1832 textRect, isSelected);
1836 * This method lays out the tab and finds the location to paint the icon
1837 * and text.
1839 * @param tabPlacement The JTabbedPane's tab placement.
1840 * @param metrics The font metrics for the font to paint with.
1841 * @param tabIndex The tab index to paint.
1842 * @param title The string painted.
1843 * @param icon The icon painted.
1844 * @param tabRect The tab bounds.
1845 * @param iconRect The calculated icon bounds.
1846 * @param textRect The calculated text bounds.
1847 * @param isSelected Whether this tab is selected.
1849 protected void layoutLabel(int tabPlacement, FontMetrics metrics,
1850 int tabIndex, String title, Icon icon,
1851 Rectangle tabRect, Rectangle iconRect,
1852 Rectangle textRect, boolean isSelected)
1854 SwingUtilities.layoutCompoundLabel(metrics, title, icon,
1855 SwingConstants.CENTER,
1856 SwingConstants.CENTER,
1857 SwingConstants.CENTER,
1858 SwingConstants.RIGHT, tabRect,
1859 iconRect, textRect, textIconGap);
1861 int shiftX = getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
1862 int shiftY = getTabLabelShiftY(tabPlacement, tabIndex, isSelected);
1864 iconRect.x += shiftX;
1865 iconRect.y += shiftY;
1867 textRect.x += shiftX;
1868 textRect.y += shiftY;
1872 * This method paints the icon.
1874 * @param g The Graphics object to paint.
1875 * @param tabPlacement The JTabbedPane's tab placement.
1876 * @param tabIndex The tab index to paint.
1877 * @param icon The icon to paint.
1878 * @param iconRect The bounds of the icon.
1879 * @param isSelected Whether this tab is selected.
1881 protected void paintIcon(Graphics g, int tabPlacement, int tabIndex,
1882 Icon icon, Rectangle iconRect, boolean isSelected)
1884 if (icon != null)
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);
1908 return;
1911 Color fg = tabPane.getForegroundAt(tabIndex);
1912 if (fg == null)
1913 fg = tabPane.getForeground();
1914 Color bg = tabPane.getBackgroundAt(tabIndex);
1915 if (bg == null)
1916 bg = tabPane.getBackground();
1918 Color saved_color = g.getColor();
1919 Font f = g.getFont();
1920 g.setFont(font);
1922 if (tabPane.isEnabledAt(tabIndex))
1924 g.setColor(fg);
1926 int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
1928 if (mnemIndex != -1)
1929 BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
1930 textRect.x,
1931 textRect.y
1932 + metrics.getAscent());
1933 else
1934 g.drawString(title, textRect.x, textRect.y + metrics.getAscent());
1936 else
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);
1945 else
1946 g.drawString(title, textRect.x, textRect.y);
1948 g.setColor(bg.darker());
1949 if (mnemIndex != -1)
1950 BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
1951 textRect.x + 1,
1952 textRect.y + 1);
1953 else
1954 g.drawString(title, textRect.x + 1, textRect.y + 1);
1957 g.setColor(saved_color);
1958 g.setFont(f);
1962 * This method returns how much the label for the tab should shift in the X
1963 * direction.
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,
1972 boolean isSelected)
1974 // No reason to shift.
1975 return 0;
1979 * This method returns how much the label for the tab should shift in the Y
1980 * direction.
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,
1989 boolean isSelected)
1991 // No reason to shift.
1992 return 0;
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,
2009 boolean isSelected)
2011 Color saved = g.getColor();
2012 calcRect = iconRect.union(textRect);
2014 g.setColor(focus);
2016 g.drawRect(calcRect.x, calcRect.y, calcRect.width, calcRect.height);
2018 g.setColor(saved);
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)
2040 g.setColor(shadow);
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);
2050 g.setColor(shadow);
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);
2066 g.setColor(saved);
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,
2083 boolean isSelected)
2085 Color saved = g.getColor();
2086 if (isSelected)
2087 g.setColor(Color.LIGHT_GRAY);
2088 else
2090 Color bg = tabPane.getBackgroundAt(tabIndex);
2091 if (bg == null)
2092 bg = Color.GRAY;
2093 g.setColor(bg);
2096 g.fillRect(x, y, w, h);
2098 g.setColor(saved);
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,
2109 int selectedIndex)
2111 int x = contentRect.x;
2112 int y = contentRect.y;
2113 int w = contentRect.width;
2114 int h = contentRect.height;
2115 paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2116 paintContentBorderLeftEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2117 paintContentBorderBottomEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2118 paintContentBorderRightEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2122 * This method paints the top edge of the content border.
2124 * @param g The Graphics object to paint with.
2125 * @param tabPlacement The JTabbedPane's tab placement.
2126 * @param selectedIndex The selected tab index.
2127 * @param x The x coordinate for the content area.
2128 * @param y The y coordinate for the content area.
2129 * @param w The width of the content area.
2130 * @param h The height of the content area.
2132 protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
2133 int selectedIndex, int x, int y,
2134 int w, int h)
2136 Color saved = g.getColor();
2137 g.setColor(lightHighlight);
2139 int startgap = rects[selectedIndex].x;
2140 int endgap = rects[selectedIndex].x + rects[selectedIndex].width;
2142 int diff = 0;
2144 if (tabPlacement == SwingConstants.TOP)
2146 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
2148 Point p = findPointForIndex(currentScrollLocation);
2149 diff = p.x;
2152 g.drawLine(x, y, startgap - diff, y);
2153 g.drawLine(endgap - diff, y, x + w, y);
2155 else
2156 g.drawLine(x, y, x + w, y);
2158 g.setColor(saved);
2162 * This method paints the left edge of the content border.
2164 * @param g The Graphics object to paint with.
2165 * @param tabPlacement The JTabbedPane's tab placement.
2166 * @param selectedIndex The selected tab index.
2167 * @param x The x coordinate for the content area.
2168 * @param y The y coordinate for the content area.
2169 * @param w The width of the content area.
2170 * @param h The height of the content area.
2172 protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
2173 int selectedIndex, int x, int y,
2174 int w, int h)
2176 Color saved = g.getColor();
2177 g.setColor(lightHighlight);
2179 int startgap = rects[selectedIndex].y;
2180 int endgap = rects[selectedIndex].y + rects[selectedIndex].height;
2182 int diff = 0;
2184 if (tabPlacement == SwingConstants.LEFT)
2186 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
2188 Point p = findPointForIndex(currentScrollLocation);
2189 diff = p.y;
2192 g.drawLine(x, y, x, startgap - diff);
2193 g.drawLine(x, endgap - diff, x, y + h);
2195 else
2196 g.drawLine(x, y, x, y + h);
2198 g.setColor(saved);
2202 * This method paints the bottom edge of the content border.
2204 * @param g The Graphics object to paint with.
2205 * @param tabPlacement The JTabbedPane's tab placement.
2206 * @param selectedIndex The selected tab index.
2207 * @param x The x coordinate for the content area.
2208 * @param y The y coordinate for the content area.
2209 * @param w The width of the content area.
2210 * @param h The height of the content area.
2212 protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
2213 int selectedIndex, int x, int y,
2214 int w, int h)
2216 Color saved = g.getColor();
2218 int startgap = rects[selectedIndex].x;
2219 int endgap = rects[selectedIndex].x + rects[selectedIndex].width;
2221 int diff = 0;
2223 if (tabPlacement == SwingConstants.BOTTOM)
2225 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
2227 Point p = findPointForIndex(currentScrollLocation);
2228 diff = p.x;
2231 g.setColor(shadow);
2232 g.drawLine(x + 1, y + h - 1, startgap - diff, y + h - 1);
2233 g.drawLine(endgap - diff, y + h - 1, x + w - 1, y + h - 1);
2235 g.setColor(darkShadow);
2236 g.drawLine(x, y + h, startgap - diff, y + h);
2237 g.drawLine(endgap - diff, y + h, x + w, y + h);
2239 else
2241 g.setColor(shadow);
2242 g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1);
2243 g.setColor(darkShadow);
2244 g.drawLine(x, y + h, x + w, y + h);
2247 g.setColor(saved);
2251 * This method paints the right edge of the content border.
2253 * @param g The Graphics object to paint with.
2254 * @param tabPlacement The JTabbedPane's tab placement.
2255 * @param selectedIndex The selected tab index.
2256 * @param x The x coordinate for the content area.
2257 * @param y The y coordinate for the content area.
2258 * @param w The width of the content area.
2259 * @param h The height of the content area.
2261 protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
2262 int selectedIndex, int x, int y,
2263 int w, int h)
2265 Color saved = g.getColor();
2266 int startgap = rects[selectedIndex].y;
2267 int endgap = rects[selectedIndex].y + rects[selectedIndex].height;
2269 int diff = 0;
2271 if (tabPlacement == SwingConstants.RIGHT)
2273 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
2275 Point p = findPointForIndex(currentScrollLocation);
2276 diff = p.y;
2279 g.setColor(shadow);
2280 g.drawLine(x + w - 1, y + 1, x + w - 1, startgap - diff);
2281 g.drawLine(x + w - 1, endgap - diff, x + w - 1, y + h - 1);
2283 g.setColor(darkShadow);
2284 g.drawLine(x + w, y, x + w, startgap - diff);
2285 g.drawLine(x + w, endgap - diff, x + w, y + h);
2287 else
2289 g.setColor(shadow);
2290 g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1);
2291 g.setColor(darkShadow);
2292 g.drawLine(x + w, y, x + w, y + h);
2295 g.setColor(saved);
2299 * This method returns the tab bounds for the given index.
2301 * @param pane The JTabbedPane.
2302 * @param i The index to look for.
2304 * @return The bounds of the tab with the given index.
2306 public Rectangle getTabBounds(JTabbedPane pane, int i)
2308 return rects[i];
2312 * This method returns the number of runs.
2314 * @param pane The JTabbedPane.
2316 * @return The number of runs.
2318 public int getTabRunCount(JTabbedPane pane)
2320 return runCount;
2324 * This method returns the tab index given a coordinate.
2326 * @param pane The JTabbedPane.
2327 * @param x The x coordinate.
2328 * @param y The y coordinate.
2330 * @return The tab index that the coordinate lands in.
2332 public int tabForCoordinate(JTabbedPane pane, int x, int y)
2334 Point p = new Point(x, y);
2335 int tabCount = tabPane.getTabCount();
2336 int currRun = 1;
2337 for (int i = 0; i < runCount; i++)
2339 int first = lastTabInRun(tabCount, getPreviousTabRun(currRun)) + 1;
2340 if (first == tabCount)
2341 first = 0;
2342 int last = lastTabInRun(tabCount, currRun);
2343 for (int j = first; j <= last; j++)
2345 if (getTabBounds(pane, j).contains(p))
2346 return j;
2348 currRun = getNextTabRun(currRun);
2350 return -1;
2354 * This method returns the tab bounds in the given rectangle.
2356 * @param tabIndex The index to get bounds for.
2357 * @param dest The rectangle to store bounds in.
2359 * @return The rectangle passed in.
2361 protected Rectangle getTabBounds(int tabIndex, Rectangle dest)
2363 dest.setBounds(getTabBounds(tabPane, tabIndex));
2364 return dest;
2368 * This method returns the component that is shown in the content area.
2370 * @return The component that is shown in the content area.
2372 protected Component getVisibleComponent()
2374 return tabPane.getComponentAt(tabPane.getSelectedIndex());
2378 * This method sets the visible component.
2380 * @param component The component to be set visible.
2382 protected void setVisibleComponent(Component component)
2384 component.setVisible(true);
2385 tabPane.setSelectedComponent(component);
2389 * This method assures that enough rectangles are created given the
2390 * tabCount. The old array is copied to the new one.
2392 * @param tabCount The number of tabs.
2394 protected void assureRectsCreated(int tabCount)
2396 if (rects.length < tabCount)
2398 Rectangle[] old = rects;
2399 rects = new Rectangle[tabCount];
2400 System.arraycopy(old, 0, rects, 0, old.length);
2401 for (int i = old.length; i < rects.length; i++)
2402 rects[i] = new Rectangle();
2407 * This method expands the tabRuns array to give it more room. The old array
2408 * is copied to the new one.
2410 protected void expandTabRunsArray()
2412 // This method adds another 10 index positions to the tabRuns array.
2413 if (tabRuns == null)
2414 tabRuns = new int[10];
2415 else
2417 int[] newRuns = new int[tabRuns.length + 10];
2418 System.arraycopy(tabRuns, 0, newRuns, 0, tabRuns.length);
2419 tabRuns = newRuns;
2424 * This method returns which run a particular tab belongs to.
2426 * @param tabCount The number of tabs.
2427 * @param tabIndex The tab to find.
2429 * @return The tabRuns index that it belongs to.
2431 protected int getRunForTab(int tabCount, int tabIndex)
2433 if (runCount == 1 && tabIndex < tabCount && tabIndex >= 0)
2434 return 1;
2435 for (int i = 0; i < runCount; i++)
2437 int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1;
2438 if (first == tabCount)
2439 first = 0;
2440 int last = lastTabInRun(tabCount, i);
2441 if (last >= tabIndex && first <= tabIndex)
2442 return i;
2444 return -1;
2448 * This method returns the index of the last tab in a run.
2450 * @param tabCount The number of tabs.
2451 * @param run The run to check.
2453 * @return The last tab in the given run.
2455 protected int lastTabInRun(int tabCount, int run)
2457 if (tabRuns[run] == 0)
2458 return tabCount - 1;
2459 else
2460 return tabRuns[run] - 1;
2464 * This method returns the tab run overlay.
2466 * @param tabPlacement The JTabbedPane's tab placement.
2468 * @return The tab run overlay.
2470 protected int getTabRunOverlay(int tabPlacement)
2472 return tabRunOverlay;
2476 * This method returns the tab run indent. It is used in WRAP_TAB_LAYOUT and
2477 * makes each tab run start indented by a certain amount.
2479 * @param tabPlacement The JTabbedPane's tab placement.
2480 * @param run The run to get indent for.
2482 * @return The amount a run should be indented.
2484 protected int getTabRunIndent(int tabPlacement, int run)
2486 return 0;
2490 * This method returns whether a tab run should be padded.
2492 * @param tabPlacement The JTabbedPane's tab placement.
2493 * @param run The run to check.
2495 * @return Whether the given run should be padded.
2497 protected boolean shouldPadTabRun(int tabPlacement, int run)
2499 return true;
2503 * This method returns whether the tab runs should be rotated.
2505 * @param tabPlacement The JTabbedPane's tab placement.
2507 * @return Whether runs should be rotated.
2509 protected boolean shouldRotateTabRuns(int tabPlacement)
2511 return true;
2515 * This method returns an icon for the tab. If the tab is disabled, it
2516 * should return the disabledIcon. If it is enabled, then it should return
2517 * the default icon.
2519 * @param tabIndex The tab index to get an icon for.
2521 * @return The icon for the tab index.
2523 protected Icon getIconForTab(int tabIndex)
2525 if (tabPane.isEnabledAt(tabIndex))
2526 return tabPane.getIconAt(tabIndex);
2527 else
2528 return tabPane.getDisabledIconAt(tabIndex);
2532 * This method returns a view that can paint the text for the label.
2534 * @param tabIndex The tab index to get a view for.
2536 * @return The view for the tab index.
2538 protected View getTextViewForTab(int tabIndex)
2540 return null;
2544 * This method returns the tab height, including insets, for the given index
2545 * and fontheight.
2547 * @param tabPlacement The JTabbedPane's tab placement.
2548 * @param tabIndex The index of the tab to calculate.
2549 * @param fontHeight The font height.
2551 * @return This tab's height.
2553 protected int calculateTabHeight(int tabPlacement, int tabIndex,
2554 int fontHeight)
2556 Icon icon = getIconForTab(tabIndex);
2557 Insets insets = getTabInsets(tabPlacement, tabIndex);
2559 int height = 0;
2560 if (icon != null)
2562 Rectangle vr = new Rectangle();
2563 Rectangle ir = new Rectangle();
2564 Rectangle tr = new Rectangle();
2565 layoutLabel(tabPlacement, getFontMetrics(), tabIndex,
2566 tabPane.getTitleAt(tabIndex), icon, vr, ir, tr,
2567 tabIndex == tabPane.getSelectedIndex());
2568 height = tr.union(ir).height;
2570 else
2571 height = fontHeight;
2573 height += insets.top + insets.bottom;
2574 return height;
2578 * This method returns the max tab height.
2580 * @param tabPlacement The JTabbedPane's tab placement.
2582 * @return The maximum tab height.
2584 protected int calculateMaxTabHeight(int tabPlacement)
2586 maxTabHeight = 0;
2588 FontMetrics fm = getFontMetrics();
2589 int fontHeight = fm.getHeight();
2591 for (int i = 0; i < tabPane.getTabCount(); i++)
2592 maxTabHeight = Math.max(calculateTabHeight(tabPlacement, i, fontHeight),
2593 maxTabHeight);
2595 return maxTabHeight;
2599 * This method calculates the tab width, including insets, for the given tab
2600 * index and font metrics.
2602 * @param tabPlacement The JTabbedPane's tab placement.
2603 * @param tabIndex The tab index to calculate for.
2604 * @param metrics The font's metrics.
2606 * @return The tab width for the given index.
2608 protected int calculateTabWidth(int tabPlacement, int tabIndex,
2609 FontMetrics metrics)
2611 Icon icon = getIconForTab(tabIndex);
2612 Insets insets = getTabInsets(tabPlacement, tabIndex);
2614 int width = 0;
2615 if (icon != null)
2617 Rectangle vr = new Rectangle();
2618 Rectangle ir = new Rectangle();
2619 Rectangle tr = new Rectangle();
2620 layoutLabel(tabPlacement, getFontMetrics(), tabIndex,
2621 tabPane.getTitleAt(tabIndex), icon, vr, ir, tr,
2622 tabIndex == tabPane.getSelectedIndex());
2623 width = tr.union(ir).width;
2625 else
2626 width = metrics.stringWidth(tabPane.getTitleAt(tabIndex));
2628 width += insets.left + insets.right;
2629 return width;
2633 * This method calculates the max tab width.
2635 * @param tabPlacement The JTabbedPane's tab placement.
2637 * @return The maximum tab width.
2639 protected int calculateMaxTabWidth(int tabPlacement)
2641 maxTabWidth = 0;
2643 FontMetrics fm = getFontMetrics();
2645 for (int i = 0; i < tabPane.getTabCount(); i++)
2646 maxTabWidth = Math.max(calculateTabWidth(tabPlacement, i, fm),
2647 maxTabWidth);
2649 return maxTabWidth;
2653 * This method calculates the tab area height, including insets, for the
2654 * given amount of runs and tab height.
2656 * @param tabPlacement The JTabbedPane's tab placement.
2657 * @param horizRunCount The number of runs.
2658 * @param maxTabHeight The max tab height.
2660 * @return The tab area height.
2662 protected int calculateTabAreaHeight(int tabPlacement, int horizRunCount,
2663 int maxTabHeight)
2665 Insets insets = getTabAreaInsets(tabPlacement);
2666 int tabAreaHeight = horizRunCount * maxTabHeight
2667 - (horizRunCount - 1) * tabRunOverlay;
2669 tabAreaHeight += insets.top + insets.bottom;
2671 return tabAreaHeight;
2675 * This method calculates the tab area width, including insets, for the
2676 * given amount of runs and tab width.
2678 * @param tabPlacement The JTabbedPane's tab placement.
2679 * @param vertRunCount The number of runs.
2680 * @param maxTabWidth The max tab width.
2682 * @return The tab area width.
2684 protected int calculateTabAreaWidth(int tabPlacement, int vertRunCount,
2685 int maxTabWidth)
2687 Insets insets = getTabAreaInsets(tabPlacement);
2688 int tabAreaWidth = vertRunCount * maxTabWidth
2689 - (vertRunCount - 1) * tabRunOverlay;
2691 tabAreaWidth += insets.left + insets.right;
2693 return tabAreaWidth;
2697 * This method returns the tab insets appropriately rotated.
2699 * @param tabPlacement The JTabbedPane's tab placement.
2700 * @param tabIndex The tab index.
2702 * @return The tab insets for the given index.
2704 protected Insets getTabInsets(int tabPlacement, int tabIndex)
2706 Insets target = new Insets(0, 0, 0, 0);
2707 rotateInsets(tabInsets, target, tabPlacement);
2708 return target;
2712 * This method returns the selected tab pad insets appropriately rotated.
2714 * @param tabPlacement The JTabbedPane's tab placement.
2716 * @return The selected tab pad insets.
2718 protected Insets getSelectedTabPadInsets(int tabPlacement)
2720 Insets target = new Insets(0, 0, 0, 0);
2721 rotateInsets(selectedTabPadInsets, target, tabPlacement);
2722 return target;
2726 * This method returns the tab area insets appropriately rotated.
2728 * @param tabPlacement The JTabbedPane's tab placement.
2730 * @return The tab area insets.
2732 protected Insets getTabAreaInsets(int tabPlacement)
2734 Insets target = new Insets(0, 0, 0, 0);
2735 rotateInsets(tabAreaInsets, target, tabPlacement);
2736 return target;
2740 * This method returns the content border insets appropriately rotated.
2742 * @param tabPlacement The JTabbedPane's tab placement.
2744 * @return The content border insets.
2746 protected Insets getContentBorderInsets(int tabPlacement)
2748 Insets target = new Insets(0, 0, 0, 0);
2749 rotateInsets(contentBorderInsets, target, tabPlacement);
2750 return target;
2754 * This method returns the fontmetrics for the font of the JTabbedPane.
2756 * @return The font metrics for the JTabbedPane.
2758 protected FontMetrics getFontMetrics()
2760 FontMetrics fm = tabPane.getFontMetrics(tabPane.getFont());
2761 return fm;
2765 * This method navigates from the selected tab into the given direction. As
2766 * a result, a new tab will be selected (if possible).
2768 * @param direction The direction to navigate in.
2770 protected void navigateSelectedTab(int direction)
2772 int tabPlacement = tabPane.getTabPlacement();
2773 if (tabPlacement == SwingConstants.TOP
2774 || tabPlacement == SwingConstants.BOTTOM)
2776 if (direction == SwingConstants.WEST)
2777 selectPreviousTabInRun(tabPane.getSelectedIndex());
2778 else if (direction == SwingConstants.EAST)
2779 selectNextTabInRun(tabPane.getSelectedIndex());
2781 else
2783 int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(),
2784 tabPane.getSelectedIndex(),
2785 (tabPlacement == SwingConstants.RIGHT)
2786 ? true : false);
2787 selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(),
2788 offset);
2791 if (tabPlacement == SwingConstants.LEFT
2792 || tabPlacement == SwingConstants.RIGHT)
2794 if (direction == SwingConstants.NORTH)
2795 selectPreviousTabInRun(tabPane.getSelectedIndex());
2796 else if (direction == SwingConstants.SOUTH)
2797 selectNextTabInRun(tabPane.getSelectedIndex());
2798 else
2800 int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(),
2801 tabPane.getSelectedIndex(),
2802 (tabPlacement == SwingConstants.RIGHT)
2803 ? true : false);
2804 selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(),
2805 offset);
2811 * This method selects the next tab in the run.
2813 * @param current The current selected index.
2815 protected void selectNextTabInRun(int current)
2817 tabPane.setSelectedIndex(getNextTabIndexInRun(tabPane.getTabCount(),
2818 current));
2822 * This method selects the previous tab in the run.
2824 * @param current The current selected index.
2826 protected void selectPreviousTabInRun(int current)
2828 tabPane.setSelectedIndex(getPreviousTabIndexInRun(tabPane.getTabCount(),
2829 current));
2833 * This method selects the next tab (regardless of runs).
2835 * @param current The current selected index.
2837 protected void selectNextTab(int current)
2839 tabPane.setSelectedIndex(getNextTabIndex(current));
2843 * This method selects the previous tab (regardless of runs).
2845 * @param current The current selected index.
2847 protected void selectPreviousTab(int current)
2849 tabPane.setSelectedIndex(getPreviousTabIndex(current));
2853 * This method selects the correct tab given an offset from the current tab
2854 * index. If the tab placement is TOP or BOTTOM, the offset will be in the
2855 * y direction, otherwise, it will be in the x direction. A new coordinate
2856 * will be found by adding the offset to the current location of the tab.
2857 * The tab that the new location will be selected.
2859 * @param tabPlacement The JTabbedPane's tab placement.
2860 * @param tabIndex The tab to start from.
2861 * @param offset The coordinate offset.
2863 protected void selectAdjacentRunTab(int tabPlacement, int tabIndex,
2864 int offset)
2866 int x = rects[tabIndex].x + rects[tabIndex].width / 2;
2867 int y = rects[tabIndex].y + rects[tabIndex].height / 2;
2869 switch (tabPlacement)
2871 case SwingConstants.TOP:
2872 case SwingConstants.BOTTOM:
2873 y += offset;
2874 break;
2875 case SwingConstants.RIGHT:
2876 case SwingConstants.LEFT:
2877 x += offset;
2878 break;
2881 int index = tabForCoordinate(tabPane, x, y);
2882 if (index != -1)
2883 tabPane.setSelectedIndex(index);
2886 // This method is called when you press up/down to cycle through tab runs.
2887 // it returns the distance (between the two runs' x/y position.
2888 // where one run is the current selected run and the other run is the run in the
2889 // direction of the scroll (dictated by the forward flag)
2890 // the offset is an absolute value of the difference
2893 * This method calculates the offset distance for use in
2894 * selectAdjacentRunTab. The offset returned will be a difference in the y
2895 * coordinate between the run in the desired direction and the current run
2896 * (for tabPlacement in TOP or BOTTOM). Use x coordinate for LEFT and
2897 * RIGHT.
2899 * @param tabPlacement The JTabbedPane's tab placement.
2900 * @param tabCount The number of tabs.
2901 * @param tabIndex The starting index.
2902 * @param forward If forward, the run in the desired direction will be the
2903 * next run.
2905 * @return The offset between the two runs.
2907 protected int getTabRunOffset(int tabPlacement, int tabCount, int tabIndex,
2908 boolean forward)
2910 int currRun = getRunForTab(tabCount, tabIndex);
2911 int offset;
2912 int nextRun = (forward) ? getNextTabRun(currRun) : getPreviousTabRun(currRun);
2913 if (tabPlacement == SwingConstants.TOP
2914 || tabPlacement == SwingConstants.BOTTOM)
2915 offset = rects[lastTabInRun(tabCount, nextRun)].y
2916 - rects[lastTabInRun(tabCount, currRun)].y;
2917 else
2918 offset = rects[lastTabInRun(tabCount, nextRun)].x
2919 - rects[lastTabInRun(tabCount, currRun)].x;
2920 return offset;
2924 * This method returns the previous tab index.
2926 * @param base The index to start from.
2928 * @return The previous tab index.
2930 protected int getPreviousTabIndex(int base)
2932 base--;
2933 if (base < 0)
2934 return tabPane.getTabCount() - 1;
2935 return base;
2939 * This method returns the next tab index.
2941 * @param base The index to start from.
2943 * @return The next tab index.
2945 protected int getNextTabIndex(int base)
2947 base++;
2948 if (base == tabPane.getTabCount())
2949 return 0;
2950 return base;
2954 * This method returns the next tab index in the run. If the next index is
2955 * out of this run, it will return the starting tab index for the run.
2957 * @param tabCount The number of tabs.
2958 * @param base The index to start from.
2960 * @return The next tab index in the run.
2962 protected int getNextTabIndexInRun(int tabCount, int base)
2964 int index = getNextTabIndex(base);
2965 int run = getRunForTab(tabCount, base);
2966 if (index == lastTabInRun(tabCount, run) + 1)
2967 index = lastTabInRun(tabCount, getPreviousTabRun(run)) + 1;
2968 return getNextTabIndex(base);
2972 * This method returns the previous tab index in the run. If the previous
2973 * index is out of this run, it will return the last index for the run.
2975 * @param tabCount The number of tabs.
2976 * @param base The index to start from.
2978 * @return The previous tab index in the run.
2980 protected int getPreviousTabIndexInRun(int tabCount, int base)
2982 int index = getPreviousTabIndex(base);
2983 int run = getRunForTab(tabCount, base);
2984 if (index == lastTabInRun(tabCount, getPreviousTabRun(run)))
2985 index = lastTabInRun(tabCount, run);
2986 return getPreviousTabIndex(base);
2990 * This method returns the index of the previous run.
2992 * @param baseRun The run to start from.
2994 * @return The index of the previous run.
2996 protected int getPreviousTabRun(int baseRun)
2998 if (getTabRunCount(tabPane) == 1)
2999 return 1;
3001 int prevRun = --baseRun;
3002 if (prevRun < 0)
3003 prevRun = getTabRunCount(tabPane) - 1;
3004 return prevRun;
3008 * This method returns the index of the next run.
3010 * @param baseRun The run to start from.
3012 * @return The index of the next run.
3014 protected int getNextTabRun(int baseRun)
3016 if (getTabRunCount(tabPane) == 1)
3017 return 1;
3019 int nextRun = ++baseRun;
3020 if (nextRun == getTabRunCount(tabPane))
3021 nextRun = 0;
3022 return nextRun;
3026 * This method rotates the insets given a direction to rotate them in.
3027 * Target placement should be one of TOP, LEFT, BOTTOM, RIGHT. The rotated
3028 * insets will be stored in targetInsets. Passing in TOP as the direction
3029 * does nothing. Passing in LEFT switches top and left, right and bottom.
3030 * Passing in BOTTOM switches top and bottom. Passing in RIGHT switches top
3031 * for left, left for bottom, bottom for right, and right for top.
3033 * @param topInsets The reference insets.
3034 * @param targetInsets An Insets object to store the new insets.
3035 * @param targetPlacement The rotation direction.
3037 protected static void rotateInsets(Insets topInsets, Insets targetInsets,
3038 int targetPlacement)
3040 // Sun's version will happily throw an NPE if params are null,
3041 // so I won't check it either.
3042 switch (targetPlacement)
3044 case SwingConstants.TOP:
3045 targetInsets.top = topInsets.top;
3046 targetInsets.left = topInsets.left;
3047 targetInsets.right = topInsets.right;
3048 targetInsets.bottom = topInsets.bottom;
3049 break;
3050 case SwingConstants.LEFT:
3051 targetInsets.left = topInsets.top;
3052 targetInsets.top = topInsets.left;
3053 targetInsets.right = topInsets.bottom;
3054 targetInsets.bottom = topInsets.right;
3055 break;
3056 case SwingConstants.BOTTOM:
3057 targetInsets.top = topInsets.bottom;
3058 targetInsets.bottom = topInsets.top;
3059 targetInsets.left = topInsets.left;
3060 targetInsets.right = topInsets.right;
3061 break;
3062 case SwingConstants.RIGHT:
3063 targetInsets.top = topInsets.left;
3064 targetInsets.left = topInsets.bottom;
3065 targetInsets.bottom = topInsets.right;
3066 targetInsets.right = topInsets.top;
3067 break;