Imported GNU Classpath 0.90
[official-gcc.git] / libjava / classpath / javax / swing / plaf / basic / BasicTabbedPaneUI.java
blob5b1e1ff0f6075aab2e30fdf67a2b8c47acdd4674
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;
454 if (runCount > tabRuns.length)
455 expandTabRunsArray();
457 tabRuns[0] = 0;
458 normalizeTabRuns(tabPlacement, tabCount, start, max);
459 selectedRun = getRunForTab(tabCount, tabPane.getSelectedIndex());
460 if (shouldRotateTabRuns(tabPlacement))
461 rotateTabRuns(tabPlacement, selectedRun);
463 // Need to pad the runs and move them to the correct location.
464 for (int i = 0; i < runCount; i++)
466 int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1;
467 if (first == tabCount)
468 first = 0;
469 int last = lastTabInRun(tabCount, i);
470 if (shouldPadTabRun(tabPlacement, i))
471 padTabRun(tabPlacement, first, last, max);
473 // Done padding, now need to move it.
474 if (tabPlacement == SwingConstants.TOP && i > 0)
476 for (int j = first; j <= last; j++)
477 rects[j].y += (runCount - i) * maxTabHeight
478 - (runCount - i) * tabRunOverlay;
481 if (tabPlacement == SwingConstants.BOTTOM)
483 int height = tabPane.getBounds().height - insets.bottom
484 - tabAreaInsets.bottom;
485 int adjustment;
486 if (i == 0)
487 adjustment = height - maxTabHeight;
488 else
489 adjustment = height - (runCount - i + 1) * maxTabHeight
490 - (runCount - i) * tabRunOverlay;
492 for (int j = first; j <= last; j++)
493 rects[j].y = adjustment;
496 if (tabPlacement == SwingConstants.LEFT && i > 0)
498 for (int j = first; j <= last; j++)
499 rects[j].x += (runCount - i) * maxTabWidth
500 - (runCount - i) * tabRunOverlay;
503 if (tabPlacement == SwingConstants.RIGHT)
505 int width = tabPane.getBounds().width - insets.right
506 - tabAreaInsets.right;
507 int adjustment;
508 if (i == 0)
509 adjustment = width - maxTabWidth;
510 else
511 adjustment = width - (runCount - i + 1) * maxTabWidth
512 + (runCount - i) * tabRunOverlay;
514 for (int j = first; j <= last; j++)
515 rects[j].x = adjustment;
518 padSelectedTab(tabPlacement, tabPane.getSelectedIndex());
522 * This method is called when the JTabbedPane is laid out in
523 * WRAP_TAB_LAYOUT. It calls calculateLayoutInfo to find the positions
524 * of all its components.
526 * @param parent The Container to lay out.
528 public void layoutContainer(Container parent)
530 calculateLayoutInfo();
534 * This method returns the minimum layout size for the given container.
536 * @param parent The container that is being sized.
538 * @return The minimum size.
540 public Dimension minimumLayoutSize(Container parent)
542 return calculateSize(false);
545 // If there is more free space in an adjacent run AND the tab in the run can fit in the
546 // adjacent run, move it. This method is not perfect, it is merely an approximation.
547 // If you play around with Sun's JTabbedPane, you'll see that
548 // it does do some pretty strange things with regards to not moving tabs
549 // that should be moved.
550 // start = the x position where the tabs will begin
551 // max = the maximum position of where the tabs can go to (tabAreaInsets.left + the width of the tab area)
554 * This method tries to "even out" the number of tabs in each run based on
555 * their widths.
557 * @param tabPlacement The JTabbedPane's tab placement.
558 * @param tabCount The number of tabs.
559 * @param start The x position where the tabs will begin.
560 * @param max The maximum x position where the tab can run to.
562 protected void normalizeTabRuns(int tabPlacement, int tabCount, int start,
563 int max)
565 Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
566 if (tabPlacement == SwingUtilities.TOP
567 || tabPlacement == SwingUtilities.BOTTOM)
569 // We should only do this for runCount - 1, cause we can only shift that many times between
570 // runs.
571 for (int i = 1; i < runCount; i++)
573 Rectangle currRun = rects[lastTabInRun(tabCount, i)];
574 Rectangle nextRun = rects[lastTabInRun(tabCount, getNextTabRun(i))];
575 int spaceInCurr = currRun.x + currRun.width;
576 int spaceInNext = nextRun.x + nextRun.width;
578 int diffNow = spaceInCurr - spaceInNext;
579 int diffLater = (spaceInCurr - currRun.width)
580 - (spaceInNext + currRun.width);
581 while (Math.abs(diffLater) < Math.abs(diffNow)
582 && spaceInNext + currRun.width < max)
584 tabRuns[i]--;
585 spaceInNext += currRun.width;
586 spaceInCurr -= currRun.width;
587 currRun = rects[lastTabInRun(tabCount, i)];
588 diffNow = spaceInCurr - spaceInNext;
589 diffLater = (spaceInCurr - currRun.width)
590 - (spaceInNext + currRun.width);
593 // Fix the bounds.
594 int first = lastTabInRun(tabCount, i) + 1;
595 int last = lastTabInRun(tabCount, getNextTabRun(i));
596 int currX = tabAreaInsets.left;
597 for (int j = first; j <= last; j++)
599 rects[j].x = currX;
600 currX += rects[j].width;
604 else
606 for (int i = 1; i < runCount; i++)
608 Rectangle currRun = rects[lastTabInRun(tabCount, i)];
609 Rectangle nextRun = rects[lastTabInRun(tabCount, getNextTabRun(i))];
610 int spaceInCurr = currRun.y + currRun.height;
611 int spaceInNext = nextRun.y + nextRun.height;
613 int diffNow = spaceInCurr - spaceInNext;
614 int diffLater = (spaceInCurr - currRun.height)
615 - (spaceInNext + currRun.height);
616 while (Math.abs(diffLater) < Math.abs(diffNow)
617 && spaceInNext + currRun.height < max)
619 tabRuns[i]--;
620 spaceInNext += currRun.height;
621 spaceInCurr -= currRun.height;
622 currRun = rects[lastTabInRun(tabCount, i)];
623 diffNow = spaceInCurr - spaceInNext;
624 diffLater = (spaceInCurr - currRun.height)
625 - (spaceInNext + currRun.height);
628 int first = lastTabInRun(tabCount, i) + 1;
629 int last = lastTabInRun(tabCount, getNextTabRun(i));
630 int currY = tabAreaInsets.top;
631 for (int j = first; j <= last; j++)
633 rects[j].y = currY;
634 currY += rects[j].height;
641 * This method pads the tab at the selected index by the selected tab pad
642 * insets (so that it looks larger).
644 * @param tabPlacement The placement of the tabs.
645 * @param selectedIndex The selected index.
647 protected void padSelectedTab(int tabPlacement, int selectedIndex)
649 Insets insets = getSelectedTabPadInsets(tabPlacement);
650 rects[selectedIndex].x -= insets.left;
651 rects[selectedIndex].y -= insets.top;
652 rects[selectedIndex].width += insets.left + insets.right;
653 rects[selectedIndex].height += insets.top + insets.bottom;
656 // If the tabs on the run don't fill the width of the window, make it fit now.
657 // start = starting index of the run
658 // end = last index of the run
659 // max = tabAreaInsets.left + width (or equivalent)
660 // assert start <= end.
663 * This method makes each tab in the run larger so that the tabs expand
664 * to fill the runs width/height (depending on tabPlacement).
666 * @param tabPlacement The placement of the tabs.
667 * @param start The index of the first tab.
668 * @param end The last index of the tab
669 * @param max The amount of space in the run (width for TOP and BOTTOM
670 * tabPlacement).
672 protected void padTabRun(int tabPlacement, int start, int end, int max)
674 if (tabPlacement == SwingConstants.TOP
675 || tabPlacement == SwingConstants.BOTTOM)
677 int runWidth = rects[end].x + rects[end].width;
678 int spaceRemaining = max - runWidth;
679 int numTabs = end - start + 1;
681 // now divvy up the space.
682 int spaceAllocated = spaceRemaining / numTabs;
683 int currX = rects[start].x;
684 for (int i = start; i <= end; i++)
686 rects[i].x = currX;
687 rects[i].width += spaceAllocated;
688 currX += rects[i].width;
689 // This is used because since the spaceAllocated
690 // variable is an int, it rounds down. Sometimes,
691 // we don't fill an entire row, so we make it do
692 // so now.
693 if (i == end && rects[i].x + rects[i].width != max)
694 rects[i].width = max - rects[i].x;
697 else
699 int runHeight = rects[end].y + rects[end].height;
700 int spaceRemaining = max - runHeight;
701 int numTabs = end - start + 1;
703 int spaceAllocated = spaceRemaining / numTabs;
704 int currY = rects[start].y;
705 for (int i = start; i <= end; i++)
707 rects[i].y = currY;
708 rects[i].height += spaceAllocated;
709 currY += rects[i].height;
710 if (i == end && rects[i].y + rects[i].height != max)
711 rects[i].height = max - rects[i].y;
717 * This method returns the preferred layout size for the given container.
719 * @param parent The container to size.
721 * @return The preferred layout size.
723 public Dimension preferredLayoutSize(Container parent)
725 return calculateSize(false);
729 * This method returns the preferred tab height given a tabPlacement and
730 * width.
732 * @param tabPlacement The JTabbedPane's tab placement.
733 * @param width The expected width.
735 * @return The preferred tab area height.
737 protected int preferredTabAreaHeight(int tabPlacement, int width)
739 if (tabPane.getTabCount() == 0)
740 return calculateTabAreaHeight(tabPlacement, 0, 0);
742 int runs = 0;
743 int runWidth = 0;
744 int tabWidth = 0;
746 FontMetrics fm = getFontMetrics();
748 Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
749 Insets insets = tabPane.getInsets();
751 // Only interested in width, this is a messed up rectangle now.
752 width -= tabAreaInsets.left + tabAreaInsets.right + insets.left
753 + insets.right;
755 // The reason why we can't use runCount:
756 // This method is only called to calculate the size request
757 // for the tabbedPane. However, this size request is dependent on
758 // our desired width. We need to find out what the height would
759 // be IF we got our desired width.
760 for (int i = 0; i < tabPane.getTabCount(); i++)
762 tabWidth = calculateTabWidth(tabPlacement, i, fm);
763 if (runWidth + tabWidth > width)
765 runWidth = tabWidth;
766 runs++;
768 else
769 runWidth += tabWidth;
771 runs++;
773 int maxTabHeight = calculateMaxTabHeight(tabPlacement);
774 int tabAreaHeight = calculateTabAreaHeight(tabPlacement, runs,
775 maxTabHeight);
776 return tabAreaHeight;
780 * This method calculates the preferred tab area width given a tab
781 * placement and height.
783 * @param tabPlacement The JTabbedPane's tab placement.
784 * @param height The expected height.
786 * @return The preferred tab area width.
788 protected int preferredTabAreaWidth(int tabPlacement, int height)
790 if (tabPane.getTabCount() == 0)
791 return calculateTabAreaHeight(tabPlacement, 0, 0);
793 int runs = 0;
794 int runHeight = 0;
795 int tabHeight = 0;
797 FontMetrics fm = getFontMetrics();
799 Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
800 Insets insets = tabPane.getInsets();
802 height -= tabAreaInsets.top + tabAreaInsets.bottom + insets.top
803 + insets.bottom;
804 int fontHeight = fm.getHeight();
806 for (int i = 0; i < tabPane.getTabCount(); i++)
808 tabHeight = calculateTabHeight(tabPlacement, i, fontHeight);
809 if (runHeight + tabHeight > height)
811 runHeight = tabHeight;
812 runs++;
814 else
815 runHeight += tabHeight;
817 runs++;
819 int maxTabWidth = calculateMaxTabWidth(tabPlacement);
820 int tabAreaWidth = calculateTabAreaWidth(tabPlacement, runs, maxTabWidth);
821 return tabAreaWidth;
825 * This method rotates the places each run in the correct place the
826 * tabRuns array. See the comment for tabRuns for how the runs are placed
827 * in the array.
829 * @param tabPlacement The JTabbedPane's tab placement.
830 * @param selectedRun The run the current selection is in.
832 protected void rotateTabRuns(int tabPlacement, int selectedRun)
834 if (runCount == 1 || selectedRun == 1 || selectedRun == -1)
835 return;
836 int[] newTabRuns = new int[tabRuns.length];
837 int currentRun = selectedRun;
838 int i = 1;
841 newTabRuns[i] = tabRuns[currentRun];
842 currentRun = getNextTabRun(currentRun);
843 i++;
845 while (i < runCount);
846 if (runCount > 1)
847 newTabRuns[0] = tabRuns[currentRun];
849 tabRuns = newTabRuns;
850 BasicTabbedPaneUI.this.selectedRun = 1;
854 * This method is called when a component is removed from the
855 * JTabbedPane.
857 * @param comp The component removed.
859 public void removeLayoutComponent(Component comp)
861 // Do nothing.
866 * This class acts as the LayoutManager for the JTabbedPane in
867 * SCROLL_TAB_MODE.
869 private class TabbedPaneScrollLayout extends TabbedPaneLayout
872 * This method returns the preferred layout size for the given container.
874 * @param parent The container to calculate a size for.
876 * @return The preferred layout size.
878 public Dimension preferredLayoutSize(Container parent)
880 return super.calculateSize(true);
884 * This method returns the minimum layout size for the given container.
886 * @param parent The container to calculate a size for.
888 * @return The minimum layout size.
890 public Dimension minimumLayoutSize(Container parent)
892 return super.calculateSize(true);
896 * This method calculates the tab area height given a desired width.
898 * @param tabPlacement The JTabbedPane's tab placement.
899 * @param width The expected width.
901 * @return The tab area height given the width.
903 protected int preferredTabAreaHeight(int tabPlacement, int width)
905 if (tabPane.getTabCount() == 0)
906 return calculateTabAreaHeight(tabPlacement, 0, 0);
908 int runs = 1;
910 int maxTabHeight = calculateMaxTabHeight(tabPlacement);
911 int tabAreaHeight = calculateTabAreaHeight(tabPlacement, runs,
912 maxTabHeight);
913 return tabAreaHeight;
917 * This method calculates the tab area width given a desired height.
919 * @param tabPlacement The JTabbedPane's tab placement.
920 * @param height The expected height.
922 * @return The tab area width given the height.
924 protected int preferredTabAreaWidth(int tabPlacement, int height)
926 if (tabPane.getTabCount() == 0)
927 return calculateTabAreaHeight(tabPlacement, 0, 0);
929 int runs = 1;
931 int maxTabWidth = calculateMaxTabWidth(tabPlacement);
932 int tabAreaWidth = calculateTabAreaWidth(tabPlacement, runs, maxTabWidth);
933 return tabAreaWidth;
937 * This method is called to calculate the tab rectangles. This method
938 * will calculate the size and position of all rectangles (taking into
939 * account which ones should be in which tab run). It will pad them and
940 * normalize them as necessary.
942 * @param tabPlacement The JTabbedPane's tab placement.
943 * @param tabCount The number of tabs.
945 protected void calculateTabRects(int tabPlacement, int tabCount)
947 if (tabCount == 0)
948 return;
950 FontMetrics fm = getFontMetrics();
951 SwingUtilities.calculateInnerArea(tabPane, calcRect);
952 Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
953 Insets insets = tabPane.getInsets();
954 int runs = 1;
955 int start = 0;
956 int top = 0;
957 if (tabPlacement == SwingConstants.TOP
958 || tabPlacement == SwingConstants.BOTTOM)
960 int maxHeight = calculateMaxTabHeight(tabPlacement);
961 calcRect.width -= tabAreaInsets.left + tabAreaInsets.right;
962 start = tabAreaInsets.left + insets.left;
963 int width = 0;
964 int runWidth = start;
965 top = insets.top + tabAreaInsets.top;
966 for (int i = 0; i < tabCount; i++)
968 width = calculateTabWidth(tabPlacement, i, fm);
970 rects[i] = new Rectangle(runWidth, top, width, maxHeight);
971 runWidth += width;
973 tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right;
974 tabAreaRect.height = runs * maxTabHeight
975 - (runs - 1) * tabRunOverlay
976 + tabAreaInsets.top + tabAreaInsets.bottom;
977 contentRect.width = tabAreaRect.width;
978 contentRect.height = tabPane.getHeight() - insets.top
979 - insets.bottom - tabAreaRect.height;
980 contentRect.x = insets.left;
981 tabAreaRect.x = insets.left;
982 if (tabPlacement == SwingConstants.BOTTOM)
984 contentRect.y = insets.top;
985 tabAreaRect.y = contentRect.y + contentRect.height;
987 else
989 tabAreaRect.y = insets.top;
990 contentRect.y = tabAreaRect.y + tabAreaRect.height;
993 else
995 int maxWidth = calculateMaxTabWidth(tabPlacement);
997 calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom;
998 int height = 0;
999 start = tabAreaInsets.top + insets.top;
1000 int runHeight = start;
1001 int fontHeight = fm.getHeight();
1002 top = insets.left + tabAreaInsets.left;
1003 for (int i = 0; i < tabCount; i++)
1005 height = calculateTabHeight(tabPlacement, i, fontHeight);
1006 rects[i] = new Rectangle(top, runHeight, maxWidth, height);
1007 runHeight += height;
1009 tabAreaRect.width = runs * maxTabWidth - (runs - 1) * tabRunOverlay
1010 + tabAreaInsets.left + tabAreaInsets.right;
1011 tabAreaRect.height = tabPane.getHeight() - insets.top
1012 - insets.bottom;
1013 tabAreaRect.y = insets.top;
1014 contentRect.width = tabPane.getWidth() - insets.left - insets.right
1015 - tabAreaRect.width;
1016 contentRect.height = tabAreaRect.height;
1017 contentRect.y = insets.top;
1018 if (tabPlacement == SwingConstants.LEFT)
1020 tabAreaRect.x = insets.left;
1021 contentRect.x = tabAreaRect.x + tabAreaRect.width;
1023 else
1025 contentRect.x = insets.left;
1026 tabAreaRect.x = contentRect.x + contentRect.width;
1029 runCount = runs;
1030 if (runCount > tabRuns.length)
1031 expandTabRunsArray();
1033 padSelectedTab(tabPlacement, tabPane.getSelectedIndex());
1037 * This method is called when the JTabbedPane is laid out in
1038 * SCROLL_TAB_LAYOUT. It finds the position for all components in the
1039 * JTabbedPane.
1041 * @param pane The JTabbedPane to be laid out.
1043 public void layoutContainer(Container pane)
1045 super.layoutContainer(pane);
1046 int tabCount = tabPane.getTabCount();
1047 Point p = null;
1048 if (tabCount == 0)
1049 return;
1050 int tabPlacement = tabPane.getTabPlacement();
1051 incrButton.setVisible(false);
1052 decrButton.setVisible(false);
1053 if (tabPlacement == SwingConstants.TOP
1054 || tabPlacement == SwingConstants.BOTTOM)
1056 if (tabAreaRect.x + tabAreaRect.width < rects[tabCount - 1].x
1057 + rects[tabCount - 1].width)
1059 Dimension incrDims = incrButton.getPreferredSize();
1060 Dimension decrDims = decrButton.getPreferredSize();
1062 decrButton.setBounds(tabAreaRect.x + tabAreaRect.width
1063 - incrDims.width - decrDims.width,
1064 tabAreaRect.y, decrDims.width,
1065 tabAreaRect.height);
1066 incrButton.setBounds(tabAreaRect.x + tabAreaRect.width
1067 - incrDims.width, tabAreaRect.y,
1068 decrDims.width, tabAreaRect.height);
1070 tabAreaRect.width -= decrDims.width + incrDims.width;
1071 incrButton.setVisible(true);
1072 decrButton.setVisible(true);
1076 if (tabPlacement == SwingConstants.LEFT
1077 || tabPlacement == SwingConstants.RIGHT)
1079 if (tabAreaRect.y + tabAreaRect.height < rects[tabCount - 1].y
1080 + rects[tabCount - 1].height)
1082 Dimension incrDims = incrButton.getPreferredSize();
1083 Dimension decrDims = decrButton.getPreferredSize();
1085 decrButton.setBounds(tabAreaRect.x,
1086 tabAreaRect.y + tabAreaRect.height
1087 - incrDims.height - decrDims.height,
1088 tabAreaRect.width, decrDims.height);
1089 incrButton.setBounds(tabAreaRect.x,
1090 tabAreaRect.y + tabAreaRect.height
1091 - incrDims.height, tabAreaRect.width,
1092 incrDims.height);
1094 tabAreaRect.height -= decrDims.height + incrDims.height;
1095 incrButton.setVisible(true);
1096 decrButton.setVisible(true);
1099 viewport.setBounds(tabAreaRect.x, tabAreaRect.y, tabAreaRect.width,
1100 tabAreaRect.height);
1101 int tabC = tabPane.getTabCount() - 1;
1102 if (tabCount > 0)
1104 int w = Math.max(rects[tabC].width + rects[tabC].x, tabAreaRect.width);
1105 int h = Math.max(rects[tabC].height, tabAreaRect.height);
1106 p = findPointForIndex(currentScrollLocation);
1108 // we want to cover that entire space so that borders that run under
1109 // the tab area don't show up when we move the viewport around.
1110 panel.setSize(w + p.x, h + p.y);
1112 viewport.setViewPosition(p);
1113 viewport.repaint();
1118 * This class handles ChangeEvents from the JTabbedPane.
1120 * @specnote Apparently this class was intended to be protected,
1121 * but was made public by a compiler bug and is now
1122 * public for compatibility.
1124 public class TabSelectionHandler implements ChangeListener
1127 * This method is called whenever a ChangeEvent is fired from the
1128 * JTabbedPane.
1130 * @param e The ChangeEvent fired.
1132 public void stateChanged(ChangeEvent e)
1134 selectedRun = getRunForTab(tabPane.getTabCount(),
1135 tabPane.getSelectedIndex());
1136 tabPane.revalidate();
1137 tabPane.repaint();
1142 * This helper class is a JPanel that fits inside the ScrollViewport. This
1143 * panel's sole job is to paint the tab rectangles inside the viewport so
1144 * that it's clipped correctly.
1146 private class ScrollingPanel extends JPanel
1149 * This is a private UI class for our panel.
1151 private class ScrollingPanelUI extends BasicPanelUI
1154 * This method overrides the default paint method. It paints the tab
1155 * rectangles for the JTabbedPane in the panel.
1157 * @param g The Graphics object to paint with.
1158 * @param c The JComponent to paint.
1160 public void paint(Graphics g, JComponent c)
1162 paintTabArea(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex());
1167 * This method overrides the updateUI method. It makes the default UI for
1168 * this ScrollingPanel to be a ScrollingPanelUI.
1170 public void updateUI()
1172 setUI((PanelUI) new ScrollingPanelUI());
1177 * This is a helper class that paints the panel that paints tabs. This
1178 * custom JViewport is used so that the tabs painted in the panel will be
1179 * clipped. This class implements UIResource so tabs are not added when
1180 * this objects of this class are added to the JTabbedPane.
1182 private class ScrollingViewport extends JViewport implements UIResource
1184 // TODO: Maybe remove this inner class.
1188 * This is a helper class that implements UIResource so it is not added as a
1189 * tab when an object of this class is added to the JTabbedPane.
1191 private class ScrollingButton extends BasicArrowButton implements UIResource
1194 * Creates a ScrollingButton given the direction.
1196 * @param dir The direction to point in.
1198 public ScrollingButton(int dir)
1200 super(dir);
1204 /** The button that increments the current scroll location.
1205 * This is package-private to avoid an accessor method. */
1206 transient ScrollingButton incrButton;
1208 /** The button that decrements the current scroll location.
1209 * This is package-private to avoid an accessor method. */
1210 transient ScrollingButton decrButton;
1212 /** The viewport used to display the tabs.
1213 * This is package-private to avoid an accessor method. */
1214 transient ScrollingViewport viewport;
1216 /** The panel inside the viewport that paints the tabs.
1217 * This is package-private to avoid an accessor method. */
1218 transient ScrollingPanel panel;
1220 /** The starting visible tab in the run in SCROLL_TAB_MODE.
1221 * This is package-private to avoid an accessor method. */
1222 transient int currentScrollLocation;
1224 /** A reusable rectangle. */
1225 protected Rectangle calcRect;
1227 /** An array of Rectangles keeping track of the tabs' area and position. */
1228 protected Rectangle[] rects;
1230 /** The insets around the content area. */
1231 protected Insets contentBorderInsets;
1233 /** The extra insets around the selected tab. */
1234 protected Insets selectedTabPadInsets;
1236 /** The insets around the tab area. */
1237 protected Insets tabAreaInsets;
1239 /** The insets around each and every tab. */
1240 protected Insets tabInsets;
1243 * The outer bottom and right edge color for both the tab and content
1244 * border.
1246 protected Color darkShadow;
1248 /** The color of the focus outline on the selected tab. */
1249 protected Color focus;
1251 /** FIXME: find a use for this. */
1252 protected Color highlight;
1254 /** The top and left edge color for both the tab and content border. */
1255 protected Color lightHighlight;
1257 /** The inner bottom and right edge color for the tab and content border. */
1258 protected Color shadow;
1260 /** The maximum tab height. */
1261 protected int maxTabHeight;
1263 /** The maximum tab width. */
1264 protected int maxTabWidth;
1266 /** The number of runs in the JTabbedPane. */
1267 protected int runCount;
1269 /** The index of the run that the selected index is in. */
1270 protected int selectedRun;
1272 /** The amount of space each run overlaps the previous by. */
1273 protected int tabRunOverlay;
1275 /** The gap between text and label */
1276 protected int textIconGap;
1278 // Keeps track of tab runs.
1279 // The organization of this array is as follows (lots of experimentation to
1280 // figure this out)
1281 // index 0 = furthest away from the component area (aka outer run)
1282 // index 1 = closest to component area (aka selected run)
1283 // index > 1 = listed in order leading from selected run to outer run.
1284 // each int in the array is the tab index + 1 (counting starts at 1)
1285 // for the last tab in the run. (same as the rects array)
1287 /** This array keeps track of which tabs are in which run. See above. */
1288 protected int[] tabRuns;
1291 * This is the keystroke for moving down.
1293 * @deprecated 1.3
1295 protected KeyStroke downKey;
1298 * This is the keystroke for moving left.
1300 * @deprecated 1.3
1302 protected KeyStroke leftKey;
1305 * This is the keystroke for moving right.
1307 * @deprecated 1.3
1309 protected KeyStroke rightKey;
1312 * This is the keystroke for moving up.
1314 * @deprecated 1.3
1316 protected KeyStroke upKey;
1318 /** The listener that listens for focus events. */
1319 protected FocusListener focusListener;
1321 /** The listener that listens for mouse events. */
1322 protected MouseListener mouseListener;
1324 /** The listener that listens for property change events. */
1325 protected PropertyChangeListener propertyChangeListener;
1327 /** The listener that listens for change events. */
1328 protected ChangeListener tabChangeListener;
1330 /** The tab pane that this UI paints. */
1331 protected JTabbedPane tabPane;
1333 /** The current layout manager for the tabPane.
1334 * This is package-private to avoid an accessor method. */
1335 transient LayoutManager layoutManager;
1337 /** The rectangle that describes the tab area's position and size.
1338 * This is package-private to avoid an accessor method. */
1339 transient Rectangle tabAreaRect;
1341 /** The rectangle that describes the content area's position and
1342 * size. This is package-private to avoid an accessor method. */
1343 transient Rectangle contentRect;
1346 * Creates a new BasicTabbedPaneUI object.
1348 public BasicTabbedPaneUI()
1350 super();
1351 rects = new Rectangle[0];
1352 tabRuns = new int[10];
1356 * This method creates a ScrollingButton that points in the appropriate
1357 * direction for an increasing button.
1358 * This is package-private to avoid an accessor method.
1360 * @return The increase ScrollingButton.
1362 ScrollingButton createIncreaseButton()
1364 if (incrButton == null)
1365 incrButton = new ScrollingButton(SwingConstants.NORTH);
1366 if (tabPane.getTabPlacement() == SwingConstants.TOP
1367 || tabPane.getTabPlacement() == SwingConstants.BOTTOM)
1368 incrButton.setDirection(SwingConstants.EAST);
1369 else
1370 incrButton.setDirection(SwingConstants.SOUTH);
1371 return incrButton;
1375 * This method creates a ScrollingButton that points in the appropriate
1376 * direction for a decreasing button.
1377 * This is package-private to avoid an accessor method.
1379 * @return The decrease ScrollingButton.
1381 ScrollingButton createDecreaseButton()
1383 if (decrButton == null)
1384 decrButton = new ScrollingButton(SwingConstants.SOUTH);
1385 if (tabPane.getTabPlacement() == SwingConstants.TOP
1386 || tabPane.getTabPlacement() == SwingConstants.BOTTOM)
1387 decrButton.setDirection(SwingConstants.WEST);
1388 else
1389 decrButton.setDirection(SwingConstants.NORTH);
1390 return decrButton;
1394 * This method finds the point to set the view position at given the index
1395 * of a tab. The tab will be the first visible tab in the run.
1396 * This is package-private to avoid an accessor method.
1398 * @param index The index of the first visible tab.
1400 * @return The position of the first visible tab.
1402 Point findPointForIndex(int index)
1404 int tabPlacement = tabPane.getTabPlacement();
1405 int selectedIndex = tabPane.getSelectedIndex();
1406 Insets insets = getSelectedTabPadInsets(tabPlacement);
1407 int w = 0;
1408 int h = 0;
1410 if (tabPlacement == TOP || tabPlacement == BOTTOM)
1412 if (index > 0)
1414 w += rects[index - 1].x + rects[index - 1].width;
1415 if (index > selectedIndex)
1416 w -= insets.left + insets.right;
1420 else
1422 if (index > 0)
1424 h += rects[index - 1].y + rects[index - 1].height;
1425 if (index > selectedIndex)
1426 h -= insets.top + insets.bottom;
1430 Point p = new Point(w, h);
1431 return p;
1435 * This method creates a new BasicTabbedPaneUI.
1437 * @param c The JComponent to create a UI for.
1439 * @return A new BasicTabbedPaneUI.
1441 public static ComponentUI createUI(JComponent c)
1443 return new BasicTabbedPaneUI();
1447 * This method installs the UI for the given JComponent.
1449 * @param c The JComponent to install the UI for.
1451 public void installUI(JComponent c)
1453 super.installUI(c);
1454 if (c instanceof JTabbedPane)
1456 tabPane = (JTabbedPane) c;
1458 installComponents();
1459 installDefaults();
1460 installListeners();
1461 installKeyboardActions();
1463 layoutManager = createLayoutManager();
1464 tabPane.setLayout(layoutManager);
1469 * This method uninstalls the UI for the given JComponent.
1471 * @param c The JComponent to uninstall the UI for.
1473 public void uninstallUI(JComponent c)
1475 layoutManager = null;
1477 uninstallKeyboardActions();
1478 uninstallListeners();
1479 uninstallDefaults();
1480 uninstallComponents();
1482 tabPane = null;
1486 * This method creates the appropriate layout manager for the JTabbedPane's
1487 * current tab layout policy. If the tab layout policy is
1488 * SCROLL_TAB_LAYOUT, then all the associated components that need to be
1489 * created will be done so now.
1491 * @return A layout manager given the tab layout policy.
1493 protected LayoutManager createLayoutManager()
1495 if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT)
1496 return new TabbedPaneLayout();
1497 else
1499 incrButton = createIncreaseButton();
1500 decrButton = createDecreaseButton();
1501 viewport = new ScrollingViewport();
1502 viewport.setLayout(null);
1503 panel = new ScrollingPanel();
1504 viewport.setView(panel);
1505 tabPane.add(incrButton);
1506 tabPane.add(decrButton);
1507 tabPane.add(viewport);
1508 currentScrollLocation = 0;
1509 decrButton.setEnabled(false);
1510 panel.addMouseListener(mouseListener);
1511 incrButton.addMouseListener(mouseListener);
1512 decrButton.addMouseListener(mouseListener);
1513 viewport.setBackground(Color.LIGHT_GRAY);
1515 return new TabbedPaneScrollLayout();
1520 * This method installs components for this JTabbedPane.
1522 protected void installComponents()
1524 // Nothing to be done.
1528 * This method uninstalls components for this JTabbedPane.
1530 protected void uninstallComponents()
1532 // Nothing to be done.
1536 * This method installs defaults for the Look and Feel.
1538 protected void installDefaults()
1540 LookAndFeel.installColorsAndFont(tabPane, "TabbedPane.background",
1541 "TabbedPane.foreground",
1542 "TabbedPane.font");
1543 tabPane.setOpaque(false);
1545 highlight = UIManager.getColor("TabbedPane.highlight");
1546 lightHighlight = UIManager.getColor("TabbedPane.lightHighlight");
1548 shadow = UIManager.getColor("TabbedPane.shadow");
1549 darkShadow = UIManager.getColor("TabbedPane.darkShadow");
1551 focus = UIManager.getColor("TabbedPane.focus");
1553 textIconGap = UIManager.getInt("TabbedPane.textIconGap");
1554 tabRunOverlay = UIManager.getInt("TabbedPane.tabRunOverlay");
1556 tabInsets = UIManager.getInsets("TabbedPane.tabInsets");
1557 selectedTabPadInsets = UIManager.getInsets("TabbedPane.tabbedPaneTabPadInsets");
1558 tabAreaInsets = UIManager.getInsets("TabbedPane.tabAreaInsets");
1559 contentBorderInsets = UIManager.getInsets("TabbedPane.tabbedPaneContentBorderInsets");
1561 calcRect = new Rectangle();
1562 tabRuns = new int[10];
1563 tabAreaRect = new Rectangle();
1564 contentRect = new Rectangle();
1568 * This method uninstalls defaults for the Look and Feel.
1570 protected void uninstallDefaults()
1572 calcRect = null;
1573 tabAreaRect = null;
1574 contentRect = null;
1575 tabRuns = null;
1577 contentBorderInsets = null;
1578 tabAreaInsets = null;
1579 selectedTabPadInsets = null;
1580 tabInsets = null;
1582 focus = null;
1583 darkShadow = null;
1584 shadow = null;
1585 lightHighlight = null;
1586 highlight = null;
1588 tabPane.setBackground(null);
1589 tabPane.setForeground(null);
1590 tabPane.setFont(null);
1594 * This method creates and installs the listeners for this UI.
1596 protected void installListeners()
1598 mouseListener = createMouseListener();
1599 tabChangeListener = createChangeListener();
1600 propertyChangeListener = createPropertyChangeListener();
1601 focusListener = createFocusListener();
1603 tabPane.addMouseListener(mouseListener);
1604 tabPane.addChangeListener(tabChangeListener);
1605 tabPane.addPropertyChangeListener(propertyChangeListener);
1606 tabPane.addFocusListener(focusListener);
1610 * This method removes and nulls the listeners for this UI.
1612 protected void uninstallListeners()
1614 tabPane.removeFocusListener(focusListener);
1615 tabPane.removePropertyChangeListener(propertyChangeListener);
1616 tabPane.removeChangeListener(tabChangeListener);
1617 tabPane.removeMouseListener(mouseListener);
1619 focusListener = null;
1620 propertyChangeListener = null;
1621 tabChangeListener = null;
1622 mouseListener = null;
1626 * This method creates a new MouseListener.
1628 * @return A new MouseListener.
1630 protected MouseListener createMouseListener()
1632 return new MouseHandler();
1636 * This method creates a new FocusListener.
1638 * @return A new FocusListener.
1640 protected FocusListener createFocusListener()
1642 return new FocusHandler();
1646 * This method creates a new ChangeListener.
1648 * @return A new ChangeListener.
1650 protected ChangeListener createChangeListener()
1652 return new TabSelectionHandler();
1656 * This method creates a new PropertyChangeListener.
1658 * @return A new PropertyChangeListener.
1660 protected PropertyChangeListener createPropertyChangeListener()
1662 return new PropertyChangeHandler();
1666 * This method installs keyboard actions for the JTabbedPane.
1668 protected void installKeyboardActions()
1670 // FIXME: Implement.
1674 * This method uninstalls keyboard actions for the JTabbedPane.
1676 protected void uninstallKeyboardActions()
1678 // FIXME: Implement.
1682 * This method returns the minimum size of the JTabbedPane.
1684 * @param c The JComponent to find a size for.
1686 * @return The minimum size.
1688 public Dimension getMinimumSize(JComponent c)
1690 return layoutManager.minimumLayoutSize(tabPane);
1694 * This method returns the maximum size of the JTabbedPane.
1696 * @param c The JComponent to find a size for.
1698 * @return The maximum size.
1700 public Dimension getMaximumSize(JComponent c)
1702 return new Dimension(Short.MAX_VALUE, Short.MAX_VALUE);
1706 * This method paints the JTabbedPane.
1708 * @param g The Graphics object to paint with.
1709 * @param c The JComponent to paint.
1711 public void paint(Graphics g, JComponent c)
1713 if (tabPane.getTabCount() == 0)
1714 return;
1715 if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT)
1716 paintTabArea(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex());
1717 paintContentBorder(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex());
1721 * This method paints the tab area. This includes painting the rectangles
1722 * that make up the tabs.
1724 * @param g The Graphics object to paint with.
1725 * @param tabPlacement The JTabbedPane's tab placement.
1726 * @param selectedIndex The selected index.
1728 protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex)
1730 Rectangle ir = new Rectangle();
1731 Rectangle tr = new Rectangle();
1733 boolean isScroll = tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT;
1735 // Please note: the ordering of the painting is important.
1736 // we WANT to paint the outermost run first and then work our way in.
1737 int tabCount = tabPane.getTabCount();
1738 int currRun = 1;
1740 if (tabCount < 1)
1741 return;
1743 if (runCount > 1)
1744 currRun = 0;
1745 for (int i = 0; i < runCount; i++)
1747 int first = lastTabInRun(tabCount, getPreviousTabRun(currRun)) + 1;
1748 if (isScroll)
1749 first = currentScrollLocation;
1750 else if (first == tabCount)
1751 first = 0;
1752 int last = lastTabInRun(tabCount, currRun);
1753 if (isScroll)
1755 for (int k = first; k < tabCount; k++)
1757 if (rects[k].x + rects[k].width - rects[first].x > viewport
1758 .getWidth())
1760 last = k;
1761 break;
1766 for (int j = first; j <= last; j++)
1768 if (j != selectedIndex || isScroll)
1769 paintTab(g, tabPlacement, rects, j, ir, tr);
1771 currRun = getPreviousTabRun(currRun);
1773 if (! isScroll)
1774 paintTab(g, tabPlacement, rects, selectedIndex, ir, tr);
1778 * This method paints an individual tab.
1780 * @param g The Graphics object to paint with.
1781 * @param tabPlacement The JTabbedPane's tab placement.
1782 * @param rects The array of rectangles that keep the size and position of
1783 * the tabs.
1784 * @param tabIndex The tab index to paint.
1785 * @param iconRect The rectangle to use for the icon.
1786 * @param textRect The rectangle to use for the text.
1788 protected void paintTab(Graphics g, int tabPlacement, Rectangle[] rects,
1789 int tabIndex, Rectangle iconRect, Rectangle textRect)
1791 FontMetrics fm = getFontMetrics();
1792 Icon icon = getIconForTab(tabIndex);
1793 String title = tabPane.getTitleAt(tabIndex);
1794 boolean isSelected = tabIndex == tabPane.getSelectedIndex();
1795 calcRect = getTabBounds(tabPane, tabIndex);
1797 int x = calcRect.x;
1798 int y = calcRect.y;
1799 int w = calcRect.width;
1800 int h = calcRect.height;
1801 if (getRunForTab(tabPane.getTabCount(), tabIndex) == 1)
1803 Insets insets = getTabAreaInsets(tabPlacement);
1804 switch (tabPlacement)
1806 case TOP:
1807 h += insets.bottom;
1808 break;
1809 case LEFT:
1810 w += insets.right;
1811 break;
1812 case BOTTOM:
1813 y -= insets.top;
1814 h += insets.top;
1815 break;
1816 case RIGHT:
1817 x -= insets.left;
1818 w += insets.left;
1819 break;
1823 layoutLabel(tabPlacement, fm, tabIndex, title, icon, calcRect, iconRect,
1824 textRect, isSelected);
1825 paintTabBackground(g, tabPlacement, tabIndex, x, y, w, h, isSelected);
1826 paintTabBorder(g, tabPlacement, tabIndex, x, y, w, h, isSelected);
1828 // FIXME: Paint little folding corner and jagged edge clipped tab.
1829 if (icon != null)
1830 paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected);
1831 if (title != null && ! title.equals(""))
1832 paintText(g, tabPlacement, tabPane.getFont(), fm, tabIndex, title,
1833 textRect, isSelected);
1837 * This method lays out the tab and finds the location to paint the icon
1838 * and text.
1840 * @param tabPlacement The JTabbedPane's tab placement.
1841 * @param metrics The font metrics for the font to paint with.
1842 * @param tabIndex The tab index to paint.
1843 * @param title The string painted.
1844 * @param icon The icon painted.
1845 * @param tabRect The tab bounds.
1846 * @param iconRect The calculated icon bounds.
1847 * @param textRect The calculated text bounds.
1848 * @param isSelected Whether this tab is selected.
1850 protected void layoutLabel(int tabPlacement, FontMetrics metrics,
1851 int tabIndex, String title, Icon icon,
1852 Rectangle tabRect, Rectangle iconRect,
1853 Rectangle textRect, boolean isSelected)
1855 SwingUtilities.layoutCompoundLabel(metrics, title, icon,
1856 SwingConstants.CENTER,
1857 SwingConstants.CENTER,
1858 SwingConstants.CENTER,
1859 SwingConstants.RIGHT, tabRect,
1860 iconRect, textRect, textIconGap);
1862 int shiftX = getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
1863 int shiftY = getTabLabelShiftY(tabPlacement, tabIndex, isSelected);
1865 iconRect.x += shiftX;
1866 iconRect.y += shiftY;
1868 textRect.x += shiftX;
1869 textRect.y += shiftY;
1873 * This method paints the icon.
1875 * @param g The Graphics object to paint.
1876 * @param tabPlacement The JTabbedPane's tab placement.
1877 * @param tabIndex The tab index to paint.
1878 * @param icon The icon to paint.
1879 * @param iconRect The bounds of the icon.
1880 * @param isSelected Whether this tab is selected.
1882 protected void paintIcon(Graphics g, int tabPlacement, int tabIndex,
1883 Icon icon, Rectangle iconRect, boolean isSelected)
1885 if (icon != null)
1886 icon.paintIcon(tabPane, g, iconRect.x, iconRect.y);
1890 * This method paints the text for the given tab.
1892 * @param g The Graphics object to paint with.
1893 * @param tabPlacement The JTabbedPane's tab placement.
1894 * @param font The font to paint with.
1895 * @param metrics The fontmetrics of the given font.
1896 * @param tabIndex The tab index.
1897 * @param title The string to paint.
1898 * @param textRect The bounds of the string.
1899 * @param isSelected Whether this tab is selected.
1901 protected void paintText(Graphics g, int tabPlacement, Font font,
1902 FontMetrics metrics, int tabIndex, String title,
1903 Rectangle textRect, boolean isSelected)
1905 View textView = getTextViewForTab(tabIndex);
1906 if (textView != null)
1908 textView.paint(g, textRect);
1909 return;
1912 Color fg = tabPane.getForegroundAt(tabIndex);
1913 if (fg == null)
1914 fg = tabPane.getForeground();
1915 Color bg = tabPane.getBackgroundAt(tabIndex);
1916 if (bg == null)
1917 bg = tabPane.getBackground();
1919 Color saved_color = g.getColor();
1920 Font f = g.getFont();
1921 g.setFont(font);
1923 if (tabPane.isEnabledAt(tabIndex))
1925 g.setColor(fg);
1927 int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
1929 if (mnemIndex != -1)
1930 BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
1931 textRect.x,
1932 textRect.y
1933 + metrics.getAscent());
1934 else
1935 g.drawString(title, textRect.x, textRect.y + metrics.getAscent());
1937 else
1939 g.setColor(bg.brighter());
1941 int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
1943 if (mnemIndex != -1)
1944 BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
1945 textRect.x, textRect.y);
1946 else
1947 g.drawString(title, textRect.x, textRect.y);
1949 g.setColor(bg.darker());
1950 if (mnemIndex != -1)
1951 BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
1952 textRect.x + 1,
1953 textRect.y + 1);
1954 else
1955 g.drawString(title, textRect.x + 1, textRect.y + 1);
1958 g.setColor(saved_color);
1959 g.setFont(f);
1963 * This method returns how much the label for the tab should shift in the X
1964 * direction.
1966 * @param tabPlacement The JTabbedPane's tab placement.
1967 * @param tabIndex The tab index being painted.
1968 * @param isSelected Whether this tab is selected.
1970 * @return The amount the label should shift by in the X direction.
1972 protected int getTabLabelShiftX(int tabPlacement, int tabIndex,
1973 boolean isSelected)
1975 // No reason to shift.
1976 return 0;
1980 * This method returns how much the label for the tab should shift in the Y
1981 * direction.
1983 * @param tabPlacement The JTabbedPane's tab placement.
1984 * @param tabIndex The tab index being painted.
1985 * @param isSelected Whether this tab is selected.
1987 * @return The amount the label should shift by in the Y direction.
1989 protected int getTabLabelShiftY(int tabPlacement, int tabIndex,
1990 boolean isSelected)
1992 // No reason to shift.
1993 return 0;
1997 * This method paints the focus rectangle around the selected tab.
1999 * @param g The Graphics object to paint with.
2000 * @param tabPlacement The JTabbedPane's tab placement.
2001 * @param rects The array of rectangles keeping track of size and position.
2002 * @param tabIndex The tab index.
2003 * @param iconRect The icon bounds.
2004 * @param textRect The text bounds.
2005 * @param isSelected Whether this tab is selected.
2007 protected void paintFocusIndicator(Graphics g, int tabPlacement,
2008 Rectangle[] rects, int tabIndex,
2009 Rectangle iconRect, Rectangle textRect,
2010 boolean isSelected)
2012 Color saved = g.getColor();
2013 calcRect = iconRect.union(textRect);
2015 g.setColor(focus);
2017 g.drawRect(calcRect.x, calcRect.y, calcRect.width, calcRect.height);
2019 g.setColor(saved);
2023 * This method paints the border for an individual tab.
2025 * @param g The Graphics object to paint with.
2026 * @param tabPlacement The JTabbedPane's tab placement.
2027 * @param tabIndex The tab index.
2028 * @param x The x position of the tab.
2029 * @param y The y position of the tab.
2030 * @param w The width of the tab.
2031 * @param h The height of the tab.
2032 * @param isSelected Whether the tab is selected.
2034 protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex,
2035 int x, int y, int w, int h, boolean isSelected)
2037 Color saved = g.getColor();
2039 if (! isSelected || tabPlacement != SwingConstants.TOP)
2041 g.setColor(shadow);
2042 g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1);
2043 g.setColor(darkShadow);
2044 g.drawLine(x, y + h, x + w, y + h);
2047 if (! isSelected || tabPlacement != SwingConstants.LEFT)
2049 g.setColor(darkShadow);
2050 g.drawLine(x + w, y, x + w, y + h);
2051 g.setColor(shadow);
2052 g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1);
2055 if (! isSelected || tabPlacement != SwingConstants.RIGHT)
2057 g.setColor(lightHighlight);
2058 g.drawLine(x, y, x, y + h);
2061 if (! isSelected || tabPlacement != SwingConstants.BOTTOM)
2063 g.setColor(lightHighlight);
2064 g.drawLine(x, y, x + w, y);
2067 g.setColor(saved);
2071 * This method paints the background for an individual tab.
2073 * @param g The Graphics object to paint with.
2074 * @param tabPlacement The JTabbedPane's tab placement.
2075 * @param tabIndex The tab index.
2076 * @param x The x position of the tab.
2077 * @param y The y position of the tab.
2078 * @param w The width of the tab.
2079 * @param h The height of the tab.
2080 * @param isSelected Whether the tab is selected.
2082 protected void paintTabBackground(Graphics g, int tabPlacement,
2083 int tabIndex, int x, int y, int w, int h,
2084 boolean isSelected)
2086 Color saved = g.getColor();
2087 if (isSelected)
2088 g.setColor(Color.LIGHT_GRAY);
2089 else
2091 Color bg = tabPane.getBackgroundAt(tabIndex);
2092 if (bg == null)
2093 bg = Color.GRAY;
2094 g.setColor(bg);
2097 g.fillRect(x, y, w, h);
2099 g.setColor(saved);
2103 * This method paints the border around the content area.
2105 * @param g The Graphics object to paint with.
2106 * @param tabPlacement The JTabbedPane's tab placement.
2107 * @param selectedIndex The index of the selected tab.
2109 protected void paintContentBorder(Graphics g, int tabPlacement,
2110 int selectedIndex)
2112 int x = contentRect.x;
2113 int y = contentRect.y;
2114 int w = contentRect.width;
2115 int h = contentRect.height;
2116 paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2117 paintContentBorderLeftEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2118 paintContentBorderBottomEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2119 paintContentBorderRightEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2123 * This method paints the top edge of the content border.
2125 * @param g The Graphics object to paint with.
2126 * @param tabPlacement The JTabbedPane's tab placement.
2127 * @param selectedIndex The selected tab index.
2128 * @param x The x coordinate for the content area.
2129 * @param y The y coordinate for the content area.
2130 * @param w The width of the content area.
2131 * @param h The height of the content area.
2133 protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
2134 int selectedIndex, int x, int y,
2135 int w, int h)
2137 Color saved = g.getColor();
2138 g.setColor(lightHighlight);
2140 int startgap = rects[selectedIndex].x;
2141 int endgap = rects[selectedIndex].x + rects[selectedIndex].width;
2143 int diff = 0;
2145 if (tabPlacement == SwingConstants.TOP)
2147 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
2149 Point p = findPointForIndex(currentScrollLocation);
2150 diff = p.x;
2153 g.drawLine(x, y, startgap - diff, y);
2154 g.drawLine(endgap - diff, y, x + w, y);
2156 else
2157 g.drawLine(x, y, x + w, y);
2159 g.setColor(saved);
2163 * This method paints the left edge of the content border.
2165 * @param g The Graphics object to paint with.
2166 * @param tabPlacement The JTabbedPane's tab placement.
2167 * @param selectedIndex The selected tab index.
2168 * @param x The x coordinate for the content area.
2169 * @param y The y coordinate for the content area.
2170 * @param w The width of the content area.
2171 * @param h The height of the content area.
2173 protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
2174 int selectedIndex, int x, int y,
2175 int w, int h)
2177 Color saved = g.getColor();
2178 g.setColor(lightHighlight);
2180 int startgap = rects[selectedIndex].y;
2181 int endgap = rects[selectedIndex].y + rects[selectedIndex].height;
2183 int diff = 0;
2185 if (tabPlacement == SwingConstants.LEFT)
2187 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
2189 Point p = findPointForIndex(currentScrollLocation);
2190 diff = p.y;
2193 g.drawLine(x, y, x, startgap - diff);
2194 g.drawLine(x, endgap - diff, x, y + h);
2196 else
2197 g.drawLine(x, y, x, y + h);
2199 g.setColor(saved);
2203 * This method paints the bottom edge of the content border.
2205 * @param g The Graphics object to paint with.
2206 * @param tabPlacement The JTabbedPane's tab placement.
2207 * @param selectedIndex The selected tab index.
2208 * @param x The x coordinate for the content area.
2209 * @param y The y coordinate for the content area.
2210 * @param w The width of the content area.
2211 * @param h The height of the content area.
2213 protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
2214 int selectedIndex, int x, int y,
2215 int w, int h)
2217 Color saved = g.getColor();
2219 int startgap = rects[selectedIndex].x;
2220 int endgap = rects[selectedIndex].x + rects[selectedIndex].width;
2222 int diff = 0;
2224 if (tabPlacement == SwingConstants.BOTTOM)
2226 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
2228 Point p = findPointForIndex(currentScrollLocation);
2229 diff = p.x;
2232 g.setColor(shadow);
2233 g.drawLine(x + 1, y + h - 1, startgap - diff, y + h - 1);
2234 g.drawLine(endgap - diff, y + h - 1, x + w - 1, y + h - 1);
2236 g.setColor(darkShadow);
2237 g.drawLine(x, y + h, startgap - diff, y + h);
2238 g.drawLine(endgap - diff, y + h, x + w, y + h);
2240 else
2242 g.setColor(shadow);
2243 g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1);
2244 g.setColor(darkShadow);
2245 g.drawLine(x, y + h, x + w, y + h);
2248 g.setColor(saved);
2252 * This method paints the right edge of the content border.
2254 * @param g The Graphics object to paint with.
2255 * @param tabPlacement The JTabbedPane's tab placement.
2256 * @param selectedIndex The selected tab index.
2257 * @param x The x coordinate for the content area.
2258 * @param y The y coordinate for the content area.
2259 * @param w The width of the content area.
2260 * @param h The height of the content area.
2262 protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
2263 int selectedIndex, int x, int y,
2264 int w, int h)
2266 Color saved = g.getColor();
2267 int startgap = rects[selectedIndex].y;
2268 int endgap = rects[selectedIndex].y + rects[selectedIndex].height;
2270 int diff = 0;
2272 if (tabPlacement == SwingConstants.RIGHT)
2274 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
2276 Point p = findPointForIndex(currentScrollLocation);
2277 diff = p.y;
2280 g.setColor(shadow);
2281 g.drawLine(x + w - 1, y + 1, x + w - 1, startgap - diff);
2282 g.drawLine(x + w - 1, endgap - diff, x + w - 1, y + h - 1);
2284 g.setColor(darkShadow);
2285 g.drawLine(x + w, y, x + w, startgap - diff);
2286 g.drawLine(x + w, endgap - diff, x + w, y + h);
2288 else
2290 g.setColor(shadow);
2291 g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1);
2292 g.setColor(darkShadow);
2293 g.drawLine(x + w, y, x + w, y + h);
2296 g.setColor(saved);
2300 * This method returns the tab bounds for the given index.
2302 * @param pane The JTabbedPane.
2303 * @param i The index to look for.
2305 * @return The bounds of the tab with the given index.
2307 public Rectangle getTabBounds(JTabbedPane pane, int i)
2309 return rects[i];
2313 * This method returns the number of runs.
2315 * @param pane The JTabbedPane.
2317 * @return The number of runs.
2319 public int getTabRunCount(JTabbedPane pane)
2321 return runCount;
2325 * This method returns the tab index given a coordinate.
2327 * @param pane The JTabbedPane.
2328 * @param x The x coordinate.
2329 * @param y The y coordinate.
2331 * @return The tab index that the coordinate lands in.
2333 public int tabForCoordinate(JTabbedPane pane, int x, int y)
2335 Point p = new Point(x, y);
2336 int tabCount = tabPane.getTabCount();
2337 int currRun = 1;
2338 for (int i = 0; i < runCount; i++)
2340 int first = lastTabInRun(tabCount, getPreviousTabRun(currRun)) + 1;
2341 if (first == tabCount)
2342 first = 0;
2343 int last = lastTabInRun(tabCount, currRun);
2344 for (int j = first; j <= last; j++)
2346 if (getTabBounds(pane, j).contains(p))
2347 return j;
2349 currRun = getNextTabRun(currRun);
2351 return -1;
2355 * This method returns the tab bounds in the given rectangle.
2357 * @param tabIndex The index to get bounds for.
2358 * @param dest The rectangle to store bounds in.
2360 * @return The rectangle passed in.
2362 protected Rectangle getTabBounds(int tabIndex, Rectangle dest)
2364 dest.setBounds(getTabBounds(tabPane, tabIndex));
2365 return dest;
2369 * This method returns the component that is shown in the content area.
2371 * @return The component that is shown in the content area.
2373 protected Component getVisibleComponent()
2375 return tabPane.getComponentAt(tabPane.getSelectedIndex());
2379 * This method sets the visible component.
2381 * @param component The component to be set visible.
2383 protected void setVisibleComponent(Component component)
2385 component.setVisible(true);
2386 tabPane.setSelectedComponent(component);
2390 * This method assures that enough rectangles are created given the
2391 * tabCount. The old array is copied to the new one.
2393 * @param tabCount The number of tabs.
2395 protected void assureRectsCreated(int tabCount)
2397 if (rects.length < tabCount)
2399 Rectangle[] old = rects;
2400 rects = new Rectangle[tabCount];
2401 System.arraycopy(old, 0, rects, 0, old.length);
2402 for (int i = old.length; i < rects.length; i++)
2403 rects[i] = new Rectangle();
2408 * This method expands the tabRuns array to give it more room. The old array
2409 * is copied to the new one.
2411 protected void expandTabRunsArray()
2413 // This method adds another 10 index positions to the tabRuns array.
2414 if (tabRuns == null)
2415 tabRuns = new int[10];
2416 else
2418 int[] newRuns = new int[tabRuns.length + 10];
2419 System.arraycopy(tabRuns, 0, newRuns, 0, tabRuns.length);
2420 tabRuns = newRuns;
2425 * This method returns which run a particular tab belongs to.
2427 * @param tabCount The number of tabs.
2428 * @param tabIndex The tab to find.
2430 * @return The tabRuns index that it belongs to.
2432 protected int getRunForTab(int tabCount, int tabIndex)
2434 if (runCount == 1 && tabIndex < tabCount && tabIndex >= 0)
2435 return 1;
2436 for (int i = 0; i < runCount; i++)
2438 int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1;
2439 if (first == tabCount)
2440 first = 0;
2441 int last = lastTabInRun(tabCount, i);
2442 if (last >= tabIndex && first <= tabIndex)
2443 return i;
2445 return -1;
2449 * This method returns the index of the last tab in a run.
2451 * @param tabCount The number of tabs.
2452 * @param run The run to check.
2454 * @return The last tab in the given run.
2456 protected int lastTabInRun(int tabCount, int run)
2458 if (tabRuns[run] == 0)
2459 return tabCount - 1;
2460 else
2461 return tabRuns[run] - 1;
2465 * This method returns the tab run overlay.
2467 * @param tabPlacement The JTabbedPane's tab placement.
2469 * @return The tab run overlay.
2471 protected int getTabRunOverlay(int tabPlacement)
2473 return tabRunOverlay;
2477 * This method returns the tab run indent. It is used in WRAP_TAB_LAYOUT and
2478 * makes each tab run start indented by a certain amount.
2480 * @param tabPlacement The JTabbedPane's tab placement.
2481 * @param run The run to get indent for.
2483 * @return The amount a run should be indented.
2485 protected int getTabRunIndent(int tabPlacement, int run)
2487 return 0;
2491 * This method returns whether a tab run should be padded.
2493 * @param tabPlacement The JTabbedPane's tab placement.
2494 * @param run The run to check.
2496 * @return Whether the given run should be padded.
2498 protected boolean shouldPadTabRun(int tabPlacement, int run)
2500 return true;
2504 * This method returns whether the tab runs should be rotated.
2506 * @param tabPlacement The JTabbedPane's tab placement.
2508 * @return Whether runs should be rotated.
2510 protected boolean shouldRotateTabRuns(int tabPlacement)
2512 return true;
2516 * This method returns an icon for the tab. If the tab is disabled, it
2517 * should return the disabledIcon. If it is enabled, then it should return
2518 * the default icon.
2520 * @param tabIndex The tab index to get an icon for.
2522 * @return The icon for the tab index.
2524 protected Icon getIconForTab(int tabIndex)
2526 if (tabPane.isEnabledAt(tabIndex))
2527 return tabPane.getIconAt(tabIndex);
2528 else
2529 return tabPane.getDisabledIconAt(tabIndex);
2533 * This method returns a view that can paint the text for the label.
2535 * @param tabIndex The tab index to get a view for.
2537 * @return The view for the tab index.
2539 protected View getTextViewForTab(int tabIndex)
2541 return null;
2545 * This method returns the tab height, including insets, for the given index
2546 * and fontheight.
2548 * @param tabPlacement The JTabbedPane's tab placement.
2549 * @param tabIndex The index of the tab to calculate.
2550 * @param fontHeight The font height.
2552 * @return This tab's height.
2554 protected int calculateTabHeight(int tabPlacement, int tabIndex,
2555 int fontHeight)
2557 Icon icon = getIconForTab(tabIndex);
2558 Insets insets = getTabInsets(tabPlacement, tabIndex);
2560 int height = 0;
2561 if (icon != null)
2563 Rectangle vr = new Rectangle();
2564 Rectangle ir = new Rectangle();
2565 Rectangle tr = new Rectangle();
2566 layoutLabel(tabPlacement, getFontMetrics(), tabIndex,
2567 tabPane.getTitleAt(tabIndex), icon, vr, ir, tr,
2568 tabIndex == tabPane.getSelectedIndex());
2569 height = tr.union(ir).height;
2571 else
2572 height = fontHeight;
2574 height += insets.top + insets.bottom;
2575 return height;
2579 * This method returns the max tab height.
2581 * @param tabPlacement The JTabbedPane's tab placement.
2583 * @return The maximum tab height.
2585 protected int calculateMaxTabHeight(int tabPlacement)
2587 maxTabHeight = 0;
2589 FontMetrics fm = getFontMetrics();
2590 int fontHeight = fm.getHeight();
2592 for (int i = 0; i < tabPane.getTabCount(); i++)
2593 maxTabHeight = Math.max(calculateTabHeight(tabPlacement, i, fontHeight),
2594 maxTabHeight);
2596 return maxTabHeight;
2600 * This method calculates the tab width, including insets, for the given tab
2601 * index and font metrics.
2603 * @param tabPlacement The JTabbedPane's tab placement.
2604 * @param tabIndex The tab index to calculate for.
2605 * @param metrics The font's metrics.
2607 * @return The tab width for the given index.
2609 protected int calculateTabWidth(int tabPlacement, int tabIndex,
2610 FontMetrics metrics)
2612 Icon icon = getIconForTab(tabIndex);
2613 Insets insets = getTabInsets(tabPlacement, tabIndex);
2615 int width = 0;
2616 if (icon != null)
2618 Rectangle vr = new Rectangle();
2619 Rectangle ir = new Rectangle();
2620 Rectangle tr = new Rectangle();
2621 layoutLabel(tabPlacement, getFontMetrics(), tabIndex,
2622 tabPane.getTitleAt(tabIndex), icon, vr, ir, tr,
2623 tabIndex == tabPane.getSelectedIndex());
2624 width = tr.union(ir).width;
2626 else
2627 width = metrics.stringWidth(tabPane.getTitleAt(tabIndex));
2629 width += insets.left + insets.right;
2630 return width;
2634 * This method calculates the max tab width.
2636 * @param tabPlacement The JTabbedPane's tab placement.
2638 * @return The maximum tab width.
2640 protected int calculateMaxTabWidth(int tabPlacement)
2642 maxTabWidth = 0;
2644 FontMetrics fm = getFontMetrics();
2646 for (int i = 0; i < tabPane.getTabCount(); i++)
2647 maxTabWidth = Math.max(calculateTabWidth(tabPlacement, i, fm),
2648 maxTabWidth);
2650 return maxTabWidth;
2654 * This method calculates the tab area height, including insets, for the
2655 * given amount of runs and tab height.
2657 * @param tabPlacement The JTabbedPane's tab placement.
2658 * @param horizRunCount The number of runs.
2659 * @param maxTabHeight The max tab height.
2661 * @return The tab area height.
2663 protected int calculateTabAreaHeight(int tabPlacement, int horizRunCount,
2664 int maxTabHeight)
2666 Insets insets = getTabAreaInsets(tabPlacement);
2667 int tabAreaHeight = horizRunCount * maxTabHeight
2668 - (horizRunCount - 1) * tabRunOverlay;
2670 tabAreaHeight += insets.top + insets.bottom;
2672 return tabAreaHeight;
2676 * This method calculates the tab area width, including insets, for the
2677 * given amount of runs and tab width.
2679 * @param tabPlacement The JTabbedPane's tab placement.
2680 * @param vertRunCount The number of runs.
2681 * @param maxTabWidth The max tab width.
2683 * @return The tab area width.
2685 protected int calculateTabAreaWidth(int tabPlacement, int vertRunCount,
2686 int maxTabWidth)
2688 Insets insets = getTabAreaInsets(tabPlacement);
2689 int tabAreaWidth = vertRunCount * maxTabWidth
2690 - (vertRunCount - 1) * tabRunOverlay;
2692 tabAreaWidth += insets.left + insets.right;
2694 return tabAreaWidth;
2698 * This method returns the tab insets appropriately rotated.
2700 * @param tabPlacement The JTabbedPane's tab placement.
2701 * @param tabIndex The tab index.
2703 * @return The tab insets for the given index.
2705 protected Insets getTabInsets(int tabPlacement, int tabIndex)
2707 Insets target = new Insets(0, 0, 0, 0);
2708 rotateInsets(tabInsets, target, tabPlacement);
2709 return target;
2713 * This method returns the selected tab pad insets appropriately rotated.
2715 * @param tabPlacement The JTabbedPane's tab placement.
2717 * @return The selected tab pad insets.
2719 protected Insets getSelectedTabPadInsets(int tabPlacement)
2721 Insets target = new Insets(0, 0, 0, 0);
2722 rotateInsets(selectedTabPadInsets, target, tabPlacement);
2723 return target;
2727 * This method returns the tab area insets appropriately rotated.
2729 * @param tabPlacement The JTabbedPane's tab placement.
2731 * @return The tab area insets.
2733 protected Insets getTabAreaInsets(int tabPlacement)
2735 Insets target = new Insets(0, 0, 0, 0);
2736 rotateInsets(tabAreaInsets, target, tabPlacement);
2737 return target;
2741 * This method returns the content border insets appropriately rotated.
2743 * @param tabPlacement The JTabbedPane's tab placement.
2745 * @return The content border insets.
2747 protected Insets getContentBorderInsets(int tabPlacement)
2749 Insets target = new Insets(0, 0, 0, 0);
2750 rotateInsets(contentBorderInsets, target, tabPlacement);
2751 return target;
2755 * This method returns the fontmetrics for the font of the JTabbedPane.
2757 * @return The font metrics for the JTabbedPane.
2759 protected FontMetrics getFontMetrics()
2761 FontMetrics fm = tabPane.getFontMetrics(tabPane.getFont());
2762 return fm;
2766 * This method navigates from the selected tab into the given direction. As
2767 * a result, a new tab will be selected (if possible).
2769 * @param direction The direction to navigate in.
2771 protected void navigateSelectedTab(int direction)
2773 int tabPlacement = tabPane.getTabPlacement();
2774 if (tabPlacement == SwingConstants.TOP
2775 || tabPlacement == SwingConstants.BOTTOM)
2777 if (direction == SwingConstants.WEST)
2778 selectPreviousTabInRun(tabPane.getSelectedIndex());
2779 else if (direction == SwingConstants.EAST)
2780 selectNextTabInRun(tabPane.getSelectedIndex());
2782 else
2784 int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(),
2785 tabPane.getSelectedIndex(),
2786 (tabPlacement == SwingConstants.RIGHT)
2787 ? true : false);
2788 selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(),
2789 offset);
2792 if (tabPlacement == SwingConstants.LEFT
2793 || tabPlacement == SwingConstants.RIGHT)
2795 if (direction == SwingConstants.NORTH)
2796 selectPreviousTabInRun(tabPane.getSelectedIndex());
2797 else if (direction == SwingConstants.SOUTH)
2798 selectNextTabInRun(tabPane.getSelectedIndex());
2799 else
2801 int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(),
2802 tabPane.getSelectedIndex(),
2803 (tabPlacement == SwingConstants.RIGHT)
2804 ? true : false);
2805 selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(),
2806 offset);
2812 * This method selects the next tab in the run.
2814 * @param current The current selected index.
2816 protected void selectNextTabInRun(int current)
2818 tabPane.setSelectedIndex(getNextTabIndexInRun(tabPane.getTabCount(),
2819 current));
2823 * This method selects the previous tab in the run.
2825 * @param current The current selected index.
2827 protected void selectPreviousTabInRun(int current)
2829 tabPane.setSelectedIndex(getPreviousTabIndexInRun(tabPane.getTabCount(),
2830 current));
2834 * This method selects the next tab (regardless of runs).
2836 * @param current The current selected index.
2838 protected void selectNextTab(int current)
2840 tabPane.setSelectedIndex(getNextTabIndex(current));
2844 * This method selects the previous tab (regardless of runs).
2846 * @param current The current selected index.
2848 protected void selectPreviousTab(int current)
2850 tabPane.setSelectedIndex(getPreviousTabIndex(current));
2854 * This method selects the correct tab given an offset from the current tab
2855 * index. If the tab placement is TOP or BOTTOM, the offset will be in the
2856 * y direction, otherwise, it will be in the x direction. A new coordinate
2857 * will be found by adding the offset to the current location of the tab.
2858 * The tab that the new location will be selected.
2860 * @param tabPlacement The JTabbedPane's tab placement.
2861 * @param tabIndex The tab to start from.
2862 * @param offset The coordinate offset.
2864 protected void selectAdjacentRunTab(int tabPlacement, int tabIndex,
2865 int offset)
2867 int x = rects[tabIndex].x + rects[tabIndex].width / 2;
2868 int y = rects[tabIndex].y + rects[tabIndex].height / 2;
2870 switch (tabPlacement)
2872 case SwingConstants.TOP:
2873 case SwingConstants.BOTTOM:
2874 y += offset;
2875 break;
2876 case SwingConstants.RIGHT:
2877 case SwingConstants.LEFT:
2878 x += offset;
2879 break;
2882 int index = tabForCoordinate(tabPane, x, y);
2883 if (index != -1)
2884 tabPane.setSelectedIndex(index);
2887 // This method is called when you press up/down to cycle through tab runs.
2888 // it returns the distance (between the two runs' x/y position.
2889 // where one run is the current selected run and the other run is the run in the
2890 // direction of the scroll (dictated by the forward flag)
2891 // the offset is an absolute value of the difference
2894 * This method calculates the offset distance for use in
2895 * selectAdjacentRunTab. The offset returned will be a difference in the y
2896 * coordinate between the run in the desired direction and the current run
2897 * (for tabPlacement in TOP or BOTTOM). Use x coordinate for LEFT and
2898 * RIGHT.
2900 * @param tabPlacement The JTabbedPane's tab placement.
2901 * @param tabCount The number of tabs.
2902 * @param tabIndex The starting index.
2903 * @param forward If forward, the run in the desired direction will be the
2904 * next run.
2906 * @return The offset between the two runs.
2908 protected int getTabRunOffset(int tabPlacement, int tabCount, int tabIndex,
2909 boolean forward)
2911 int currRun = getRunForTab(tabCount, tabIndex);
2912 int offset;
2913 int nextRun = (forward) ? getNextTabRun(currRun) : getPreviousTabRun(currRun);
2914 if (tabPlacement == SwingConstants.TOP
2915 || tabPlacement == SwingConstants.BOTTOM)
2916 offset = rects[lastTabInRun(tabCount, nextRun)].y
2917 - rects[lastTabInRun(tabCount, currRun)].y;
2918 else
2919 offset = rects[lastTabInRun(tabCount, nextRun)].x
2920 - rects[lastTabInRun(tabCount, currRun)].x;
2921 return offset;
2925 * This method returns the previous tab index.
2927 * @param base The index to start from.
2929 * @return The previous tab index.
2931 protected int getPreviousTabIndex(int base)
2933 base--;
2934 if (base < 0)
2935 return tabPane.getTabCount() - 1;
2936 return base;
2940 * This method returns the next tab index.
2942 * @param base The index to start from.
2944 * @return The next tab index.
2946 protected int getNextTabIndex(int base)
2948 base++;
2949 if (base == tabPane.getTabCount())
2950 return 0;
2951 return base;
2955 * This method returns the next tab index in the run. If the next index is
2956 * out of this run, it will return the starting tab index for the run.
2958 * @param tabCount The number of tabs.
2959 * @param base The index to start from.
2961 * @return The next tab index in the run.
2963 protected int getNextTabIndexInRun(int tabCount, int base)
2965 int index = getNextTabIndex(base);
2966 int run = getRunForTab(tabCount, base);
2967 if (index == lastTabInRun(tabCount, run) + 1)
2968 index = lastTabInRun(tabCount, getPreviousTabRun(run)) + 1;
2969 return getNextTabIndex(base);
2973 * This method returns the previous tab index in the run. If the previous
2974 * index is out of this run, it will return the last index for the run.
2976 * @param tabCount The number of tabs.
2977 * @param base The index to start from.
2979 * @return The previous tab index in the run.
2981 protected int getPreviousTabIndexInRun(int tabCount, int base)
2983 int index = getPreviousTabIndex(base);
2984 int run = getRunForTab(tabCount, base);
2985 if (index == lastTabInRun(tabCount, getPreviousTabRun(run)))
2986 index = lastTabInRun(tabCount, run);
2987 return getPreviousTabIndex(base);
2991 * This method returns the index of the previous run.
2993 * @param baseRun The run to start from.
2995 * @return The index of the previous run.
2997 protected int getPreviousTabRun(int baseRun)
2999 if (getTabRunCount(tabPane) == 1)
3000 return 1;
3002 int prevRun = --baseRun;
3003 if (prevRun < 0)
3004 prevRun = getTabRunCount(tabPane) - 1;
3005 return prevRun;
3009 * This method returns the index of the next run.
3011 * @param baseRun The run to start from.
3013 * @return The index of the next run.
3015 protected int getNextTabRun(int baseRun)
3017 if (getTabRunCount(tabPane) == 1)
3018 return 1;
3020 int nextRun = ++baseRun;
3021 if (nextRun == getTabRunCount(tabPane))
3022 nextRun = 0;
3023 return nextRun;
3027 * This method rotates the insets given a direction to rotate them in.
3028 * Target placement should be one of TOP, LEFT, BOTTOM, RIGHT. The rotated
3029 * insets will be stored in targetInsets. Passing in TOP as the direction
3030 * does nothing. Passing in LEFT switches top and left, right and bottom.
3031 * Passing in BOTTOM switches top and bottom. Passing in RIGHT switches top
3032 * for left, left for bottom, bottom for right, and right for top.
3034 * @param topInsets The reference insets.
3035 * @param targetInsets An Insets object to store the new insets.
3036 * @param targetPlacement The rotation direction.
3038 protected static void rotateInsets(Insets topInsets, Insets targetInsets,
3039 int targetPlacement)
3041 // Sun's version will happily throw an NPE if params are null,
3042 // so I won't check it either.
3043 switch (targetPlacement)
3045 case SwingConstants.TOP:
3046 targetInsets.top = topInsets.top;
3047 targetInsets.left = topInsets.left;
3048 targetInsets.right = topInsets.right;
3049 targetInsets.bottom = topInsets.bottom;
3050 break;
3051 case SwingConstants.LEFT:
3052 targetInsets.left = topInsets.top;
3053 targetInsets.top = topInsets.left;
3054 targetInsets.right = topInsets.bottom;
3055 targetInsets.bottom = topInsets.right;
3056 break;
3057 case SwingConstants.BOTTOM:
3058 targetInsets.top = topInsets.bottom;
3059 targetInsets.bottom = topInsets.top;
3060 targetInsets.left = topInsets.left;
3061 targetInsets.right = topInsets.right;
3062 break;
3063 case SwingConstants.RIGHT:
3064 targetInsets.top = topInsets.left;
3065 targetInsets.left = topInsets.bottom;
3066 targetInsets.bottom = topInsets.right;
3067 targetInsets.right = topInsets.top;
3068 break;