Source for javax.swing.plaf.basic.BasicTabbedPaneUI

   1: /* BasicTabbedPaneUI.java --
   2:    Copyright (C) 2002, 2004, 2005, 2006  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   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.
  10: 
  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.
  15: 
  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.
  20: 
  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.
  25: 
  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. */
  37: 
  38: 
  39: package javax.swing.plaf.basic;
  40: 
  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.ActionEvent;
  53: import java.awt.event.FocusAdapter;
  54: import java.awt.event.FocusEvent;
  55: import java.awt.event.FocusListener;
  56: import java.awt.event.MouseAdapter;
  57: import java.awt.event.MouseEvent;
  58: import java.awt.event.MouseListener;
  59: import java.beans.PropertyChangeEvent;
  60: import java.beans.PropertyChangeListener;
  61: 
  62: import javax.swing.AbstractAction;
  63: import javax.swing.ActionMap;
  64: import javax.swing.Icon;
  65: import javax.swing.InputMap;
  66: import javax.swing.JComponent;
  67: import javax.swing.JPanel;
  68: import javax.swing.JTabbedPane;
  69: import javax.swing.JViewport;
  70: import javax.swing.KeyStroke;
  71: import javax.swing.LookAndFeel;
  72: import javax.swing.SwingConstants;
  73: import javax.swing.SwingUtilities;
  74: import javax.swing.UIManager;
  75: import javax.swing.event.ChangeEvent;
  76: import javax.swing.event.ChangeListener;
  77: import javax.swing.plaf.ActionMapUIResource;
  78: import javax.swing.plaf.ComponentUI;
  79: import javax.swing.plaf.TabbedPaneUI;
  80: import javax.swing.plaf.UIResource;
  81: import javax.swing.text.View;
  82: 
  83: /**
  84:  * This is the Basic Look and Feel's UI delegate for JTabbedPane.
  85:  * 
  86:  * @author Lillian Angel (langel@redhat.com)
  87:  * @author Kim Ho (kho@redhat.com)
  88:  * @author Roman Kennke (kennke@aicas.com)
  89:  * @author Robert Schuster (robertschuster@fsfe.org)
  90:  */
  91: public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
  92: {
  93:   
  94:   static class NavigateAction extends AbstractAction
  95:   {
  96:     int direction;
  97:     
  98:     NavigateAction(String name, int dir)
  99:     {
 100:       super(name);
 101:       direction = dir;
 102:     }
 103: 
 104:     public void actionPerformed(ActionEvent event)
 105:     {
 106:       JTabbedPane tp = (JTabbedPane) event.getSource();
 107:       BasicTabbedPaneUI ui = (BasicTabbedPaneUI) tp.getUI();
 108: 
 109:       ui.navigateSelectedTab(direction);
 110:     }
 111:   
 112:   }
 113:   
 114:   static class NavigatePageDownAction extends AbstractAction
 115:   {
 116: 
 117:     public NavigatePageDownAction()
 118:     {
 119:       super("navigatePageDown");
 120:     }
 121: 
 122:     public void actionPerformed(ActionEvent event)
 123:     {
 124:       JTabbedPane tp = (JTabbedPane) event.getSource();
 125:       BasicTabbedPaneUI ui = (BasicTabbedPaneUI) tp.getUI();
 126:       
 127:       int i = tp.getSelectedIndex();
 128:       
 129:       if (i < 0)
 130:         i = 0;
 131:       
 132:       ui.selectNextTabInRun(i);
 133:     }
 134:     
 135:   }
 136:   
 137:   static class NavigatePageUpAction extends AbstractAction
 138:   {
 139: 
 140:     public NavigatePageUpAction()
 141:     {
 142:       super("navigatePageUp");
 143:     }
 144: 
 145:     public void actionPerformed(ActionEvent event)
 146:     {
 147:       JTabbedPane tp = (JTabbedPane) event.getSource();
 148:       BasicTabbedPaneUI ui = (BasicTabbedPaneUI) tp.getUI();
 149:       
 150:       int i = tp.getSelectedIndex();
 151:       
 152:       if (i < 0)
 153:         i = 0;
 154:       
 155:       ui.selectPreviousTabInRun(i);
 156: 
 157:     }    
 158:   }
 159:   
 160:   static class RequestFocusAction extends AbstractAction
 161:   {
 162: 
 163:     public RequestFocusAction()
 164:     {
 165:       super("requestFocus");
 166:     }
 167: 
 168:     public void actionPerformed(ActionEvent event)
 169:     {
 170:       ((JTabbedPane) event.getSource()).requestFocus();
 171:     }
 172:     
 173:   }
 174: 
 175:   static class RequestFocusForVisibleComponentAction extends AbstractAction
 176:   {
 177: 
 178:     public RequestFocusForVisibleComponentAction()
 179:     {
 180:       super("requestFocusForVisibleComponent");
 181:     }
 182: 
 183:     public void actionPerformed(ActionEvent event)
 184:     {
 185:       JTabbedPane tp = (JTabbedPane) event.getSource();
 186:       
 187:       // FIXME: This should select a suitable component within
 188:       // the tab content. However I dont know whether we have
 189:       // to search for this component or wether the called is
 190:       // supposed to do that.
 191:       tp.getSelectedComponent().requestFocus();
 192:     }
 193:     
 194:   }
 195: 
 196:   /**
 197:    * A helper class that handles focus. 
 198:    * <p>The purpose of this class is to implement a more flexible focus
 199:    * handling for the tabbed pane, which is used to determine whether the
 200:    * focus indicator should be painted or not. When in scrolling layout
 201:    * mode the area containing the tabs is a scrollpane, so simply testing
 202:    * whether the tabbed pane has the focus does not work.</p>
 203:    * <p>The <code>FocusHandler</code> is installed on the scrollpane and
 204:    * the tabbed pane and sets the variable <code>hasFocus</code> to
 205:    * <code>false</code> only when both components do not hold the focus.</p>
 206:    *
 207:    * @specnote Apparently this class was intended to be protected,
 208:    *           but was made public by a compiler bug and is now
 209:    *           public for compatibility.
 210:    */
 211:   public class FocusHandler extends FocusAdapter
 212:   {
 213:     /**
 214:      * This method is called when the component gains focus.
 215:      *
 216:      * @param e The FocusEvent.
 217:      */
 218:     public void focusGained(FocusEvent e)
 219:     {
 220:       Object source = e.getSource();
 221:       if (source == panel )
 222:         tabPane.requestFocus();
 223:       else if (source == tabPane)
 224:         tabPane.repaint();
 225:     }
 226: 
 227:     /**
 228:      * This method is called when the component loses focus.
 229:      *
 230:      * @param e The FocusEvent.
 231:      */
 232:     public void focusLost(FocusEvent e)
 233:     {
 234:       if (e.getOppositeComponent() == tabPane.getSelectedComponent())
 235:         tabPane.requestFocus();
 236:       else if (e.getSource() == tabPane)
 237:         tabPane.repaint();
 238:     }
 239:   }
 240: 
 241:   /**
 242:    * A helper class for determining if mouse presses occur inside tabs and
 243:    * sets the index appropriately. In SCROLL_TAB_MODE, this class also
 244:    * handles the mouse clicks on the scrolling buttons.
 245:    *
 246:    * @specnote Apparently this class was intended to be protected,
 247:    *           but was made public by a compiler bug and is now
 248:    *           public for compatibility.
 249:    */
 250:   public class MouseHandler extends MouseAdapter
 251:   {
 252:     public void mouseReleased(MouseEvent e)
 253:     {
 254:       Object s = e.getSource();
 255: 
 256:       // Event may originate from the viewport in
 257:       // SCROLL_TAB_LAYOUT mode. It is redisptached
 258:       // through the tabbed pane then.
 259:       if (tabPane != e.getSource())
 260:         {
 261:           redispatchEvent(e);
 262:           e.setSource(s);
 263:         }
 264:     }
 265:     
 266:     /**
 267:      * This method is called when the mouse is pressed. The index cannot
 268:      * change to a tab that is  not enabled.
 269:      *
 270:      * @param e The MouseEvent.
 271:      */
 272:     public void mousePressed(MouseEvent e)
 273:     {
 274:       Object s = e.getSource();
 275: 
 276:       // Event may originate from the viewport in
 277:       // SCROLL_TAB_LAYOUT mode. It is redisptached
 278:       // through the tabbed pane then.
 279:       if (tabPane != e.getSource())
 280:         {
 281:           redispatchEvent(e);
 282:           e.setSource(s);
 283:         }
 284:       
 285:       int placement = tabPane.getTabPlacement();
 286:     
 287:       if (s == incrButton)
 288:         {
 289:           if(!incrButton.isEnabled())
 290:             return;
 291: 
 292:           currentScrollLocation++;
 293: 
 294:           switch (placement)
 295:             {
 296:               case JTabbedPane.TOP:
 297:               case JTabbedPane.BOTTOM: 
 298:                 currentScrollOffset = getTabAreaInsets(placement).left;
 299:                 for (int i = 0; i < currentScrollLocation; i++)
 300:                   currentScrollOffset += rects[i].width;
 301:                 break;
 302:               default:
 303:                 currentScrollOffset = getTabAreaInsets(placement).top;
 304:                 for (int i = 0; i < currentScrollLocation; i++)
 305:                   currentScrollOffset += rects[i].height;
 306:                 break;
 307:             }
 308:             
 309:           updateViewPosition();
 310:           updateButtons();
 311:         
 312:           tabPane.repaint();
 313:         }
 314:       else if (s == decrButton)
 315:         {
 316:           if(!decrButton.isEnabled())
 317:             return;
 318:         
 319:            // The scroll location may be zero but the offset
 320:            // greater than zero because of an adjustement to
 321:            // make a partially visible tab completely visible.
 322:            if (currentScrollLocation > 0)
 323:              currentScrollLocation--;
 324:         
 325:            // Set the offset back to 0 and recompute it.
 326:            currentScrollOffset = 0;
 327: 
 328:            switch (placement)
 329:              {
 330:                case JTabbedPane.TOP:
 331:                case JTabbedPane.BOTTOM: 
 332:                  // Take the tab area inset into account.
 333:                  if (currentScrollLocation > 0)
 334:                    currentScrollOffset = getTabAreaInsets(placement).left;
 335:                  // Recompute scroll offset.
 336:                  for (int i = 0; i < currentScrollLocation; i++)
 337:                    currentScrollOffset += rects[i].width;
 338:                  break;
 339:                default:
 340:                  // Take the tab area inset into account.
 341:                  if (currentScrollLocation > 0)
 342:                    currentScrollOffset = getTabAreaInsets(placement).top;
 343:                 
 344:                  for (int i = 0; i < currentScrollLocation; i++)
 345:                    currentScrollOffset += rects[i].height;
 346:              }          
 347:         
 348:            updateViewPosition();
 349:            updateButtons();
 350:         
 351:            tabPane.repaint();
 352:         }
 353:       else if (tabPane.isEnabled())
 354:         {
 355:           int index = tabForCoordinate(tabPane, e.getX(), e.getY());
 356:           if (!tabPane.isEnabledAt(index))
 357:             return;
 358:           
 359:           if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT
 360:               && s == panel)
 361:             {
 362:               scrollTab(index, placement);
 363:               
 364:               tabPane.setSelectedIndex(index);
 365:               tabPane.repaint();
 366:             }
 367:           else
 368:             {
 369:               tabPane.setSelectedIndex(index);
 370:               tabPane.revalidate();
 371:               tabPane.repaint();
 372:             }
 373:           
 374:         }
 375:       
 376:     }
 377: 
 378:     /**
 379:      * Receives notification when the mouse pointer has entered the tabbed
 380:      * pane.
 381:      *
 382:      * @param e the mouse event
 383:      */
 384:     public void mouseEntered(MouseEvent e)
 385:     {
 386:       Object s = e.getSource();
 387: 
 388:       // Event may originate from the viewport in
 389:       // SCROLL_TAB_LAYOUT mode. It is redisptached
 390:       // through the tabbed pane then.
 391:       if (tabPane != e.getSource())
 392:         {
 393:           redispatchEvent(e);
 394:           e.setSource(s);
 395:         }
 396:       
 397:       int tabIndex = tabForCoordinate(tabPane, e.getX(), e.getY());
 398:       setRolloverTab(tabIndex);
 399:     }
 400: 
 401:     /**
 402:      * Receives notification when the mouse pointer has exited the tabbed
 403:      * pane.
 404:      *
 405:      * @param e the mouse event
 406:      */
 407:     public void mouseExited(MouseEvent e)
 408:     {
 409:       Object s = e.getSource();
 410: 
 411:       // Event may originate from the viewport in
 412:       // SCROLL_TAB_LAYOUT mode. It is redisptached
 413:       // through the tabbed pane then.
 414:       if (tabPane != e.getSource())
 415:         {
 416:           redispatchEvent(e);
 417:           e.setSource(s);
 418:         }
 419:       
 420:       setRolloverTab(-1);
 421:     }
 422: 
 423:     /**
 424:      * Receives notification when the mouse pointer has moved over the tabbed
 425:      * pane.
 426:      *
 427:      * @param ev the mouse event
 428:      */
 429:     public void mouseMoved(MouseEvent ev)
 430:     {
 431:       Object s = ev.getSource();
 432: 
 433:       if (tabPane != ev.getSource())
 434:         {
 435:           ev.setSource(tabPane);
 436:           tabPane.dispatchEvent(ev);
 437:           
 438:           ev.setSource(s);
 439:         }
 440: 
 441:       int tabIndex = tabForCoordinate(tabPane, ev.getX(), ev.getY());
 442:       setRolloverTab(tabIndex);
 443:     }
 444:     
 445:     /** Modifies the mouse event to originate from 
 446:      * the tabbed pane and redispatches it.
 447:      * 
 448:      * @param me
 449:      */
 450:     void redispatchEvent(MouseEvent me)
 451:     {
 452:       me.setSource(tabPane);
 453:       Point viewPos = viewport.getViewPosition();
 454:       viewPos.x -= viewport.getX();
 455:       viewPos.y -= viewport.getY();
 456:       me.translatePoint(-viewPos.x, -viewPos.y);
 457:       tabPane.dispatchEvent(me);
 458:       
 459:       me.translatePoint(viewPos.x, viewPos.y);
 460:     }
 461:     
 462:   }
 463: 
 464:   /**
 465:    * This class handles PropertyChangeEvents fired from the JTabbedPane.
 466:    *
 467:    * @specnote Apparently this class was intended to be protected,
 468:    *           but was made public by a compiler bug and is now
 469:    *           public for compatibility.
 470:    */
 471:   public class PropertyChangeHandler implements PropertyChangeListener
 472:   {
 473:     /**
 474:      * This method is called whenever one of the properties of the JTabbedPane
 475:      * changes.
 476:      *
 477:      * @param e The PropertyChangeEvent.
 478:      */
 479:     public void propertyChange(PropertyChangeEvent e)
 480:     {
 481:       out:
 482:         {
 483:           if (e.getPropertyName().equals("tabLayoutPolicy"))
 484:             {
 485:               currentScrollLocation = currentScrollOffset = 0;
 486:               
 487:               layoutManager = createLayoutManager();
 488:               
 489:               tabPane.setLayout(layoutManager);
 490:             }
 491:           else if (e.getPropertyName().equals("tabPlacement")
 492:                    && tabPane.getTabLayoutPolicy() 
 493:                    == JTabbedPane.SCROLL_TAB_LAYOUT)
 494:             {
 495:               incrButton = createIncreaseButton();
 496:               decrButton = createDecreaseButton();
 497:               
 498:               // If the tab placement value was changed of a tabbed pane
 499:               // in SCROLL_TAB_LAYOUT mode we investigate the change to
 500:               // implement the following behavior which was observed in
 501:               // the RI:
 502:               // The scrolling offset will be reset if we change to
 503:               // a direction which is orthogonal to the current
 504:               // direction and stays the same if it is parallel.
 505:               
 506:               int oldPlacement = ((Integer) e.getOldValue()).intValue();
 507:               int newPlacement = ((Integer) e.getNewValue()).intValue();
 508:               switch (newPlacement)
 509:                 {
 510:                   case JTabbedPane.TOP:
 511:                   case JTabbedPane.BOTTOM:
 512:                     if (oldPlacement == JTabbedPane.TOP
 513:                         || oldPlacement == JTabbedPane.BOTTOM)
 514:                       break out;
 515:                   
 516:                     currentScrollOffset = getTabAreaInsets(newPlacement).left;
 517:                     break;
 518:                   default:
 519:                     if (oldPlacement == JTabbedPane.LEFT
 520:                        || oldPlacement == JTabbedPane.RIGHT)
 521:                       break out;
 522:                   
 523:                     currentScrollOffset = getTabAreaInsets(newPlacement).top;
 524:                 }
 525:               
 526:               updateViewPosition();
 527:               updateButtons();
 528:             }
 529:         }
 530:     
 531:       tabPane.revalidate();
 532:       tabPane.repaint();
 533:     }
 534:   }
 535: 
 536:   /**
 537:    * A LayoutManager responsible for placing all the tabs and the visible
 538:    * component inside the JTabbedPane. This class is only used for
 539:    * WRAP_TAB_LAYOUT.
 540:    *
 541:    * @specnote Apparently this class was intended to be protected,
 542:    *           but was made public by a compiler bug and is now
 543:    *           public for compatibility.
 544:    */
 545:   public class TabbedPaneLayout implements LayoutManager
 546:   {
 547:     /**
 548:      * This method is called when a component is added to the JTabbedPane.
 549:      *
 550:      * @param name The name of the component.
 551:      * @param comp The component being added.
 552:      */
 553:     public void addLayoutComponent(String name, Component comp)
 554:     {
 555:       // Do nothing.
 556:     }
 557: 
 558:     /**
 559:      * This method is called when the rectangles need to be calculated. It
 560:      * also fixes the size of the visible component.
 561:      */
 562:     public void calculateLayoutInfo()
 563:     {
 564:       int count = tabPane.getTabCount();
 565:       assureRectsCreated(count);
 566:       calculateTabRects(tabPane.getTabPlacement(), count);
 567:       tabRunsDirty = false;
 568:     }
 569: 
 570:     /**
 571:      * This method calculates the size of the the JTabbedPane.
 572:      *
 573:      * @param minimum Whether the JTabbedPane will try to be as small as it
 574:      *        can.
 575:      *
 576:      * @return The desired size of the JTabbedPane.
 577:      */
 578:     protected Dimension calculateSize(boolean minimum)
 579:     {
 580:       int tabPlacement = tabPane.getTabPlacement();
 581: 
 582:       int width = 0;
 583:       int height = 0;
 584:       Component c;
 585:       Dimension dims;
 586: 
 587:       // Find out the minimum/preferred size to display the largest child
 588:       // of the tabbed pane.
 589:       int count = tabPane.getTabCount();
 590:       for (int i = 0; i < count; i++)
 591:         {
 592:           c = tabPane.getComponentAt(i);
 593:           if (c == null)
 594:             continue;
 595:           dims = minimum ? c.getMinimumSize() : c.getPreferredSize(); 
 596:           if (dims != null)
 597:             {
 598:               height = Math.max(height, dims.height);
 599:               width = Math.max(width, dims.width);
 600:             }
 601:         }
 602: 
 603:       Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
 604:       if (tabPlacement == SwingConstants.TOP
 605:           || tabPlacement == SwingConstants.BOTTOM)
 606:         {
 607:           width = Math.max(calculateMaxTabWidth(tabPlacement), width);
 608:           
 609:           height += preferredTabAreaHeight(tabPlacement,
 610:                                            width - tabAreaInsets.left
 611:                                            - tabAreaInsets.right);
 612:         }
 613:       else
 614:         {
 615:           height = Math.max(calculateMaxTabHeight(tabPlacement), height);
 616:           
 617:           width += preferredTabAreaWidth(tabPlacement,
 618:                                          height - tabAreaInsets.top
 619:                                          - tabAreaInsets.bottom);
 620:         }
 621: 
 622:       Insets tabPaneInsets = tabPane.getInsets();
 623:       return new Dimension(width + tabPaneInsets.left + tabPaneInsets.right,
 624:                            height + tabPaneInsets.top + tabPaneInsets.bottom);
 625:     }
 626: 
 627:     // if tab placement is LEFT OR RIGHT, they share width.
 628:     // if tab placement is TOP OR BOTTOM, they share height
 629:     // PRE STEP: finds the default sizes for the labels as well as their
 630:     // locations.
 631:     // AND where they will be placed within the run system.
 632:     // 1. calls normalizeTab Runs.
 633:     // 2. calls rotate tab runs.
 634:     // 3. pads the tab runs.
 635:     // 4. pads the selected tab.
 636: 
 637:     /**
 638:      * This method is called to calculate the tab rectangles.  This method
 639:      * will calculate the size and position of all  rectangles (taking into
 640:      * account which ones should be in which tab run). It will pad them and
 641:      * normalize them  as necessary.
 642:      *
 643:      * @param tabPlacement The JTabbedPane's tab placement.
 644:      * @param tabCount The run the current selection is in.
 645:      */
 646:     protected void calculateTabRects(int tabPlacement, int tabCount)
 647:     {
 648:       Insets insets = tabPane.getInsets();
 649:       Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
 650:       Dimension size = tabPane.getSize();
 651:       
 652:       // The coordinates of the upper left corner of the tab area.
 653:       int x;
 654:       int y;
 655:       // The location at which the runs must be broken.
 656:       int breakAt;
 657: 
 658:       // Calculate the bounds for the tab area.
 659:       switch (tabPlacement)
 660:       {
 661:         case LEFT:
 662:           maxTabWidth = calculateMaxTabWidth(tabPlacement);
 663:           x = insets.left + tabAreaInsets.left;
 664:           y = insets.top + tabAreaInsets.top;
 665:           breakAt = size.height - (insets.bottom + tabAreaInsets.bottom);
 666:           break;
 667:         case RIGHT:
 668:           maxTabWidth = calculateMaxTabWidth(tabPlacement);
 669:           x = size.width - (insets.right + tabAreaInsets.right)
 670:               - maxTabWidth - 1;
 671:           y = insets.top + tabAreaInsets.top;
 672:           breakAt = size.height - (insets.bottom + tabAreaInsets.bottom);
 673:           break;
 674:         case BOTTOM:
 675:           maxTabHeight = calculateMaxTabHeight(tabPlacement);
 676:           x = insets.left + tabAreaInsets.left;
 677:           y = size.height - (insets.bottom + tabAreaInsets.bottom)
 678:               - maxTabHeight - 1;
 679:           breakAt = size.width - (insets.right + tabAreaInsets.right);
 680:           break;
 681:         case TOP:
 682:         default:
 683:           maxTabHeight = calculateMaxTabHeight(tabPlacement);
 684:           x = insets.left + tabAreaInsets.left;
 685:           y = insets.top + tabAreaInsets.top;
 686:           breakAt = size.width - (insets.right + tabAreaInsets.right);
 687:           break;
 688:       }
 689: 
 690:       if (tabCount == 0)
 691:         return;
 692: 
 693:       FontMetrics fm = getFontMetrics();
 694:       runCount = 0;
 695:       selectedRun = -1;
 696:       int selectedIndex = tabPane.getSelectedIndex();
 697:       if (selectedIndex < 0)
 698:           selectedIndex = 0;
 699: 
 700:       Rectangle rect;
 701: 
 702:       // Go through all the tabs and build the tab runs.
 703:       if (tabPlacement == SwingConstants.TOP
 704:           || tabPlacement == SwingConstants.BOTTOM)
 705:         {
 706:           for (int i = 0; i < tabCount; i++)
 707:             {
 708:               rect = rects[i];
 709:               if (i > 0)
 710:                 {
 711:                   rect.x = rects[i - 1].x + rects[i - 1].width;
 712:                 }
 713:               else
 714:                 {
 715:                   tabRuns[0] = 0;
 716:                   runCount = 1;
 717:                   maxTabWidth = 0;
 718:                   rect.x = x;
 719:                 }
 720:               rect.width = calculateTabWidth(tabPlacement, i, fm);
 721:               maxTabWidth = Math.max(maxTabWidth, rect.width);
 722: 
 723:               if (rect.x != 2 + insets.left && rect.x + rect.width > breakAt)
 724:                 {
 725:                   if (runCount > tabRuns.length - 1)
 726:                     expandTabRunsArray();
 727:                   tabRuns[runCount] = i;
 728:                   runCount++;
 729:                   rect.x = x;
 730:                 }
 731: 
 732:               rect.y = y;
 733:               rect.height = maxTabHeight;
 734:               if (i == selectedIndex)
 735:                 selectedRun = runCount - 1;
 736:             }
 737:         }
 738:       else
 739:         {
 740:           for (int i = 0; i < tabCount; i++)
 741:             {
 742:               rect = rects[i];
 743:               if (i > 0)
 744:                 {
 745:                   rect.y = rects[i - 1].y + rects[i - 1].height;
 746:                 }
 747:               else
 748:                 {
 749:                   tabRuns[0] = 0;
 750:                   runCount = 1;
 751:                   maxTabHeight = 0;
 752:                   rect.y = y;
 753:                 }
 754:               rect.height = calculateTabHeight(tabPlacement, i,
 755:                                                fm.getHeight());
 756:               maxTabHeight = Math.max(maxTabHeight, rect.height);
 757: 
 758:               if (rect.y != 2 + insets.top && rect.y + rect.height > breakAt)
 759:                 {
 760:                   if (runCount > tabRuns.length - 1)
 761:                     expandTabRunsArray();
 762:                   tabRuns[runCount] = i;
 763:                   runCount++;
 764:                   rect.y = y;
 765:                 }
 766: 
 767:               rect.x = x;
 768:               rect.width = maxTabWidth;
 769: 
 770:               if (i == selectedIndex)
 771:                 selectedRun = runCount - 1;
 772:             }
 773:         }
 774: 
 775:       if (runCount > 1)
 776:         {
 777:           int start;
 778:           if  (tabPlacement == SwingConstants.TOP
 779:               || tabPlacement == SwingConstants.BOTTOM)
 780:             start = x;
 781:           else
 782:             start = y;
 783:           normalizeTabRuns(tabPlacement, tabCount, start, breakAt);
 784:           selectedRun = getRunForTab(tabCount, selectedIndex);
 785:           if (shouldRotateTabRuns(tabPlacement))
 786:             {
 787:               rotateTabRuns(tabPlacement, selectedRun);
 788:             }
 789:         }
 790:       
 791:       // Suppress padding if we have only one tab run.
 792:       if (runCount == 1)
 793:         return;
 794:       
 795:       // Pad the runs.
 796:       int tabRunOverlay = getTabRunOverlay(tabPlacement);
 797:       for (int i = runCount - 1; i >= 0; --i)
 798:         {
 799:           int start = tabRuns[i];
 800:           int nextIndex;
 801:           if (i == runCount - 1)
 802:             nextIndex = 0;
 803:           else
 804:             nextIndex = i + 1;
 805:           int next = tabRuns[nextIndex];
 806:           int end = next != 0 ? next - 1 : tabCount - 1;
 807:           if (tabPlacement == SwingConstants.TOP
 808:               || tabPlacement == SwingConstants.BOTTOM)
 809:             {
 810:               for (int j = start; j <= end; ++j)
 811:                 {
 812:                   rect = rects[j];
 813:                   rect.y = y;
 814:                   rect.x += getTabRunIndent(tabPlacement, i);
 815:                 }
 816:               if (shouldPadTabRun(tabPlacement, i))
 817:                 {
 818:                   padTabRun(tabPlacement, start, end, breakAt);
 819:                 }
 820:               if (tabPlacement == BOTTOM)
 821:                 y -= maxTabHeight - tabRunOverlay;
 822:               else
 823:                 y += maxTabHeight - tabRunOverlay;
 824:             }
 825:           else
 826:             {
 827:               for (int j = start; j <= end; ++j)
 828:                 {
 829:                   rect = rects[j];
 830:                   rect.x = x;
 831:                   rect.y += getTabRunIndent(tabPlacement, i);
 832:                 }
 833:               if (shouldPadTabRun(tabPlacement, i))
 834:                 {
 835:                   padTabRun(tabPlacement, start, end, breakAt);
 836:                 }
 837:               if (tabPlacement == RIGHT)
 838:                 x -= maxTabWidth - tabRunOverlay;
 839:               else
 840:                 x += maxTabWidth - tabRunOverlay;
 841:               
 842:             }
 843:         }
 844:       padSelectedTab(tabPlacement, selectedIndex);
 845:     }
 846: 
 847:     /**
 848:      * This method is called when the JTabbedPane is laid out in
 849:      * WRAP_TAB_LAYOUT. It calls calculateLayoutInfo to  find the positions
 850:      * of all its components.
 851:      *
 852:      * @param parent The Container to lay out.
 853:      */
 854:     public void layoutContainer(Container