Source for javax.swing.plaf.basic.BasicTabbedPaneUI

   1: /* BasicTabbedPaneUI.java --
   2:    Copyright (C) 2002, 2004, 2005  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.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;
  60: 
  61: import javax.swing.Icon;
  62: import javax.swing.JComponent;
  63: import javax.swing.JPanel;
  64: import javax.swing.JTabbedPane;
  65: import javax.swing.JViewport;
  66: import javax.swing.KeyStroke;
  67: import javax.swing.SwingConstants;
  68: import javax.swing.SwingUtilities;
  69: import javax.swing.UIDefaults;
  70: import javax.swing.UIManager;
  71: import javax.swing.event.ChangeEvent;
  72: import javax.swing.event.ChangeListener;
  73: import javax.swing.plaf.ComponentUI;
  74: import javax.swing.plaf.PanelUI;
  75: import javax.swing.plaf.TabbedPaneUI;
  76: import javax.swing.plaf.UIResource;
  77: import javax.swing.text.View;
  78: 
  79: /**
  80:  * This is the Basic Look and Feel's UI delegate for JTabbedPane.
  81:  */
  82: public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
  83: {
  84:   /**
  85:    * A helper class that handles focus.
  86:    *
  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.
  90:    */
  91:   public class FocusHandler extends FocusAdapter
  92:   {
  93:     /**
  94:      * This method is called when the component gains focus.
  95:      *
  96:      * @param e The FocusEvent.
  97:      */
  98:     public void focusGained(FocusEvent e)
  99:     {
 100:       // FIXME: Implement.
 101:     }
 102: 
 103:     /**
 104:      * This method is called when the component loses focus.
 105:      *
 106:      * @param e The FocusEvent.
 107:      */
 108:     public void focusLost(FocusEvent e)
 109:     {
 110:       // FIXME: Implement.
 111:     }
 112:   }
 113: 
 114:   /**
 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.
 118:    *
 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.
 122:    */
 123:   public class MouseHandler extends MouseAdapter
 124:   {
 125:     /**
 126:      * This method is called when the mouse is pressed. The index cannot
 127:      * change to a tab that is  not enabled.
 128:      *
 129:      * @param e The MouseEvent.
 130:      */
 131:     public void mousePressed(MouseEvent e)
 132:     {
 133:       int x = e.getX();
 134:       int y = e.getY();
 135:       int tabCount = tabPane.getTabCount();
 136: 
 137:       if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
 138:         {
 139:       if (e.getSource() == incrButton)
 140:         {
 141:           if (++currentScrollLocation >= tabCount)
 142:         currentScrollLocation = tabCount - 1;
 143: 
 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;
 156:         }
 157:       else if (e.getSource() == decrButton)
 158:         {
 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;
 168:         }
 169:         }
 170: 
 171:       int index = tabForCoordinate(tabPane, x, y);
 172: 
 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();
 179:     }
 180:   }
 181: 
 182:   /**
 183:    * This class handles PropertyChangeEvents fired from the JTabbedPane.
 184:    *
 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.
 188:    */
 189:   public class PropertyChangeHandler implements PropertyChangeListener
 190:   {
 191:     /**
 192:      * This method is called whenever one of the properties of the JTabbedPane
 193:      * changes.
 194:      *
 195:      * @param e The PropertyChangeEvent.
 196:      */
 197:     public void propertyChange(PropertyChangeEvent e)
 198:     {
 199:       if (e.getPropertyName().equals("tabLayoutPolicy"))
 200:         {
 201:       layoutManager = createLayoutManager();
 202: 
 203:       tabPane.setLayout(layoutManager);
 204:         }
 205:       else if (e.getPropertyName().equals("tabPlacement")
 206:                && tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
 207:         {
 208:       incrButton = createIncreaseButton();
 209:       decrButton = createDecreaseButton();
 210:         }
 211:       tabPane.layout();
 212:       tabPane.repaint();
 213:     }
 214:   }
 215: 
 216:   /**
 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.
 220:    *
 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.
 224:    */
 225:   public class TabbedPaneLayout implements LayoutManager
 226:   {
 227:     /**
 228:      * This method is called when a component is added to the JTabbedPane.
 229:      *
 230:      * @param name The name of the component.
 231:      * @param comp The component being added.
 232:      */
 233:     public void addLayoutComponent(String name, Component comp)
 234:     {
 235:       // Do nothing.
 236:     }
 237: 
 238:     /**
 239:      * This method is called when the rectangles need to be calculated. It
 240:      * also fixes the size of the visible component.
 241:      */
 242:     public void calculateLayoutInfo()
 243:     {
 244:       calculateTabRects(tabPane.getTabPlacement(), tabPane.getTabCount());
 245: 
 246:       if (tabPane.getSelectedIndex() != -1)
 247:         {
 248:       Component visible = getVisibleComponent();
 249:       Insets insets = getContentBorderInsets(tabPane.getTabPlacement());
 250:       if (visible != null)
 251:         visible.setBounds(contentRect.x + insets.left,
 252:                           contentRect.y + insets.top,
 253:                           contentRect.width - insets.left - insets.right,
 254:                           contentRect.height - insets.top - insets.bottom);
 255:         }
 256:     }
 257: 
 258:     /**
 259:      * This method calculates the size of the the JTabbedPane.
 260:      *
 261:      * @param minimum Whether the JTabbedPane will try to be as small as it
 262:      *        can.
 263:      *
 264:      * @return The desired size of the JTabbedPane.
 265:      */
 266:     protected Dimension calculateSize(boolean minimum)
 267:     {
 268:       int tabPlacement = tabPane.getTabPlacement();
 269:       int width = 0;
 270:       int height = 0;
 271: 
 272:       int componentHeight = 0;
 273:       int componentWidth = 0;
 274:       Component c;
 275:       Dimension dims;
 276:       for (int i = 0; i < tabPane.getTabCount(); i++)
 277:         {
 278:       c = tabPane.getComponentAt(i);
 279:       if (c == null)
 280:         continue;
 281:       calcRect = c.getBounds();
 282:       dims = c.getPreferredSize();
 283:       if (dims != null)
 284:         {
 285:           componentHeight = Math.max(componentHeight, dims.height);
 286:           componentWidth = Math.max(componentWidth, dims.width);
 287:         }
 288:         }
 289:       Insets insets = tabPane.getInsets();
 290: 
 291:       if (tabPlacement == SwingConstants.TOP
 292:           || tabPlacement == SwingConstants.BOTTOM)
 293:         {
 294:       int min = calculateMaxTabWidth(tabPlacement);
 295:       width = Math.max(min, componentWidth);
 296: 
 297:       int tabAreaHeight = preferredTabAreaHeight(tabPlacement, width);
 298:       height = tabAreaHeight + componentHeight;
 299:         }
 300:       else
 301:         {
 302:       int min = calculateMaxTabHeight(tabPlacement);
 303:       height = Math.max(min, componentHeight);
 304: 
 305:       int tabAreaWidth = preferredTabAreaWidth(tabPlacement, height);
 306:       width = tabAreaWidth + componentWidth;
 307:         }
 308: 
 309:       return new Dimension(width, height);
 310:     }
 311: 
 312:     // if tab placement is LEFT OR RIGHT, they share width.
 313:     // if tab placement is TOP OR BOTTOM, they share height
 314:     // PRE STEP: finds the default sizes for the labels as well as their locations.
 315:     // AND where they will be placed within the run system.
 316:     // 1. calls normalizeTab Runs.
 317:     // 2. calls rotate tab runs.
 318:     // 3. pads the tab runs.
 319:     // 4. pads the selected tab.
 320: 
 321:     /**
 322:      * This method is called to calculate the tab rectangles.  This method
 323:      * will calculate the size and position of all  rectangles (taking into
 324:      * account which ones should be in which tab run). It will pad them and
 325:      * normalize them  as necessary.
 326:      *
 327:      * @param tabPlacement The JTabbedPane's tab placement.
 328:      * @param tabCount The run the current selection is in.
 329:      */
 330:     protected void calculateTabRects(int tabPlacement, int tabCount)
 331:     {
 332:       if (tabCount == 0)
 333:     return;
 334:       assureRectsCreated(tabCount);
 335: 
 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)
 345:         {
 346:       int maxHeight = calculateMaxTabHeight(tabPlacement);
 347: 
 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;
 353: 
 354:       for (int i = 0; i < tabCount; i++)
 355:         {
 356:           width = calculateTabWidth(tabPlacement, i, fm);
 357: 
 358:           if (runWidth + width > max)
 359:             {
 360:           runWidth = tabAreaInsets.left + insets.left
 361:                      + getTabRunIndent(tabPlacement, ++runs);
 362:           rects[i] = new Rectangle(runWidth,
 363:                                    insets.top + tabAreaInsets.top,
 364:                                    width, maxHeight);
 365:           runWidth += width;
 366:           if (runs > tabRuns.length - 1)
 367:             expandTabRunsArray();
 368:           tabRuns[runs] = i;
 369:             }
 370:           else
 371:             {
 372:           rects[i] = new Rectangle(runWidth,
 373:                                    insets.top + tabAreaInsets.top,
 374:                                    width, maxHeight);
 375:           runWidth += width;
 376:             }
 377:         }
 378:       runs++;
 379:       tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right;
 380:       tabAreaRect.height = runs * maxTabHeight
 381:                            - (runs - 1) * tabRunOverlay
 382:                            + tabAreaInsets.top + tabAreaInsets.bottom;
 383:       contentRect.width = tabAreaRect.width;
 384:       contentRect.height = tabPane.getHeight() - insets.top
 385:                            - insets.bottom - tabAreaRect.height;
 386:       contentRect.x = insets.left;
 387:       tabAreaRect.x = insets.left;
 388:       if (tabPlacement == SwingConstants.BOTTOM)
 389:         {
 390:           contentRect.y = insets.top;
 391:           tabAreaRect.y = contentRect.y + contentRect.height;
 392:         }
 393:       else
 394:         {
 395:           tabAreaRect.y = insets.top;
 396:           contentRect.y = tabAreaRect.y + tabAreaRect.height;
 397:         }
 398:         }
 399:       else
 400:         {
 401:       int maxWidth = calculateMaxTabWidth(tabPlacement);
 402:       calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom;
 403:       max = calcRect.height + tabAreaInsets.top + insets.top;
 404: 
 405:       int height = 0;
 406:       start += tabAreaInsets.top + insets.top;
 407:       int runHeight = start;
 408: 
 409:       int fontHeight = fm.getHeight();
 410: 
 411:       for (int i = 0; i < tabCount; i++)
 412:         {
 413:           height = calculateTabHeight(tabPlacement, i, fontHeight);
 414:           if (runHeight + height > max)
 415:             {
 416:           runHeight = tabAreaInsets.top + insets.top
 417:                       + getTabRunIndent(tabPlacement, ++runs);
 418:           rects[i] = new Rectangle(insets.left + tabAreaInsets.left,
 419:                                    runHeight, maxWidth, height);
 420:           runHeight += height;
 421:           if (runs > tabRuns.length - 1)
 422:             expandTabRunsArray();
 423:           tabRuns[runs] = i;
 424:             }
 425:           else
 426:             {
 427:           rects[i] = new Rectangle(insets.left + tabAreaInsets.left,
 428:                                    runHeight, maxWidth, height);
 429:           runHeight += height;
 430:             }
 431:         }
 432:       runs++;
 433: 
 434:       tabAreaRect.width = runs * maxTabWidth - (runs - 1) * tabRunOverlay
 435:                           + tabAreaInsets.left + tabAreaInsets.right;
 436:       tabAreaRect.height = tabPane.getHeight() - insets.top
 437:                            - insets.bottom;
 438:       tabAreaRect.y = insets.top;
 439:       contentRect.width = tabPane.getWidth() - insets.left - insets.right
 440:                           - tabAreaRect.width;
 441:       contentRect.height = tabAreaRect.height;
 442:       contentRect.y = insets.top;
 443:       if (tabPlacement == SwingConstants.LEFT)
 444:         {
 445:           tabAreaRect.x = insets.left;
 446:           contentRect.x = tabAreaRect.x + tabAreaRect.width;
 447:         }
 448:       else
 449:         {
 450:           contentRect.x = insets.left;
 451:           tabAreaRect.x = contentRect.x + contentRect.width;
 452:         }
 453:         }
 454:       runCount = runs;
 455: 
 456:       tabRuns[0] = 0;
 457:       normalizeTabRuns(tabPlacement, tabCount, start, max);
 458:       selectedRun = getRunForTab(tabCount, tabPane.getSelectedIndex());
 459:       if (shouldRotateTabRuns(tabPlacement))
 460:     rotateTabRuns(tabPlacement, selectedRun);
 461: 
 462:       // Need to pad the runs and move them to the correct location.
 463:       for (int i = 0; i < runCount; i++)
 464:         {
 465:       int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1;
 466:       if (first == tabCount)
 467:         first = 0;
 468:       int last = lastTabInRun(tabCount, i);
 469:       if (shouldPadTabRun(tabPlacement, i))
 470:         padTabRun(tabPlacement, first, last, max);
 471: 
 472:       // Done padding, now need to move it.
 473:       if (tabPlacement == SwingConstants.TOP && i > 0)
 474:         {
 475:           for (int j = first; j <= last; j++)
 476:         rects[j].y += (runCount - i) * maxTabHeight
 477:         - (runCount - i) * tabRunOverlay;
 478:         }
 479: 
 480:       if (tabPlacement == SwingConstants.BOTTOM)
 481:         {
 482:           int height = tabPane.getBounds().height - insets.bottom
 483:                        - tabAreaInsets.bottom;
 484:           int adjustment;
 485:           if (i == 0)
 486:         adjustment = height - maxTabHeight;
 487:           else
 488:         adjustment = height - (runCount - i + 1) * maxTabHeight
 489:                      - (runCount - i) * tabRunOverlay;
 490: 
 491:           for (int j = first; j <= last; j++)
 492:         rects[j].y = adjustment;
 493:         }
 494: 
 495:       if (tabPlacement == SwingConstants.LEFT && i > 0)
 496:         {
 497:           for (int j = first; j <= last; j++)
 498:         rects[j].x += (runCount - i) * maxTabWidth
 499:         - (runCount - i) * tabRunOverlay;
 500:         }
 501: 
 502:       if (tabPlacement == SwingConstants.RIGHT)
 503:         {
 504:           int width = tabPane.getBounds().width - insets.right
 505:                       - tabAreaInsets.right;
 506:           int adjustment;
 507:           if (i == 0)
 508:         adjustment = width - maxTabWidth;
 509:           else
 510:         adjustment = width - (runCount - i + 1) * maxTabWidth
 511:                      + (runCount - i) * tabRunOverlay;
 512: 
 513:           for (int j = first; j <= last; j++)
 514:         rects[j].x = adjustment;
 515:         }
 516:         }
 517:       padSelectedTab(tabPlacement, tabPane.getSelectedIndex());
 518:     }
 519: 
 520:     /**
 521:      * This method is called when the JTabbedPane is laid out in
 522:      * WRAP_TAB_LAYOUT. It calls calculateLayoutInfo to  find the positions
 523:      * of all its components.
 524:      *
 525:      * @param parent The Container to lay out.
 526:      */
 527:     public void layoutContainer(Container parent)
 528:     {
 529:       calculateLayoutInfo();
 530:     }
 531: 
 532:     /**
 533:      * This method returns the minimum layout size for the given container.
 534:      *
 535:      * @param parent The container that is being sized.
 536:      *
 537:      * @return The minimum size.
 538:      */
 539:     public Dimension minimumLayoutSize(Container parent)
 540:     {
 541:       return calculateSize(false);
 542:     }
 543: 
 544:     // If there is more free space in an adjacent run AND the tab in the run can fit in the 
 545:     // adjacent run, move it. This method is not perfect, it is merely an approximation.
 546:     // If you play around with Sun's JTabbedPane, you'll see that 
 547:     // it does do some pretty strange things with regards to not moving tabs 
 548:     // that should be moved. 
 549:     // start = the x position where the tabs will begin
 550:     // max = the maximum position of where the tabs can go to (tabAreaInsets.left + the width of the tab area)
 551: 
 552:     /**
 553:      * This method tries to "even out" the number of tabs in each run based on
 554:      * their widths.
 555:      *
 556:      * @param tabPlacement The JTabbedPane's tab placement.
 557:      * @param tabCount The number of tabs.
 558:      * @param start The x position where the tabs will begin.
 559:      * @param max The maximum x position where the tab can run to.
 560:      */
 561:     protected void normalizeTabRuns(int tabPlacement, int tabCount, int start,
 562:                                     int max)
 563:     {
 564:       Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
 565:       if (tabPlacement == SwingUtilities.TOP
 566:           || tabPlacement == SwingUtilities.BOTTOM)
 567:         {
 568:       // We should only do this for runCount - 1, cause we can only shift that many times between
 569:       // runs.
 570:       for (int i = 1; i < runCount; i++)
 571:         {
 572:           Rectangle currRun = rects[lastTabInRun(tabCount, i)];
 573:           Rectangle nextRun = rects[lastTabInRun(tabCount, getNextTabRun(i))];
 574:           int spaceInCurr = currRun.x + currRun.width;
 575:           int spaceInNext = nextRun.x + nextRun.width;
 576: 
 577:           int diffNow = spaceInCurr - spaceInNext;
 578:           int diffLater = (spaceInCurr - currRun.width)
 579:                           - (spaceInNext + currRun.width);
 580:           while (Math.abs(diffLater) < Math.abs(diffNow)
 581:                  && spaceInNext + currRun.width < max)
 582:             {
 583:           tabRuns[i]--;
 584:           spaceInNext += currRun.width;
 585:           spaceInCurr -= currRun.width;
 586:           currRun = rects[lastTabInRun(tabCount, i)];
 587:           diffNow = spaceInCurr - spaceInNext;
 588:           diffLater = (spaceInCurr - currRun.width)
 589:                       - (spaceInNext + currRun.width);
 590:             }
 591: 
 592:           // Fix the bounds.
 593:           int first = lastTabInRun(tabCount, i) + 1;
 594:           int last = lastTabInRun(tabCount, getNextTabRun(i));
 595:           int currX = tabAreaInsets.left;
 596:           for (int j = first; j <= last; j++)
 597:             {
 598:           rects[j].x = currX;
 599:           currX += rects[j].width;
 600:             }
 601:         }
 602:         }
 603:       else
 604:         {
 605:       for (int i = 1; i < runCount; i++)
 606:         {
 607:           Rectangle currRun = rects[lastTabInRun(tabCount, i)];
 608:           Rectangle nextRun = rects[lastTabInRun(tabCount, getNextTabRun(i))];
 609:           int spaceInCurr = currRun.y + currRun.height;
 610:           int spaceInNext = nextRun.y + nextRun.height;
 611: 
 612:           int diffNow = spaceInCurr - spaceInNext;
 613:           int diffLater = (spaceInCurr - currRun.height)
 614:                           - (spaceInNext + currRun.height);
 615:           while (Math.abs(diffLater) < Math.abs(diffNow)
 616:                  && spaceInNext + currRun.height < max)
 617:             {
 618:           tabRuns[i]--;
 619:           spaceInNext += currRun.height;
 620:           spaceInCurr -= currRun.height;
 621:           currRun = rects[lastTabInRun(tabCount, i)];
 622:           diffNow = spaceInCurr - spaceInNext;
 623:           diffLater = (spaceInCurr - currRun.height)
 624:                       - (spaceInNext + currRun.height);
 625:             }
 626: 
 627:           int first = lastTabInRun(tabCount, i) + 1;
 628:           int last = lastTabInRun(tabCount, getNextTabRun(i));
 629:           int currY = tabAreaInsets.top;
 630:           for (int j = first; j <= last; j++)
 631:             {
 632:           rects[j].y = currY;
 633:           currY += rects[j].height;
 634:             }
 635:         }
 636:         }
 637:     }
 638: 
 639:     /**
 640:      * This method pads the tab at the selected index by the  selected tab pad
 641:      * insets (so that it looks larger).
 642:      *
 643:      * @param tabPlacement The placement of the tabs.
 644:      * @param selectedIndex The selected index.
 645:      */
 646:     protected void padSelectedTab(int tabPlacement, int selectedIndex)
 647:     {
 648:       Insets insets = getSelectedTabPadInsets(tabPlacement);
 649:       rects[selectedIndex].x -= insets.left;
 650:       rects[selectedIndex].y -= insets.top;
 651:       rects[selectedIndex].width += insets.left + insets.right;
 652:       rects[selectedIndex].height += insets.top + insets.bottom;
 653:     }
 654: 
 655:     // If the tabs on the run don't fill the width of the window, make it fit now.
 656:     // start = starting index of the run
 657:     // end = last index of the run
 658:     // max = tabAreaInsets.left + width (or equivalent)
 659:     // assert start <= end.
 660: 
 661:     /**
 662:      * This method makes each tab in the run larger so that the  tabs expand
 663:      * to fill the runs width/height (depending on tabPlacement).
 664:      *
 665:      * @param tabPlacement The placement of the tabs.
 666:      * @param start The index of the first tab.
 667:      * @param end The last index of the tab
 668:      * @param max The amount of space in the run (width for TOP and BOTTOM
 669:      *        tabPlacement).
 670:      */
 671:     protected void padTabRun(int tabPlacement, int start, int end, int max)
 672:     {
 673:       if (tabPlacement == SwingConstants.TOP
 674:           || tabPlacement == SwingConstants.BOTTOM)
 675:         {
 676:       int runWidth = rects[end].x + rects[end].width;
 677:       int spaceRemaining = max - runWidth;
 678:       int numTabs = end - start + 1;
 679: 
 680:       // now divvy up the space.
 681:       int spaceAllocated = spaceRemaining / numTabs;
 682:       int currX = rects[start].x;
 683:       for (int i = start; i <= end; i++)
 684:         {
 685:           rects[i].x = currX;
 686:           rects[i].width += spaceAllocated;
 687:           currX += rects[i].width;
 688:           // This is used because since the spaceAllocated 
 689:           // variable is an int, it rounds down. Sometimes,
 690:           // we don't fill an entire row, so we make it do
 691:           // so now.
 692:           if (i == end && rects[i].x + rects[i].width != max)
 693:         rects[i].width = max - rects[i].x;
 694:         }
 695:         }
 696:       else
 697:         {
 698:       int runHeight = rects[end].y + rects[end].height;
 699:       int spaceRemaining = max - runHeight;
 700:       int numTabs = end - start + 1;
 701: 
 702:       int spaceAllocated = spaceRemaining / numTabs;
 703:       int currY = rects[start].y;
 704:       for (int i = start; i <= end; i++)
 705:         {
 706:           rects[i].y = currY;
 707:           rects[i].height += spaceAllocated;
 708:           currY += rects[i].height;
 709:           if (i == end && rects[i].y + rects[i].height != max)
 710:         rects[i].height = max - rects[i].y;
 711:         }
 712:         }
 713:     }
 714: 
 715:     /**
 716:      * This method returns the preferred layout size for the given container.
 717:      *
 718:      * @param parent The container to size.
 719:      *
 720:      * @return The preferred layout size.
 721:      */
 722:     public Dimension preferredLayoutSize(Container parent)
 723:     {
 724:       return calculateSize(false);
 725:     }
 726: 
 727:     /**
 728:      * This method returns the preferred tab height given a tabPlacement and
 729:      * width.
 730:      *
 731:      * @param tabPlacement The JTabbedPane's tab placement.
 732:      * @param width The expected width.
 733:      *
 734:      * @return The preferred tab area height.
 735:      */
 736:     protected int preferredTabAreaHeight(int tabPlacement, int width)
 737:     {
 738:       if (tabPane.getTabCount() == 0)
 739:     return calculateTabAreaHeight(tabPlacement, 0, 0);
 740: 
 741:       int runs = 0;
 742:       int runWidth = 0;
 743:       int tabWidth = 0;
 744: 
 745:       FontMetrics fm = getFontMetrics();
 746: 
 747:       Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
 748:       Insets insets = tabPane.getInsets();
 749: 
 750:       // Only interested in width, this is a messed up rectangle now.
 751:       width -= tabAreaInsets.left + tabAreaInsets.right + insets.left
 752:       + insets.right;
 753: 
 754:       // The reason why we can't use runCount:
 755:       // This method is only called to calculate the size request
 756:       // for the tabbedPane. However, this size request is dependent on 
 757:       // our desired width. We need to find out what the height would
 758:       // be IF we got our desired width.
 759:       for (int i = 0; i < tabPane.getTabCount(); i++)
 760:         {
 761:       tabWidth = calculateTabWidth(tabPlacement, i, fm);
 762:       if (runWidth + tabWidth > width)
 763:         {
 764:           runWidth = tabWidth;
 765:           runs++;
 766:         }
 767:       else
 768:         runWidth += tabWidth;
 769:         }
 770:       runs++;
 771: 
 772:       int maxTabHeight = calculateMaxTabHeight(tabPlacement);
 773:       int tabAreaHeight = calculateTabAreaHeight(tabPlacement, runs,
 774:                                                  maxTabHeight);
 775:       return tabAreaHeight;
 776:     }
 777: 
 778:     /**
 779:      * This method calculates the preferred tab area width given a tab
 780:      * placement and height.
 781:      *
 782:      * @param tabPlacement The JTabbedPane's tab placement.
 783:      * @param height The expected height.
 784:      *
 785:      * @return The preferred tab area width.
 786:      */
 787:     protected int preferredTabAreaWidth(int tabPlacement, int height)
 788:     {
 789:       if (tabPane.getTabCount() == 0)
 790:     return calculateTabAreaHeight(tabPlacement, 0, 0);
 791: 
 792:       int runs = 0;
 793:       int runHeight = 0;
 794:       int tabHeight = 0;
 795: 
 796:       FontMetrics fm = getFontMetrics();
 797: 
 798:       Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
 799:       Insets insets = tabPane.getInsets();
 800: 
 801:       height -= tabAreaInsets.top + tabAreaInsets.bottom + insets.top
 802:       + insets.bottom;
 803:       int fontHeight = fm.getHeight();
 804: 
 805:       for (int i = 0; i < tabPane.getTabCount(); i++)
 806:         {
 807:       tabHeight = calculateTabHeight(tabPlacement, i, fontHeight);
 808:       if (runHeight + tabHeight > height)
 809:         {
 810:           runHeight = tabHeight;
 811:           runs++;
 812:         }
 813:       else
 814:         runHeight += tabHeight;
 815:         }
 816:       runs++;
 817: 
 818:       int maxTabWidth = calculateMaxTabWidth(tabPlacement);
 819:       int tabAreaWidth = calculateTabAreaWidth(tabPlacement, runs, maxTabWidth);
 820:       return tabAreaWidth;
 821:     }
 822: 
 823:     /**
 824:      * This method rotates the places each run in the correct place  the
 825:      * tabRuns array. See the comment for tabRuns for how the runs are placed
 826:      * in the array.
 827:      *
 828:      * @param tabPlacement The JTabbedPane's tab placement.
 829:      * @param selectedRun The run the current selection is in.
 830:      */
 831:     protected void rotateTabRuns(int tabPlacement, int selectedRun)
 832:     {
 833:       if (runCount == 1 || selectedRun == 1 || selectedRun == -1)
 834:     return;
 835:       int[] newTabRuns = new int[tabRuns.length];
 836:       int currentRun = selectedRun;
 837:       int i = 1;
 838:       do
 839:         {
 840:       newTabRuns[i] = tabRuns[currentRun];
 841:       currentRun = getNextTabRun(currentRun);
 842:       i++;
 843:         }
 844:       while (i < runCount);
 845:       if (runCount > 1)
 846:     newTabRuns[0] = tabRuns[currentRun];
 847: 
 848:       tabRuns = newTabRuns;
 849:       BasicTabbedPaneUI.this.selectedRun = 1;
 850:     }
 851: 
 852:     /**
 853:      * This method is called when a component is removed  from the
 854:      * JTabbedPane.
 855:      *
 856:      * @param comp The component removed.
 857:      */
 858:     public void removeLayoutComponent(Component comp)
 859:     {
 860:       // Do nothing.
 861:     }
 862:   }
 863: 
 864:   /**
 865:    * This class acts as the LayoutManager for the JTabbedPane in
 866:    * SCROLL_TAB_MODE.
 867:    */
 868:   private class TabbedPaneScrollLayout extends TabbedPaneLayout
 869:   {
 870:     /**
 871:      * This method returns the preferred layout size for the given container.
 872:      *
 873:      * @param parent The container to calculate a size for.
 874:      *
 875:      * @return The preferred layout size.
 876:      */
 877:     public Dimension preferredLayoutSize(Container parent)
 878:     {
 879:       return super.calculateSize(true);
 880:     }
 881: 
 882:     /**
 883:      * This method returns the minimum layout size for the given container.
 884:      *
 885:      * @param parent The container to calculate a size for.
 886:      *
 887:      * @return The minimum layout size.
 888:      */
 889:     public Dimension minimumLayoutSize(Container parent)
 890:     {
 891:       return super.calculateSize(true);
 892:     }
 893: 
 894:     /**
 895:      * This method calculates the tab area height given  a desired width.
 896:      *
 897:      * @param tabPlacement The JTabbedPane's tab placement.
 898:      * @param width The expected width.
 899:      *
 900:      * @return The tab area height given the width.
 901:      */
 902:     protected int preferredTabAreaHeight(int tabPlacement, int width)
 903:     {
 904:       if (tabPane.getTabCount() == 0)
 905:     return calculateTabAreaHeight(tabPlacement, 0, 0);
 906: 
 907:       int runs = 1;
 908: 
 909:       int maxTabHeight = calculateMaxTabHeight(tabPlacement);
 910:       int tabAreaHeight = calculateTabAreaHeight(tabPlacement, runs,
 911:                                                  maxTabHeight);
 912:       return tabAreaHeight;
 913:     }
 914: 
 915:     /**
 916:      * This method calculates the tab area width given a desired height.
 917:      *
 918:      * @param tabPlacement The JTabbedPane's tab placement.
 919:      * @param height The expected height.
 920:      *
 921:      * @return The tab area width given the height.
 922:      */
 923:     protected int preferredTabAreaWidth(int tabPlacement, int height)
 924:     {
 925:       if (tabPane.getTabCount() == 0)
 926:     return calculateTabAreaHeight(tabPlacement, 0, 0);
 927: 
 928:       int runs = 1;
 929: 
 930:       int maxTabWidth = calculateMaxTabWidth(tabPlacement);
 931:       int tabAreaWidth = calculateTabAreaWidth(tabPlacement, runs, maxTabWidth);
 932:       return tabAreaWidth;
 933:     }
 934: 
 935:     /**
 936:      * This method is called to calculate the tab rectangles.  This method
 937:      * will calculate the size and position of all  rectangles (taking into
 938:      * account which ones should be in which tab run). It will pad them and
 939:      * normalize them  as necessary.
 940:      *
 941:      * @param tabPlacement The JTabbedPane's tab placement.
 942:      * @param tabCount The number of tabs.
 943:      */
 944:     protected void calculateTabRects(int tabPlacement, int tabCount)
 945:     {
 946:       if (tabCount == 0)
 947:     return;
 948:       assureRectsCreated(tabCount);
 949: 
 950:       FontMetrics fm = getFontMetrics();
 951:       SwingUtilities.calculateInnerArea(tabPane, calcRect);
 952:       Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
 953:       Insets insets = tabPane.getInsets();
 954:       int max = 0;
 955:       int runs = 1;
 956:       int start = 0;
 957:       int top = 0;
 958:       if (tabPlacement == SwingConstants.TOP
 959:           || tabPlacement == SwingConstants.BOTTOM)
 960:         {
 961:       int maxHeight = calculateMaxTabHeight(tabPlacement);
 962:       calcRect.width -= tabAreaInsets.left + tabAreaInsets.right;
 963:       max = calcRect.width + tabAreaInsets.left + insets.left;
 964:       start = tabAreaInsets.left + insets.left;
 965:       int width = 0;
 966:       int runWidth = start;
 967:       top = insets.top + tabAreaInsets.top;
 968:       for (int i = 0; i < tabCount; i++)
 969:         {
 970:           width = calculateTabWidth(tabPlacement, i, fm);
 971: 
 972:           rects[i] = new Rectangle(runWidth, top, width, maxHeight);
 973:           runWidth += width;
 974:         }
 975:       tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right;
 976:       tabAreaRect.height = runs * maxTabHeight
 977:                            - (runs - 1) * tabRunOverlay
 978:                            + tabAreaInsets.top + tabAreaInsets.bottom;
 979:       contentRect.width = tabAreaRect.width;
 980:       contentRect.height = tabPane.getHeight() - insets.top
 981:                            - insets.bottom - tabAreaRect.height;
 982:       contentRect.x = insets.left;
 983:       tabAreaRect.x = insets.left;
 984:       if (tabPlacement == SwingConstants.BOTTOM)
 985:         {
 986:           contentRect.y = insets.top;
 987:           tabAreaRect.y = contentRect.y + contentRect.height;
 988:         }
 989:       else
 990:         {
 991:           tabAreaRect.y = insets.top;
 992:           contentRect.y = tabAreaRect.y + tabAreaRect.height;
 993:         }
 994:         }
 995:       else
 996:         {
 997:       int maxWidth = calculateMaxTabWidth(tabPlacement);
 998: 
 999:       calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom;
1000:       max = calcRect.height + tabAreaInsets.top;
1001:       int height = 0;
1002:       start = tabAreaInsets.top + insets.top;
1003:       int runHeight = start;
1004:       int fontHeight = fm.getHeight();
1005:       top = insets.left + tabAreaInsets.left;
1006:       for (int i = 0; i < tabCount; i++)
1007:         {
1008:           height = calculateTabHeight(tabPlacement, i, fontHeight);
1009:           rects[i] = new Rectangle(top, runHeight, maxWidth, height);
1010:           runHeight += height;
1011:         }
1012:       tabAreaRect.width = runs * maxTabWidth - (runs - 1) * tabRunOverlay
1013:                           + tabAreaInsets.left + tabAreaInsets.right;
1014:       tabAreaRect.height = tabPane.getHeight() - insets.top
1015:                            - insets.bottom;
1016:       tabAreaRect.y = insets.top;
1017:       contentRect.width = tabPane.getWidth() - insets.left - insets.right
1018:                           - tabAreaRect.width;
1019:       contentRect.height = tabAreaRect.height;
1020:       contentRect.y = insets.top;
1021:       if (tabPlacement == SwingConstants.LEFT)
1022:         {
1023:           tabAreaRect.x = insets.left;
1024:           contentRect.x = tabAreaRect.x + tabAreaRect.width;
1025:         }
1026:       else
1027:         {
1028:           contentRect.x = insets.left;
1029:           tabAreaRect.x = contentRect.x + contentRect.width;
1030:         }
1031:         }
1032:       runCount = runs;
1033: 
1034:       padSelectedTab(tabPlacement, tabPane.getSelectedIndex());
1035:     }
1036: 
1037:     /**
1038:      * This method is called when the JTabbedPane is laid out in
1039:      * SCROLL_TAB_LAYOUT. It finds the position for all components in the
1040:      * JTabbedPane.
1041:      *
1042:      * @param pane The JTabbedPane to be laid out.
1043:      */
1044:     public void layoutContainer(Container pane)
1045:     {
1046:       super.layoutContainer(pane);
1047:       int tabCount = tabPane.getTabCount();
1048:       Point p = null;
1049:       if (tabCount == 0)
1050:     return;
1051:       int tabPlacement = tabPane.getTabPlacement();
1052:       incrButton.hide();
1053:       decrButton.hide();
1054:       if (tabPlacement == SwingConstants.TOP
1055:           || tabPlacement == SwingConstants.BOTTOM)
1056:         {
1057:       if (tabAreaRect.x + tabAreaRect.width < rects[tabCount - 1].x
1058:           + rects[tabCount - 1].width)
1059:         {
1060:           Dimension incrDims = incrButton.getPreferredSize();
1061:           Dimension decrDims = decrButton.getPreferredSize();
1062: 
1063:           decrButton.setBounds(tabAreaRect.x + tabAreaRect.width
1064:                                - incrDims.width - decrDims.width,
1065:                                tabAreaRect.y, decrDims.width,
1066:                                tabAreaRect.height);
1067:           incrButton.setBounds(tabAreaRect.x + tabAreaRect.width
1068:                                - incrDims.width, tabAreaRect.y,
1069:                                decrDims.width, tabAreaRect.height);
1070: 
1071:           tabAreaRect.width -= decrDims.width + incrDims.width;
1072:           incrButton.show();
1073:           decrButton.show();
1074:         }
1075:         }
1076: 
1077:       if (tabPlacement == SwingConstants.LEFT
1078:           || tabPlacement == SwingConstants.RIGHT)
1079:         {
1080:       if (tabAreaRect.y + tabAreaRect.height < rects[tabCount - 1].y
1081:           + rects[tabCount - 1].height)
1082:         {
1083:           Dimension incrDims = incrButton.getPreferredSize();
1084:           Dimension decrDims = decrButton.getPreferredSize();
1085: 
1086:           decrButton.setBounds(tabAreaRect.x,
1087:                                tabAreaRect.y + tabAreaRect.height
1088:                                - incrDims.height - decrDims.height,
1089:                                tabAreaRect.width, decrDims.height);
1090:           incrButton.setBounds(tabAreaRect.x,
1091:                                tabAreaRect.y + tabAreaRect.height
1092:                                - incrDims.height, tabAreaRect.width,
1093:                                incrDims.height);
1094: 
1095:           tabAreaRect.height -= decrDims.height + incrDims.height;
1096:           incrButton.show();
1097:           decrButton.show();
1098:         }
1099:         }
1100:       viewport.setBounds(tabAreaRect.x, tabAreaRect.y, tabAreaRect.width,
1101:                          tabAreaRect.height);
1102:       int tabC = tabPane.getTabCount() - 1;
1103:       if (tabCount > 0)
1104:         {
1105:       int w = Math.max(rects[tabC].width + rects[tabC].x, tabAreaRect.width);
1106:       int h = Math.max(rects[tabC].height, tabAreaRect.height);
1107:       p = findPointForIndex(currentScrollLocation);
1108: 
1109:       // we want to cover that entire space so that borders that run under
1110:       // the tab area don't show up when we move the viewport around.
1111:       panel.setSize(w + p.x, h + p.y);
1112:         }
1113:       viewport.setViewPosition(p);
1114:       viewport.repaint();
1115:     }
1116:   }
1117: 
1118:   /**
1119:    * This class handles ChangeEvents from the JTabbedPane.
1120:    *
1121:    * @specnote Apparently this class was intended to be protected,
1122:    *           but was made public by a compiler bug and is now
1123:    *           public for compatibility.
1124:    */
1125:   public class TabSelectionHandler implements ChangeListener
1126:   {
1127:     /**
1128:      * This method is called whenever a ChangeEvent is fired from the
1129:      * JTabbedPane.
1130:      *
1131:      * @param e The ChangeEvent fired.
1132:      */
1133:     public void stateChanged(ChangeEvent e)
1134:     {
1135:       selectedRun = getRunForTab(tabPane.getTabCount(),
1136:                                  tabPane.getSelectedIndex());
1137:       tabPane.revalidate();
1138:       tabPane.repaint();
1139:     }
1140:   }
1141: 
1142:   /**
1143:    * This helper class is a JPanel that fits inside the ScrollViewport. This
1144:    * panel's sole job is to paint the tab rectangles inside the  viewport so
1145:    * that it's clipped correctly.
1146:    */
1147:   private class ScrollingPanel extends JPanel
1148:   {
1149:     /**
1150:      * This is a private UI class for our panel.
1151:      */
1152:     private class ScrollingPanelUI extends BasicPanelUI
1153:     {
1154:       /**
1155:        * This method overrides the default paint method. It paints the tab
1156:        * rectangles for the JTabbedPane in the panel.
1157:        *
1158:        * @param g The Graphics object to paint with.
1159:        * @param c The JComponent to paint.
1160:        */
1161:       public void paint(Graphics g, JComponent c)
1162:       {
1163:     paintTabArea(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex());
1164:       }
1165:     }
1166: 
1167:     /**
1168:      * This method overrides the updateUI method. It makes the default UI for
1169:      * this ScrollingPanel to be  a ScrollingPanelUI.
1170:      */
1171:     public void updateUI()
1172:     {
1173:       setUI((PanelUI) new ScrollingPanelUI());
1174:     }
1175:   }
1176: 
1177:   /**
1178:    * This is a helper class that paints the panel that paints tabs. This
1179:    * custom JViewport is used so that the tabs painted in the panel will be
1180:    * clipped. This class implements UIResource so tabs are not added when
1181:    * this objects of this class are added to the  JTabbedPane.
1182:    */
1183:   private class ScrollingViewport extends JViewport implements UIResource
1184:   {
1185:   }
1186: 
1187:   /**
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.
1190:    */
1191:   private class ScrollingButton extends BasicArrowButton implements UIResource
1192:   {
1193:     /**
1194:      * Creates a ScrollingButton given the direction.
1195:      *
1196:      * @param dir The direction to point in.
1197:      */
1198:     public ScrollingButton(int dir)
1199:     {
1200:       super(dir);
1201:     }
1202:   }
1203: 
1204:   /** The button that increments the current scroll location.
1205:    * This is package-private to avoid an accessor method.  */
1206:   transient ScrollingButton incrButton;
1207: 
1208:   /** The button that decrements the current scroll location.
1209:    * This is package-private to avoid an accessor method.  */
1210:   transient ScrollingButton decrButton;
1211: 
1212:   /** The viewport used to display the tabs.
1213:    * This is package-private to avoid an accessor method.  */
1214:   transient ScrollingViewport viewport;
1215: 
1216:   /** The panel inside the viewport that paints the tabs.
1217:    * This is package-private to avoid an accessor method.  */
1218:   transient ScrollingPanel panel;
1219: 
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;
1223: 
1224:   /** A reusable rectangle. */
1225:   protected Rectangle calcRect;
1226: 
1227:   /** An array of Rectangles keeping track of the tabs' area and position. */
1228:   protected Rectangle[] rects;
1229: 
1230:   /** The insets around the content area. */
1231:   protected Insets contentBorderInsets;
1232: 
1233:   /** The extra insets around the selected tab. */
1234:   protected Insets selectedTabPadInsets;
1235: 
1236:   /** The insets around the tab area. */
1237:   protected Insets tabAreaInsets;
1238: 
1239:   /** The insets around each and every tab. */
1240:   protected Insets tabInsets;
1241: 
1242:   /**
1243:    * The outer bottom and right edge color for both the tab and content
1244:    * border.
1245:    */
1246:   protected Color darkShadow;
1247: 
1248:   /** The color of the focus outline on the selected tab. */
1249:   protected Color focus;
1250: 
1251:   /** FIXME: find a use for this. */
1252:   protected Color highlight;
1253: 
1254:   /** The top and left edge color for both the tab and content border. */
1255:   protected Color lightHighlight;
1256: 
1257:   /** The inner bottom and right edge color for the tab and content border. */
1258:   protected Color shadow;
1259: 
1260:   /** The maximum tab height. */
1261:   protected int maxTabHeight;
1262: 
1263:   /** The maximum tab width. */
1264:   protected int maxTabWidth;
1265: 
1266:   /** The number of runs in the JTabbedPane. */
1267:   protected int runCount;
1268: 
1269:   /** The index of the run that the selected index is in. */
1270:   protected int selectedRun;
1271: 
1272:   /** The amount of space each run overlaps the previous by. */
1273:   protected int tabRunOverlay;
1274: 
1275:   /** The gap between text and label */
1276:   protected int textIconGap;
1277: 
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)
1286: 
1287:   /** This array keeps track of which tabs are in which run. See above. */
1288:   protected int[] tabRuns;
1289: 
1290:   /**
1291:    * This is the keystroke for moving down.
1292:    *
1293:    * @deprecated 1.3
1294:    */
1295:   protected KeyStroke downKey;
1296: 
1297:   /**
1298:    * This is the keystroke for moving left.
1299:    *
1300:    * @deprecated 1.3
1301:    */
1302:   protected KeyStroke leftKey;
1303: 
1304:   /**
1305:    * This is the keystroke for moving right.
1306:    *
1307:    * @deprecated 1.3
1308:    */
1309:   protected KeyStroke rightKey;
1310: 
1311:   /**
1312:    * This is the keystroke for moving up.
1313:    *
1314:    * @deprecated 1.3
1315:    */
1316:   protected KeyStroke upKey;
1317: 
1318:   /** The listener that listens for focus events. */
1319:   protected FocusListener focusListener;
1320: 
1321:   /** The listener that listens for mouse events. */
1322:   protected MouseListener mouseListener;
1323: 
1324:   /** The listener that listens for property change events. */
1325:   protected PropertyChangeListener propertyChangeListener;
1326: 
1327:   /** The listener that listens for change events. */
1328:   protected ChangeListener tabChangeListener;
1329: 
1330:   /** The tab pane that this UI paints. */
1331:   protected JTabbedPane tabPane;
1332: 
1333:   /** The current layout manager for the tabPane.
1334:    * This is package-private to avoid an accessor method.  */
1335:   transient LayoutManager layoutManager;
1336: 
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;
1340: 
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;
1344: 
1345:   /**
1346:    * Creates a new BasicTabbedPaneUI object.
1347:    */
1348:   public BasicTabbedPaneUI()
1349:   {
1350:     super();
1351:   }
1352: 
1353:   /**
1354:    * This method creates a ScrollingButton that  points in the appropriate
1355:    * direction for an increasing button.
1356:    * This is package-private to avoid an accessor method.
1357:    *
1358:    * @return The increase ScrollingButton.
1359:    */
1360:   ScrollingButton createIncreaseButton()
1361:   {
1362:     if (incrButton == null)
1363:       incrButton = new ScrollingButton(SwingConstants.NORTH);
1364:     if (tabPane.getTabPlacement() == SwingConstants.TOP
1365:         || tabPane.getTabPlacement() == SwingConstants.BOTTOM)
1366:       incrButton.setDirection(SwingConstants.EAST);
1367:     else
1368:       incrButton.setDirection(SwingConstants.SOUTH);
1369:     return incrButton;
1370:   }
1371: 
1372:   /**
1373:    * This method creates a ScrollingButton that points in the appropriate
1374:    * direction for a decreasing button.
1375:    * This is package-private to avoid an accessor method.
1376:    *
1377:    * @return The decrease ScrollingButton.
1378:    */
1379:   ScrollingButton createDecreaseButton()
1380:   {
1381:     if (decrButton == null)
1382:       decrButton = new ScrollingButton(SwingConstants.SOUTH);
1383:     if (tabPane.getTabPlacement() == SwingConstants.TOP
1384:         || tabPane.getTabPlacement() == SwingConstants.BOTTOM)
1385:       decrButton.setDirection(SwingConstants.WEST);
1386:     else
1387:       decrButton.setDirection(SwingConstants.NORTH);
1388:     return decrButton;
1389:   }
1390: 
1391:   /**
1392:    * This method finds the point to set the view  position at given the index
1393:    * of a tab. The tab will be the first visible tab in the run.
1394:    * This is package-private to avoid an accessor method.
1395:    *
1396:    * @param index The index of the first visible tab.
1397:    *
1398:    * @return The position of the first visible tab.
1399:    */
1400:   Point findPointForIndex(int index)
1401:   {
1402:     int tabPlacement = tabPane.getTabPlacement();
1403:     int selectedIndex = tabPane.getSelectedIndex();
1404:     Insets insets = getSelectedTabPadInsets(tabPlacement);
1405:     int w = 0;
1406:     int h = 0;
1407: 
1408:     if (tabPlacement == TOP || tabPlacement == BOTTOM)
1409:       {
1410:     if (index > 0)
1411:       {
1412:         w += rects[index - 1].x + rects[index - 1].width;
1413:         if (index > selectedIndex)
1414:           w -= insets.left + insets.right;
1415:       }
1416:       }
1417: 
1418:     else
1419:       {
1420:     if (index > 0)
1421:       {
1422:         h += rects[index - 1].y + rects[index - 1].height;
1423:         if (index > selectedIndex)
1424:           h -= insets.top + insets.bottom;
1425:       }
1426:       }
1427: 
1428:     Point p = new Point(w, h);
1429:     return p;
1430:   }
1431: 
1432:   /**
1433:    * This method creates a new BasicTabbedPaneUI.
1434:    *
1435:    * @param c The JComponent to create a UI for.
1436:    *
1437:    * @return A new BasicTabbedPaneUI.
1438:    */
1439:   public static ComponentUI createUI(JComponent c)
1440:   {
1441:     return new BasicTabbedPaneUI();
1442:   }
1443: 
1444:   /**
1445:    * This method installs the UI for the given JComponent.
1446:    *
1447:    * @param c The JComponent to install the UI for.
1448:    */
1449:   public void installUI(JComponent c)
1450:   {
1451:     super.installUI(c);
1452:     if (c instanceof JTabbedPane)
1453:       {
1454:     tabPane = (JTabbedPane) c;
1455: 
1456:     installComponents();
1457:     installDefaults();
1458:     installListeners();
1459:     installKeyboardActions();
1460: 
1461:     layoutManager = createLayoutManager();
1462:     tabPane.setLayout(layoutManager);
1463:     tabPane.layout();
1464:       }
1465:   }
1466: 
1467:   /**
1468:    * This method uninstalls the UI for the  given JComponent.
1469:    *
1470:    * @param c The JComponent to uninstall the UI for.
1471:    */
1472:   public void uninstallUI(JComponent c)
1473:   {
1474:     layoutManager = null;
1475: 
1476:     uninstallKeyboardActions();
1477:     uninstallListeners();
1478:     uninstallDefaults();
1479:     uninstallComponents();
1480: 
1481:     tabPane = null;
1482:   }
1483: 
1484:   /**
1485:    * This method creates the appropriate layout manager for the JTabbedPane's
1486:    * current tab layout policy. If the tab layout policy is
1487:    * SCROLL_TAB_LAYOUT, then all the associated components that need to be
1488:    * created will be done so now.
1489:    *
1490:    * @return A layout manager given the tab layout policy.
1491:    */
1492:   protected LayoutManager createLayoutManager()
1493:   {
1494:     if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT)
1495:       return new TabbedPaneLayout();
1496:     else
1497:       {
1498:     incrButton = createIncreaseButton();
1499:     decrButton = createDecreaseButton();
1500:     viewport = new ScrollingViewport();
1501:     viewport.setLayout(null);
1502:     panel = new ScrollingPanel();
1503:     viewport.setView(panel);
1504:     tabPane.add(incrButton);
1505:     tabPane.add(decrButton);
1506:     tabPane.add(viewport);
1507:     currentScrollLocation = 0;
1508:     decrButton.setEnabled(false);
1509:     panel.addMouseListener(mouseListener);
1510:     incrButton.addMouseListener(mouseListener);
1511:     decrButton.addMouseListener(mouseListener);
1512:     viewport.setBackground(Color.LIGHT_GRAY);
1513: 
1514:     return new TabbedPaneScrollLayout();
1515:       }
1516:   }
1517: 
1518:   /**
1519:    * This method installs components for this JTabbedPane.
1520:    */
1521:   protected void installComponents()
1522:   {
1523:     // Nothing to be done.
1524:   }
1525: 
1526:   /**
1527:    * This method uninstalls components for this JTabbedPane.
1528:    */
1529:   protected void uninstallComponents()
1530:   {
1531:     // Nothing to be done.
1532:   }
1533: 
1534:   /**
1535:    * This method installs defaults for the Look and Feel.
1536:    */
1537:   protected void installDefaults()
1538:   {
1539:     UIDefaults defaults = UIManager.getLookAndFeelDefaults();
1540: 
1541:     tabPane.setFont(defaults.getFont("TabbedPane.font"));
1542:     tabPane.setForeground(defaults.getColor("TabbedPane.foreground"));
1543:     tabPane.setBackground(defaults.getColor("TabbedPane.background"));
1544:     tabPane.setOpaque(false);
1545: 
1546:     highlight = defaults.getColor("TabbedPane.highlight");
1547:     lightHighlight = defaults.getColor("TabbedPane.lightHighlight");
1548: 
1549:     shadow = defaults.getColor("TabbedPane.shadow");
1550:     darkShadow = defaults.getColor("TabbedPane.darkShadow");
1551: 
1552:     focus = defaults.getColor("TabbedPane.focus");
1553: 
1554:     textIconGap = defaults.getInt("TabbedPane.textIconGap");
1555:     tabRunOverlay = defaults.getInt("TabbedPane.tabRunOverlay");
1556: 
1557:     tabInsets = defaults.getInsets("TabbedPane.tabbedPaneTabInsets");
1558:     selectedTabPadInsets = defaults.getInsets("TabbedPane.tabbedPaneTabPadInsets");
1559:     tabAreaInsets = defaults.getInsets("TabbedPane.tabbedPaneTabAreaInsets");
1560:     contentBorderInsets = defaults.getInsets("TabbedPane.tabbedPaneContentBorderInsets");
1561: 
1562:     calcRect = new Rectangle();
1563:     tabRuns = new int[10];
1564:     tabAreaRect = new Rectangle();
1565:     contentRect = new Rectangle();
1566:   }
1567: 
1568:   /**
1569:    * This method uninstalls defaults for the Look and Feel.
1570:    */
1571:   protected void uninstallDefaults()
1572:   {
1573:     calcRect = null;
1574:     tabAreaRect = null;
1575:     contentRect = null;
1576:     tabRuns = null;
1577: 
1578:     contentBorderInsets = null;
1579:     tabAreaInsets = null;
1580:     selectedTabPadInsets = null;
1581:     tabInsets = null;
1582: 
1583:     focus = null;
1584:     darkShadow = null;
1585:     shadow = null;
1586:     lightHighlight = null;
1587:     highlight = null;
1588: 
1589:     tabPane.setBackground(null);
1590:     tabPane.setForeground(null);
1591:     tabPane.setFont(null);
1592:   }
1593: 
1594:   /**
1595:    * This method creates and installs the listeners for this UI.
1596:    */
1597:   protected void installListeners()
1598:   {
1599:     mouseListener = createMouseListener();
1600:     tabChangeListener = createChangeListener();
1601:     propertyChangeListener = createPropertyChangeListener();
1602:     focusListener = createFocusListener();
1603: 
1604:     tabPane.addMouseListener(mouseListener);
1605:     tabPane.addChangeListener(tabChangeListener);
1606:     tabPane.addPropertyChangeListener(propertyChangeListener);
1607:     tabPane.addFocusListener(focusListener);
1608:   }
1609: 
1610:   /**
1611:    * This method removes and nulls the listeners for this UI.
1612:    */
1613:   protected void uninstallListeners()
1614:   {
1615:     tabPane.removeFocusListener(focusListener);
1616:     tabPane.removePropertyChangeListener(propertyChangeListener);
1617:     tabPane.removeChangeListener(tabChangeListener);
1618:     tabPane.removeMouseListener(mouseListener);
1619: 
1620:     focusListener = null;
1621:     propertyChangeListener = null;
1622:     tabChangeListener = null;
1623:     mouseListener = null;
1624:   }
1625: 
1626:   /**
1627:    * This method creates a new MouseListener.
1628:    *
1629:    * @return A new MouseListener.
1630:    */
1631:   protected MouseListener createMouseListener()
1632:   {
1633:     return new MouseHandler();
1634:   }
1635: 
1636:   /**
1637:    * This method creates a new FocusListener.
1638:    *
1639:    * @return A new FocusListener.
1640:    */
1641:   protected FocusListener createFocusListener()
1642:   {
1643:     return new FocusHandler();
1644:   }
1645: 
1646:   /**
1647:    * This method creates a new ChangeListener.
1648:    *
1649:    * @return A new ChangeListener.
1650:    */
1651:   protected ChangeListener createChangeListener()
1652:   {
1653:     return new TabSelectionHandler();
1654:   }
1655: 
1656:   /**
1657:    * This method creates a new PropertyChangeListener.
1658:    *
1659:    * @return A new PropertyChangeListener.
1660:    */
1661:   protected PropertyChangeListener createPropertyChangeListener()
1662:   {
1663:     return new PropertyChangeHandler();
1664:   }
1665: 
1666:   /**
1667:    * This method installs keyboard actions for the JTabbedPane.
1668:    */
1669:   protected void installKeyboardActions()
1670:   {
1671:     // FIXME: Implement.
1672:   }
1673: 
1674:   /**
1675:    * This method uninstalls keyboard actions for the JTabbedPane.
1676:    */
1677:   protected void uninstallKeyboardActions()
1678:   {
1679:     // FIXME: Implement.
1680:   }
1681: 
1682:   /**
1683:    * This method returns the minimum size of the JTabbedPane.
1684:    *
1685:    * @param c The JComponent to find a size for.
1686:    *
1687:    * @return The minimum size.
1688:    */
1689:   public Dimension getMinimumSize(JComponent c)
1690:   {
1691:     return layoutManager.minimumLayoutSize(tabPane);
1692:   }
1693: 
1694:   /**
1695:    * This method returns the maximum size of the JTabbedPane.
1696:    *
1697:    * @param c The JComponent to find a size for.
1698:    *
1699:    * @return The maximum size.
1700:    */
1701:   public Dimension getMaximumSize(JComponent c)
1702:   {
1703:     return new Dimension(Short.MAX_VALUE, Short.MAX_VALUE);
1704:   }
1705: 
1706:   /**
1707:    * This method paints the JTabbedPane.
1708:    *
1709:    * @param g The Graphics object to paint with.
1710:    * @param c The JComponent to paint.
1711:    */
1712:   public void paint(Graphics g, JComponent c)
1713:   {
1714:     if (tabPane.getTabCount() == 0)
1715:       return;
1716:     if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT)
1717:       paintTabArea(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex());
1718:     paintContentBorder(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex());
1719:   }
1720: 
1721:   /**
1722:    * This method paints the tab area. This includes painting the rectangles
1723:    * that make up the tabs.
1724:    *
1725:    * @param g The Graphics object to paint with.
1726:    * @param tabPlacement The JTabbedPane's tab placement.
1727:    * @param selectedIndex The selected index.
1728:    */
1729:   protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex)
1730:   {
1731:     Rectangle ir = new Rectangle();
1732:     Rectangle tr = new Rectangle();
1733: 
1734:     boolean isScroll = tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT;
1735: 
1736:     // Please note: the ordering of the painting is important. 
1737:     // we WANT to paint the outermost run first and then work our way in.
1738:     int tabCount = tabPane.getTabCount();
1739:     int currRun = 1;
1740:     if (tabCount < 1)
1741:       return;
1742: 
1743:     if (runCount > 1)
1744:       currRun = 0;
1745:     for (int i = 0; i < runCount; i++)
1746:       {
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)
1754:       {
1755:         for (int k = first; k < tabCount; k++)
1756:           {
1757:         if (rects[k].x + rects[k].width - rects[first].x > viewport
1758:                                                            .getWidth())
1759:           {
1760:             last = k;
1761:             break;
1762:           }
1763:           }
1764:       }
1765: 
1766:     for (int j = first; j <= last; j++)
1767:       {
1768:         if (j != selectedIndex || isScroll)
1769:           paintTab(g, tabPlacement, rects, j, ir, tr);
1770:       }
1771:     currRun = getPreviousTabRun(currRun);
1772:       }
1773:     if (! isScroll)
1774:       paintTab(g, tabPlacement, rects, selectedIndex, ir, tr);
1775:   }
1776: 
1777:   /**
1778:    * This method paints an individual tab.
1779:    *
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.
1787:    */
1788:   protected void paintTab(Graphics g, int tabPlacement, Rectangle[] rects,
1789:                           int tabIndex, Rectangle iconRect, Rectangle textRect)
1790:   {
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);
1796: 
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)
1802:       {
1803:     Insets insets = getTabAreaInsets(tabPlacement);
1804:     switch (tabPlacement)
1805:       {
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;
1820:       }
1821:       }
1822: 
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);
1827: 
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);
1834:   }
1835: 
1836:   /**
1837:    * This method lays out the tab and finds the location to paint the  icon
1838:    * and text.
1839:    *
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.
1849:    */
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)
1854:   {
1855:     SwingUtilities.layoutCompoundLabel(metrics, title, icon,
1856:                                        SwingConstants.CENTER,
1857:                                        SwingConstants.CENTER,
1858:                                        SwingConstants.CENTER,
1859:                                        SwingConstants.CENTER, tabRect,
1860:                                        iconRect, textRect, textIconGap);
1861: 
1862:     int shiftX = getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
1863:     int shiftY = getTabLabelShiftY(tabPlacement, tabIndex, isSelected);
1864: 
1865:     iconRect.x += shiftX;
1866:     iconRect.y += shiftY;
1867: 
1868:     textRect.x += shiftX;
1869:     textRect.y += shiftY;
1870:   }
1871: 
1872:   /**
1873:    * This method paints the icon.
1874:    *
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.
1881:    */
1882:   protected void paintIcon(Graphics g, int tabPlacement, int tabIndex,
1883:                            Icon icon, Rectangle iconRect, boolean isSelected)
1884:   {
1885:     icon.paintIcon(tabPane, g, iconRect.x, iconRect.y);
1886:   }
1887: 
1888:   /**
1889:    * This method paints the text for the given tab.
1890:    *
1891:    * @param g The Graphics object to paint with.
1892:    * @param tabPlacement The JTabbedPane's tab placement.
1893:    * @param font The font to paint with.
1894:    * @param metrics The fontmetrics of the given font.
1895:    * @param tabIndex The tab index.
1896:    * @param title The string to paint.
1897:    * @param textRect The bounds of the string.
1898:    * @param isSelected Whether this tab is selected.
1899:    */
1900:   protected void paintText(Graphics g, int tabPlacement, Font font,
1901:                            FontMetrics metrics, int tabIndex, String title,
1902:                            Rectangle textRect, boolean isSelected)
1903:   {
1904:     View textView = getTextViewForTab(tabIndex);
1905:     if (textView != null)
1906:       {
1907:     textView.paint(g, textRect);
1908:     return;
1909:       }
1910: 
1911:     Color fg = tabPane.getForegroundAt(tabIndex);
1912:     if (fg == null)
1913:       fg = tabPane.getForeground();
1914:     Color bg = tabPane.getBackgroundAt(tabIndex);
1915:     if (bg == null)
1916:       bg = tabPane.getBackground();
1917: 
1918:     Color saved_color = g.getColor();
1919:     Font f = g.getFont();
1920:     g.setFont(font);
1921: 
1922:     if (tabPane.isEnabledAt(tabIndex))
1923:       {
1924:     g.setColor(fg);
1925: 
1926:     int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
1927: 
1928:     if (mnemIndex != -1)
1929:       BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
1930:                                                    textRect.x,
1931:                                                    textRect.y
1932:                                                    + metrics.getAscent());
1933:     else
1934:       g.drawString(title, textRect.x, textRect.y + metrics.getAscent());
1935:       }
1936:     else
1937:       {
1938:     g.setColor(bg.brighter());
1939: 
1940:     int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
1941: 
1942:     if (mnemIndex != -1)
1943:       BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
1944:                                                    textRect.x, textRect.y);
1945:     else
1946:       g.drawString(title, textRect.x, textRect.y);
1947: 
1948:     g.setColor(bg.darker());
1949:     if (mnemIndex != -1)
1950:       BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
1951:                                                    textRect.x + 1,
1952:                                                    textRect.y + 1);
1953:     else
1954:       g.drawString(title, textRect.x + 1, textRect.y + 1);
1955:       }
1956: 
1957:     g.setColor(saved_color);
1958:     g.setFont(f);
1959:   }
1960: 
1961:   /**
1962:    * This method returns how much the label for the tab should shift in the X
1963:    * direction.
1964:    *
1965:    * @param tabPlacement The JTabbedPane's tab placement.
1966:    * @param tabIndex The tab index being painted.
1967:    * @param isSelected Whether this tab is selected.
1968:    *
1969:    * @return The amount the label should shift by in the X direction.
1970:    */
1971:   protected int getTabLabelShiftX(int tabPlacement, int tabIndex,
1972:                                   boolean isSelected)
1973:   {
1974:     // No reason to shift.
1975:     return 0;
1976:   }
1977: 
1978:   /**
1979:    * This method returns how much the label for the tab should shift in the Y
1980:    * direction.
1981:    *
1982:    * @param tabPlacement The JTabbedPane's tab placement.
1983:    * @param tabIndex The tab index being painted.
1984:    * @param isSelected Whether this tab is selected.
1985:    *
1986:    * @return The amount the label should shift by in the Y direction.
1987:    */
1988:   protected int getTabLabelShiftY(int tabPlacement, int tabIndex,
1989:                                   boolean isSelected)
1990:   {
1991:     // No reason to shift.
1992:     return 0;
1993:   }
1994: 
1995:   /**
1996:    * This method paints the focus rectangle around the selected tab.
1997:    *
1998:    * @param g The Graphics object to paint with.
1999:    * @param tabPlacement The JTabbedPane's tab placement.
2000:    * @param rects The array of rectangles keeping track of size and position.
2001:    * @param tabIndex The tab index.
2002:    * @param iconRect The icon bounds.
2003:    * @param textRect The text bounds.
2004:    * @param isSelected Whether this tab is selected.
2005:    */
2006:   protected void paintFocusIndicator(Graphics g, int tabPlacement,
2007:                                      Rectangle[] rects, int tabIndex,
2008:                                      Rectangle iconRect, Rectangle textRect,
2009:                                      boolean isSelected)
2010:   {
2011:     Color saved = g.getColor();
2012:     calcRect = iconRect.union(textRect);
2013: 
2014:     g.setColor(focus);
2015: 
2016:     g.drawRect(calcRect.x, calcRect.y, calcRect.width, calcRect.height);
2017: 
2018:     g.setColor(saved);
2019:   }
2020: 
2021:   /**
2022:    * This method paints the border for an individual tab.
2023:    *
2024:    * @param g The Graphics object to paint with.
2025:    * @param tabPlacement The JTabbedPane's tab placement.
2026:    * @param tabIndex The tab index.
2027:    * @param x The x position of the tab.
2028:    * @param y The y position of the tab.
2029:    * @param w The width of the tab.
2030:    * @param h The height of the tab.
2031:    * @param isSelected Whether the tab is selected.
2032:    */
2033:   protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex,
2034:                                 int x, int y, int w, int h, boolean isSelected)
2035:   {
2036:     Color saved = g.getColor();
2037: 
2038:     if (! isSelected || tabPlacement != SwingConstants.TOP)
2039:       {
2040:     g.setColor(shadow);
2041:     g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1);
2042:     g.setColor(darkShadow);
2043:     g.drawLine(x, y + h, x + w, y + h);
2044:       }
2045: 
2046:     if (! isSelected || tabPlacement != SwingConstants.LEFT)
2047:       {
2048:     g.setColor(darkShadow);
2049:     g.drawLine(x + w, y, x + w, y + h);
2050:     g.setColor(shadow);
2051:     g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1);
2052:       }
2053: 
2054:     if (! isSelected || tabPlacement != SwingConstants.RIGHT)
2055:       {
2056:     g.setColor(lightHighlight);
2057:     g.drawLine(x, y, x, y + h);
2058:       }
2059: 
2060:     if (! isSelected || tabPlacement != SwingConstants.BOTTOM)
2061:       {
2062:     g.setColor(lightHighlight);
2063:     g.drawLine(x, y, x + w, y);
2064:       }
2065: 
2066:     g.setColor(saved);
2067:   }
2068: 
2069:   /**
2070:    * This method paints the background for an individual tab.
2071:    *
2072:    * @param g The Graphics object to paint with.
2073:    * @param tabPlacement The JTabbedPane's tab placement.
2074:    * @param tabIndex The tab index.
2075:    * @param x The x position of the tab.
2076:    * @param y The y position of the tab.
2077:    * @param w The width of the tab.
2078:    * @param h The height of the tab.
2079:    * @param isSelected Whether the tab is selected.
2080:    */
2081:   protected void paintTabBackground(Graphics g, int tabPlacement,
2082:                                     int tabIndex, int x, int y, int w, int h,
2083:                                     boolean isSelected)
2084:   {
2085:     Color saved = g.getColor();
2086:     if (isSelected)
2087:       g.setColor(Color.LIGHT_GRAY);
2088:     else
2089:       {
2090:     Color bg = tabPane.getBackgroundAt(tabIndex);
2091:     if (bg == null)
2092:       bg = Color.GRAY;
2093:     g.setColor(bg);
2094:       }
2095: 
2096:     g.fillRect(x, y, w, h);
2097: 
2098:     g.setColor(saved);
2099:   }
2100: 
2101:   /**
2102:    * This method paints the border around the content area.
2103:    *
2104:    * @param g The Graphics object to paint with.
2105:    * @param tabPlacement The JTabbedPane's tab placement.
2106:    * @param selectedIndex The index of the selected tab.
2107:    */
2108:   protected void paintContentBorder(Graphics g, int tabPlacement,
2109:                                     int selectedIndex)
2110:   {
2111:     Insets insets = getContentBorderInsets(tabPlacement);
2112:     int x = contentRect.x;
2113:     int y = contentRect.y;
2114:     int w = contentRect.width;
2115:     int h = contentRect.height;
2116:     paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2117:     paintContentBorderLeftEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2118:     paintContentBorderBottomEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2119:     paintContentBorderRightEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2120:   }
2121: 
2122:   /**
2123:    * This method paints the top edge of the content border.
2124:    *
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.
2132:    */
2133:   protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
2134:                                            int selectedIndex, int x, int y,
2135:                                            int w, int h)
2136:   {
2137:     Color saved = g.getColor();
2138:     g.setColor(lightHighlight);
2139: 
2140:     int startgap = rects[selectedIndex].x;
2141:     int endgap = rects[selectedIndex].x + rects[selectedIndex].width;
2142: 
2143:     int diff = 0;
2144: 
2145:     if (tabPlacement == SwingConstants.TOP)
2146:       {
2147:     if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
2148:       {
2149:         Point p = findPointForIndex(currentScrollLocation);
2150:         diff = p.x;
2151:       }
2152: 
2153:     g.drawLine(x, y, startgap - diff, y);
2154:     g.drawLine(endgap - diff, y, x + w, y);
2155:       }
2156:     else
2157:       g.drawLine(x, y, x + w, y);
2158: 
2159:     g.setColor(saved);
2160:   }
2161: 
2162:   /**
2163:    * This method paints the left edge of the content border.
2164:    *
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.
2172:    */
2173:   protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
2174:                                             int selectedIndex, int x, int y,
2175:                                             int w, int h)
2176:   {
2177:     Color saved = g.getColor();
2178:     g.setColor(lightHighlight);
2179: 
2180:     int startgap = rects[selectedIndex].y;
2181:     int endgap = rects[selectedIndex].y + rects[selectedIndex].height;
2182: 
2183:     int diff = 0;
2184: 
2185:     if (tabPlacement == SwingConstants.LEFT)
2186:       {
2187:     if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
2188:       {
2189:         Point p = findPointForIndex(currentScrollLocation);
2190:         diff = p.y;
2191:       }
2192: 
2193:     g.drawLine(x, y, x, startgap - diff);
2194:     g.drawLine(x, endgap - diff, x, y + h);
2195:       }
2196:     else
2197:       g.drawLine(x, y, x, y + h);
2198: 
2199:     g.setColor(saved);
2200:   }
2201: 
2202:   /**
2203:    * This method paints the bottom edge of the content border.
2204:    *
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.
2212:    */
2213:   protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
2214:                                               int selectedIndex, int x, int y,
2215:                                               int w, int h)
2216:   {
2217:     Color saved = g.getColor();
2218: 
2219:     int startgap = rects[selectedIndex].x;
2220:     int endgap = rects[selectedIndex].x + rects[selectedIndex].width;
2221: 
2222:     int diff = 0;
2223: 
2224:     if (tabPlacement == SwingConstants.BOTTOM)
2225:       {
2226:     if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
2227:       {
2228:         Point p = findPointForIndex(currentScrollLocation);
2229:         diff = p.x;
2230:       }
2231: 
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);
2235: 
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);
2239:       }
2240:     else
2241:       {
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);
2246:       }
2247: 
2248:     g.setColor(saved);
2249:   }
2250: 
2251:   /**
2252:    * This method paints the right edge of the content border.
2253:    *
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.
2261:    */
2262:   protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
2263:                                              int selectedIndex, int x, int y,
2264:                                              int w, int h)
2265:   {
2266:     Color saved = g.getColor();
2267:     int startgap = rects[selectedIndex].y;
2268:     int endgap = rects[selectedIndex].y + rects[selectedIndex].height;
2269: 
2270:     int diff = 0;
2271: 
2272:     if (tabPlacement == SwingConstants.RIGHT)
2273:       {
2274:     if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
2275:       {
2276:         Point p = findPointForIndex(currentScrollLocation);
2277:         diff = p.y;
2278:       }
2279: 
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);
2283: 
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);
2287:       }
2288:     else
2289:       {
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);
2294:       }
2295: 
2296:     g.setColor(saved);
2297:   }
2298: 
2299:   /**
2300:    * This method returns the tab bounds for the given index.
2301:    *
2302:    * @param pane The JTabbedPane.
2303:    * @param i The index to look for.
2304:    *
2305:    * @return The bounds of the tab with the given index.
2306:    */
2307:   public Rectangle getTabBounds(JTabbedPane pane, int i)
2308:   {
2309:     return rects[i];
2310:   }
2311: 
2312:   /**
2313:    * This method returns the number of runs.
2314:    *
2315:    * @param pane The JTabbedPane.
2316:    *
2317:    * @return The number of runs.
2318:    */
2319:   public int getTabRunCount(JTabbedPane pane)
2320:   {
2321:     return runCount;
2322:   }
2323: 
2324:   /**
2325:    * This method returns the tab index given a coordinate.
2326:    *
2327:    * @param pane The JTabbedPane.
2328:    * @param x The x coordinate.
2329:    * @param y The y coordinate.
2330:    *
2331:    * @return The tab index that the coordinate lands in.
2332:    */
2333:   public int tabForCoordinate(JTabbedPane pane, int x, int y)
2334:   {
2335:     Point p = new Point(x, y);
2336:     int tabCount = tabPane.getTabCount();
2337:     int currRun = 1;
2338:     for (int i = 0; i < runCount; i++)
2339:       {
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++)
2345:       {
2346:         if (getTabBounds(pane, j).contains(p))
2347:           return j;
2348:       }
2349:     currRun = getNextTabRun(currRun);
2350:       }
2351:     return -1;
2352:   }
2353: 
2354:   /**
2355:    * This method returns the tab bounds in the given rectangle.
2356:    *
2357:    * @param tabIndex The index to get bounds for.
2358:    * @param dest The rectangle to store bounds in.
2359:    *
2360:    * @return The rectangle passed in.
2361:    */
2362:   protected Rectangle getTabBounds(int tabIndex, Rectangle dest)
2363:   {
2364:     dest.setBounds(getTabBounds(tabPane, tabIndex));
2365:     return dest;
2366:   }
2367: 
2368:   /**
2369:    * This method returns the component that is shown in  the content area.
2370:    *
2371:    * @return The component that is shown in the content area.
2372:    */
2373:   protected Component getVisibleComponent()
2374:   {
2375:     return tabPane.getComponentAt(tabPane.getSelectedIndex());
2376:   }
2377: 
2378:   /**
2379:    * This method sets the visible component.
2380:    *
2381:    * @param component The component to be set visible.
2382:    */
2383:   protected void setVisibleComponent(Component component)
2384:   {
2385:     component.setVisible(true);
2386:     tabPane.setSelectedComponent(component);
2387:   }
2388: 
2389:   /**
2390:    * This method assures that enough rectangles are created given the
2391:    * tabCount. The old array is copied to the  new one.
2392:    *
2393:    * @param tabCount The number of tabs.
2394:    */
2395:   protected void assureRectsCreated(int tabCount)
2396:   {
2397:     if (rects == null)
2398:       rects = new Rectangle[tabCount];
2399:     if (tabCount == rects.length)
2400:       return;
2401:     else
2402:       {
2403:     int numToCopy = Math.min(tabCount, rects.length);
2404:     Rectangle[] tmp = new Rectangle[tabCount];
2405:     System.arraycopy(rects, 0, tmp, 0, numToCopy);
2406:     rects = tmp;
2407:       }
2408:   }
2409: 
2410:   /**
2411:    * This method expands the tabRuns array to give it more room. The old array
2412:    * is copied to the new one.
2413:    */
2414:   protected void expandTabRunsArray()
2415:   {
2416:     // This method adds another 10 index positions to the tabRuns array.
2417:     if (tabRuns == null)
2418:       tabRuns = new int[10];
2419:     else
2420:       {
2421:     int[] newRuns = new int[tabRuns.length + 10];
2422:     System.arraycopy(tabRuns, 0, newRuns, 0, tabRuns.length);
2423:     tabRuns = newRuns;
2424:       }
2425:   }
2426: 
2427:   /**
2428:    * This method returns which run a particular tab belongs to.
2429:    *
2430:    * @param tabCount The number of tabs.
2431:    * @param tabIndex The tab to find.
2432:    *
2433:    * @return The tabRuns index that it belongs to.
2434:    */
2435:   protected int getRunForTab(int tabCount, int tabIndex)
2436:   {
2437:     if (runCount == 1 && tabIndex < tabCount && tabIndex >= 0)
2438:       return 1;
2439:     for (int i = 0; i < runCount; i++)
2440:       {
2441:     int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1;
2442:     if (first == tabCount)
2443:       first = 0;
2444:     int last = lastTabInRun(tabCount, i);
2445:     if (last >= tabIndex && first <= tabIndex)
2446:       return i;
2447:       }
2448:     return -1;
2449:   }
2450: 
2451:   /**
2452:    * This method returns the index of the last tab in  a run.
2453:    *
2454:    * @param tabCount The number of tabs.
2455:    * @param run The run to check.
2456:    *
2457:    * @return The last tab in the given run.
2458:    */
2459:   protected int lastTabInRun(int tabCount, int run)
2460:   {
2461:     if (tabRuns[run] == 0)
2462:       return tabCount - 1;
2463:     else
2464:       return tabRuns[run] - 1;
2465:   }
2466: 
2467:   /**
2468:    * This method returns the tab run overlay.
2469:    *
2470:    * @param tabPlacement The JTabbedPane's tab placement.
2471:    *
2472:    * @return The tab run overlay.
2473:    */
2474:   protected int getTabRunOverlay(int tabPlacement)
2475:   {
2476:     return tabRunOverlay;
2477:   }
2478: 
2479:   /**
2480:    * This method returns the tab run indent. It is used in WRAP_TAB_LAYOUT and
2481:    * makes each tab run start indented by a certain amount.
2482:    *
2483:    * @param tabPlacement The JTabbedPane's tab placement.
2484:    * @param run The run to get indent for.
2485:    *
2486:    * @return The amount a run should be indented.
2487:    */
2488:   protected int getTabRunIndent(int tabPlacement, int run)
2489:   {
2490:     return 0;
2491:   }
2492: 
2493:   /**
2494:    * This method returns whether a tab run should be padded.
2495:    *
2496:    * @param tabPlacement The JTabbedPane's tab placement.
2497:    * @param run The run to check.
2498:    *
2499:    * @return Whether the given run should be padded.
2500:    */
2501:   protected boolean shouldPadTabRun(int tabPlacement, int run)
2502:   {
2503:     return true;
2504:   }
2505: 
2506:   /**
2507:    * This method returns whether the tab runs should be rotated.
2508:    *
2509:    * @param tabPlacement The JTabbedPane's tab placement.
2510:    *
2511:    * @return Whether runs should be rotated.
2512:    */
2513:   protected boolean shouldRotateTabRuns(int tabPlacement)
2514:   {
2515:     return true;
2516:   }
2517: 
2518:   /**
2519:    * This method returns an icon for the tab. If the tab is disabled, it
2520:    * should return the disabledIcon. If it is enabled, then it should return
2521:    * the default icon.
2522:    *
2523:    * @param tabIndex The tab index to get an icon for.
2524:    *
2525:    * @return The icon for the tab index.
2526:    */
2527:   protected Icon getIconForTab(int tabIndex)
2528:   {
2529:     if (tabPane.isEnabledAt(tabIndex))
2530:       return tabPane.getIconAt(tabIndex);
2531:     else
2532:       return tabPane.getDisabledIconAt(tabIndex);
2533:   }
2534: 
2535:   /**
2536:    * This method returns a view that can paint the text for the label.
2537:    *
2538:    * @param tabIndex The tab index to get a view for.
2539:    *
2540:    * @return The view for the tab index.
2541:    */
2542:   protected View getTextViewForTab(int tabIndex)
2543:   {
2544:     return null;
2545:   }
2546: 
2547:   /**
2548:    * This method returns the tab height, including insets, for the given index
2549:    * and fontheight.
2550:    *
2551:    * @param tabPlacement The JTabbedPane's tab placement.
2552:    * @param tabIndex The index of the tab to calculate.
2553:    * @param fontHeight The font height.
2554:    *
2555:    * @return This tab's height.
2556:    */
2557:   protected int calculateTabHeight(int tabPlacement, int tabIndex,
2558:                                    int fontHeight)
2559:   {
2560:     Icon icon = getIconForTab(tabIndex);
2561:     Insets insets = getTabInsets(tabPlacement, tabIndex);
2562: 
2563:     if (icon != null)
2564:       {
2565:     Rectangle vr = new Rectangle();
2566:     Rectangle ir = new Rectangle();
2567:     Rectangle tr = new Rectangle();
2568:     layoutLabel(tabPlacement, getFontMetrics(), tabIndex,
2569:                 tabPane.getTitleAt(tabIndex), icon, vr, ir, tr,
2570:                 tabIndex == tabPane.getSelectedIndex());
2571:     calcRect = tr.union(ir);
2572:       }
2573:     else
2574:       calcRect.height = fontHeight;
2575: 
2576:     calcRect.height += insets.top + insets.bottom;
2577:     return calcRect.height;
2578:   }
2579: 
2580:   /**
2581:    * This method returns the max tab height.
2582:    *
2583:    * @param tabPlacement The JTabbedPane's tab placement.
2584:    *
2585:    * @return The maximum tab height.
2586:    */
2587:   protected int calculateMaxTabHeight(int tabPlacement)
2588:   {
2589:     maxTabHeight = 0;
2590: 
2591:     FontMetrics fm = getFontMetrics();
2592:     int fontHeight = fm.getHeight();
2593: 
2594:     for (int i = 0; i < tabPane.getTabCount(); i++)
2595:       maxTabHeight = Math.max(calculateTabHeight(tabPlacement, i, fontHeight),
2596:                               maxTabHeight);
2597: 
2598:     return maxTabHeight;
2599:   }
2600: 
2601:   /**
2602:    * This method calculates the tab width, including insets, for the given tab
2603:    * index and font metrics.
2604:    *
2605:    * @param tabPlacement The JTabbedPane's tab placement.
2606:    * @param tabIndex The tab index to calculate for.
2607:    * @param metrics The font's metrics.
2608:    *
2609:    * @return The tab width for the given index.
2610:    */
2611:   protected int calculateTabWidth(int tabPlacement, int tabIndex,
2612:                                   FontMetrics metrics)
2613:   {
2614:     Icon icon = getIconForTab(tabIndex);
2615:     Insets insets = getTabInsets(tabPlacement, tabIndex);
2616: 
2617:     if (icon != null)
2618:       {
2619:     Rectangle vr = new Rectangle();
2620:     Rectangle ir = new Rectangle();
2621:     Rectangle tr = new Rectangle();
2622:     layoutLabel(tabPlacement, getFontMetrics(), tabIndex,
2623:                 tabPane.getTitleAt(tabIndex), icon, vr, ir, tr,
2624:                 tabIndex == tabPane.getSelectedIndex());
2625:     calcRect = tr.union(ir);
2626:       }
2627:     else
2628:       calcRect.width = metrics.stringWidth(tabPane.getTitleAt(tabIndex));
2629: 
2630:     calcRect.width += insets.left + insets.right;
2631:     return calcRect.width;
2632:   }
2633: 
2634:   /**
2635:    * This method calculates the max tab width.
2636:    *
2637:    * @param tabPlacement The JTabbedPane's tab placement.
2638:    *
2639:    * @return The maximum tab width.
2640:    */
2641:   protected int calculateMaxTabWidth(int tabPlacement)
2642:   {
2643:     maxTabWidth = 0;
2644: 
2645:     FontMetrics fm = getFontMetrics();
2646: 
2647:     for (int i = 0; i < tabPane.getTabCount(); i++)
2648:       maxTabWidth = Math.max(calculateTabWidth(tabPlacement, i, fm),
2649:                              maxTabWidth);
2650: 
2651:     return maxTabWidth;
2652:   }
2653: 
2654:   /**
2655:    * This method calculates the tab area height, including insets, for the
2656:    * given amount of runs and tab height.
2657:    *
2658:    * @param tabPlacement The JTabbedPane's tab placement.
2659:    * @param horizRunCount The number of runs.
2660:    * @param maxTabHeight The max tab height.
2661:    *
2662:    * @return The tab area height.
2663:    */
2664:   protected int calculateTabAreaHeight(int tabPlacement, int horizRunCount,
2665:                                        int maxTabHeight)
2666:   {
2667:     Insets insets = getTabAreaInsets(tabPlacement);
2668:     int tabAreaHeight = horizRunCount * maxTabHeight
2669:                         - (horizRunCount - 1) * tabRunOverlay;
2670: 
2671:     tabAreaHeight += insets.top + insets.bottom;
2672: 
2673:     return tabAreaHeight;
2674:   }
2675: 
2676:   /**
2677:    * This method calculates the tab area width, including insets, for the
2678:    * given amount of runs and tab width.
2679:    *
2680:    * @param tabPlacement The JTabbedPane's tab placement.
2681:    * @param vertRunCount The number of runs.
2682:    * @param maxTabWidth The max tab width.
2683:    *
2684:    * @return The tab area width.
2685:    */
2686:   protected int calculateTabAreaWidth(int tabPlacement, int vertRunCount,
2687:                                       int maxTabWidth)
2688:   {
2689:     Insets insets = getTabAreaInsets(tabPlacement);
2690:     int tabAreaWidth = vertRunCount * maxTabWidth
2691:                        - (vertRunCount - 1) * tabRunOverlay;
2692: 
2693:     tabAreaWidth += insets.left + insets.right;
2694: 
2695:     return tabAreaWidth;
2696:   }
2697: 
2698:   /**
2699:    * This method returns the tab insets appropriately rotated.
2700:    *
2701:    * @param tabPlacement The JTabbedPane's tab placement.
2702:    * @param tabIndex The tab index.
2703:    *
2704:    * @return The tab insets for the given index.
2705:    */
2706:   protected Insets getTabInsets(int tabPlacement, int tabIndex)
2707:   {
2708:     Insets target = new Insets(0, 0, 0, 0);
2709:     rotateInsets(tabInsets, target, tabPlacement);
2710:     return target;
2711:   }
2712: 
2713:   /**
2714:    * This method returns the selected tab pad insets appropriately rotated.
2715:    *
2716:    * @param tabPlacement The JTabbedPane's tab placement.
2717:    *
2718:    * @return The selected tab pad insets.
2719:    */
2720:   protected Insets getSelectedTabPadInsets(int tabPlacement)
2721:   {
2722:     Insets target = new Insets(0, 0, 0, 0);
2723:     rotateInsets(selectedTabPadInsets, target, tabPlacement);
2724:     return target;
2725:   }
2726: 
2727:   /**
2728:    * This method returns the tab area insets appropriately rotated.
2729:    *
2730:    * @param tabPlacement The JTabbedPane's tab placement.
2731:    *
2732:    * @return The tab area insets.
2733:    */
2734:   protected Insets getTabAreaInsets(int tabPlacement)
2735:   {
2736:     Insets target = new Insets(0, 0, 0, 0);
2737:     rotateInsets(tabAreaInsets, target, tabPlacement);
2738:     return target;
2739:   }
2740: 
2741:   /**
2742:    * This method returns the content border insets appropriately rotated.
2743:    *
2744:    * @param tabPlacement The JTabbedPane's tab placement.
2745:    *
2746:    * @return The content border insets.
2747:    */
2748:   protected Insets getContentBorderInsets(int tabPlacement)
2749:   {
2750:     Insets target = new Insets(0, 0, 0, 0);
2751:     rotateInsets(contentBorderInsets, target, tabPlacement);
2752:     return target;
2753:   }
2754: 
2755:   /**
2756:    * This method returns the fontmetrics for the font of the JTabbedPane.
2757:    *
2758:    * @return The font metrics for the JTabbedPane.
2759:    */
2760:   protected FontMetrics getFontMetrics()
2761:   {
2762:     FontMetrics fm = tabPane.getToolkit().getFontMetrics(tabPane.getFont());
2763:     return fm;
2764:   }
2765: 
2766:   /**
2767:    * This method navigates from the selected tab into the given direction. As
2768:    * a result, a new tab will be selected (if possible).
2769:    *
2770:    * @param direction The direction to navigate in.
2771:    */
2772:   protected void navigateSelectedTab(int direction)
2773:   {
2774:     int tabPlacement = tabPane.getTabPlacement();
2775:     if (tabPlacement == SwingConstants.TOP
2776:         || tabPlacement == SwingConstants.BOTTOM)
2777:       {
2778:     if (direction == SwingConstants.WEST)
2779:       selectPreviousTabInRun(tabPane.getSelectedIndex());
2780:     else if (direction == SwingConstants.EAST)
2781:       selectNextTabInRun(tabPane.getSelectedIndex());
2782: 
2783:     else
2784:       {
2785:         int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(),
2786:                                      tabPane.getSelectedIndex(),
2787:                                      (tabPlacement == SwingConstants.RIGHT)
2788:                                      ? true : false);
2789:         selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(),
2790:                              offset);
2791:       }
2792:       }
2793:     if (tabPlacement == SwingConstants.LEFT
2794:         || tabPlacement == SwingConstants.RIGHT)
2795:       {
2796:     if (direction == SwingConstants.NORTH)
2797:       selectPreviousTabInRun(tabPane.getSelectedIndex());
2798:     else if (direction == SwingConstants.SOUTH)
2799:       selectNextTabInRun(tabPane.getSelectedIndex());
2800:     else
2801:       {
2802:         int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(),
2803:                                      tabPane.getSelectedIndex(),
2804:                                      (tabPlacement == SwingConstants.RIGHT)
2805:                                      ? true : false);
2806:         selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(),
2807:                              offset);
2808:       }
2809:       }
2810:   }
2811: 
2812:   /**
2813:    * This method selects the next tab in the run.
2814:    *
2815:    * @param current The current selected index.
2816:    */
2817:   protected void selectNextTabInRun(int current)
2818:   {
2819:     tabPane.setSelectedIndex(getNextTabIndexInRun(tabPane.getTabCount(),
2820:                                                   current));
2821:   }
2822: 
2823:   /**
2824:    * This method selects the previous tab in the run.
2825:    *
2826:    * @param current The current selected index.
2827:    */
2828:   protected void selectPreviousTabInRun(int current)
2829:   {
2830:     tabPane.setSelectedIndex(getPreviousTabIndexInRun(tabPane.getTabCount(),
2831:                                                       current));
2832:   }
2833: 
2834:   /**
2835:    * This method selects the next tab (regardless of runs).
2836:    *
2837:    * @param current The current selected index.
2838:    */
2839:   protected void selectNextTab(int current)
2840:   {
2841:     tabPane.setSelectedIndex(getNextTabIndex(current));
2842:   }
2843: 
2844:   /**
2845:    * This method selects the previous tab (regardless of runs).
2846:    *
2847:    * @param current The current selected index.
2848:    */
2849:   protected void selectPreviousTab(int current)
2850:   {
2851:     tabPane.setSelectedIndex(getPreviousTabIndex(current));
2852:   }
2853: 
2854:   /**
2855:    * This method selects the correct tab given an offset from the current tab
2856:    * index. If the tab placement is TOP or BOTTOM, the offset will be in the
2857:    * y direction, otherwise, it will be in the x direction. A new coordinate
2858:    * will be found by adding the offset to the current location of the tab.
2859:    * The tab that the new location will be selected.
2860:    *
2861:    * @param tabPlacement The JTabbedPane's tab placement.
2862:    * @param tabIndex The tab to start from.
2863:    * @param offset The coordinate offset.
2864:    */
2865:   protected void selectAdjacentRunTab(int tabPlacement, int tabIndex,
2866:                                       int offset)
2867:   {
2868:     int x = rects[tabIndex].x + rects[tabIndex].width / 2;
2869:     int y = rects[tabIndex].y + rects[tabIndex].height / 2;
2870: 
2871:     switch (tabPlacement)
2872:       {
2873:       case SwingConstants.TOP:
2874:       case SwingConstants.BOTTOM:
2875:     y += offset;
2876:     break;
2877:       case SwingConstants.RIGHT:
2878:       case SwingConstants.LEFT:
2879:     x += offset;
2880:     break;
2881:       }
2882: 
2883:     int index = tabForCoordinate(tabPane, x, y);
2884:     if (index != -1)
2885:       tabPane.setSelectedIndex(index);
2886:   }
2887: 
2888:   // This method is called when you press up/down to cycle through tab runs.
2889:   // it returns the distance (between the two runs' x/y position.
2890:   // where one run is the current selected run and the other run is the run in the
2891:   // direction of the scroll (dictated by the forward flag)
2892:   // the offset is an absolute value of the difference
2893: 
2894:   /**
2895:    * This method calculates the offset distance for use in
2896:    * selectAdjacentRunTab. The offset returned will be a difference in the y
2897:    * coordinate between the run in  the desired direction and the current run
2898:    * (for tabPlacement in TOP or BOTTOM). Use x coordinate for LEFT and
2899:    * RIGHT.
2900:    *
2901:    * @param tabPlacement The JTabbedPane's tab placement.
2902:    * @param tabCount The number of tabs.
2903:    * @param tabIndex The starting index.
2904:    * @param forward If forward, the run in the desired direction will be the
2905:    *        next run.
2906:    *
2907:    * @return The offset between the two runs.
2908:    */
2909:   protected int getTabRunOffset(int tabPlacement, int tabCount, int tabIndex,
2910:                                 boolean forward)
2911:   {
2912:     int currRun = getRunForTab(tabCount, tabIndex);
2913:     int offset;
2914:     int nextRun = (forward) ? getNextTabRun(currRun) : getPreviousTabRun(currRun);
2915:     if (tabPlacement == SwingConstants.TOP
2916:         || tabPlacement == SwingConstants.BOTTOM)
2917:       offset = rects[lastTabInRun(tabCount, nextRun)].y
2918:                - rects[lastTabInRun(tabCount, currRun)].y;
2919:     else
2920:       offset = rects[lastTabInRun(tabCount, nextRun)].x
2921:                - rects[lastTabInRun(tabCount, currRun)].x;
2922:     return offset;
2923:   }
2924: 
2925:   /**
2926:    * This method returns the previous tab index.
2927:    *
2928:    * @param base The index to start from.
2929:    *
2930:    * @return The previous tab index.
2931:    */
2932:   protected int getPreviousTabIndex(int base)
2933:   {
2934:     base--;
2935:     if (base < 0)
2936:       return tabPane.getTabCount() - 1;
2937:     return base;
2938:   }
2939: 
2940:   /**
2941:    * This method returns the next tab index.
2942:    *
2943:    * @param base The index to start from.
2944:    *
2945:    * @return The next tab index.
2946:    */
2947:   protected int getNextTabIndex(int base)
2948:   {
2949:     base++;
2950:     if (base == tabPane.getTabCount())
2951:       return 0;
2952:     return base;
2953:   }
2954: 
2955:   /**
2956:    * This method returns the next tab index in the run. If the next index is
2957:    * out of this run, it will return the starting tab index for the run.
2958:    *
2959:    * @param tabCount The number of tabs.
2960:    * @param base The index to start from.
2961:    *
2962:    * @return The next tab index in the run.
2963:    */
2964:   protected int getNextTabIndexInRun(int tabCount, int base)
2965:   {
2966:     int index = getNextTabIndex(base);
2967:     int run = getRunForTab(tabCount, base);
2968:     if (index == lastTabInRun(tabCount, run) + 1)
2969:       index = lastTabInRun(tabCount, getPreviousTabRun(run)) + 1;
2970:     return getNextTabIndex(base);
2971:   }
2972: 
2973:   /**
2974:    * This method returns the previous tab index in the run. If the previous
2975:    * index is out of this run, it will return the last index for the run.
2976:    *
2977:    * @param tabCount The number of tabs.
2978:    * @param base The index to start from.
2979:    *
2980:    * @return The previous tab index in the run.
2981:    */
2982:   protected int getPreviousTabIndexInRun(int tabCount, int base)
2983:   {
2984:     int index = getPreviousTabIndex(base);
2985:     int run = getRunForTab(tabCount, base);
2986:     if (index == lastTabInRun(tabCount, getPreviousTabRun(run)))
2987:       index = lastTabInRun(tabCount, run);
2988:     return getPreviousTabIndex(base);
2989:   }
2990: 
2991:   /**
2992:    * This method returns the index of the previous run.
2993:    *
2994:    * @param baseRun The run to start from.
2995:    *
2996:    * @return The index of the previous run.
2997:    */
2998:   protected int getPreviousTabRun(int baseRun)
2999:   {
3000:     if (getTabRunCount(tabPane) == 1)
3001:       return 1;
3002: 
3003:     int prevRun = --baseRun;
3004:     if (prevRun < 0)
3005:       prevRun = getTabRunCount(tabPane) - 1;
3006:     return prevRun;
3007:   }
3008: 
3009:   /**
3010:    * This method returns the index of the next run.
3011:    *
3012:    * @param baseRun The run to start from.
3013:    *
3014:    * @return The index of the next run.
3015:    */
3016:   protected int getNextTabRun(int baseRun)
3017:   {
3018:     if (getTabRunCount(tabPane) == 1)
3019:       return 1;
3020: 
3021:     int nextRun = ++baseRun;
3022:     if (nextRun == getTabRunCount(tabPane))
3023:       nextRun = 0;
3024:     return nextRun;
3025:   }
3026: 
3027:   /**
3028:    * This method rotates the insets given a direction to rotate them in.
3029:    * Target placement should be one of TOP, LEFT, BOTTOM, RIGHT. The  rotated
3030:    * insets will be stored in targetInsets. Passing in TOP as  the direction
3031:    * does nothing. Passing in LEFT switches top and left, right and bottom.
3032:    * Passing in BOTTOM switches top and bottom. Passing in RIGHT switches top
3033:    * for left, left for bottom, bottom for right, and right for top.
3034:    *
3035:    * @param topInsets The reference insets.
3036:    * @param targetInsets An Insets object to store the new insets.
3037:    * @param targetPlacement The rotation direction.
3038:    */
3039:   protected static void rotateInsets(Insets topInsets, Insets targetInsets,
3040:                                      int targetPlacement)
3041:   {
3042:     // Sun's version will happily throw an NPE if params are null,
3043:     // so I won't check it either.
3044:     switch (targetPlacement)
3045:       {
3046:       case SwingConstants.TOP:
3047:     targetInsets.top = topInsets.top;
3048:     targetInsets.left = topInsets.left;
3049:     targetInsets.right = topInsets.right;
3050:     targetInsets.bottom = topInsets.bottom;
3051:     break;
3052:       case SwingConstants.LEFT:
3053:     targetInsets.left = topInsets.top;
3054:     targetInsets.top = topInsets.left;
3055:     targetInsets.right = topInsets.bottom;
3056:     targetInsets.bottom = topInsets.right;
3057:     break;
3058:       case SwingConstants.BOTTOM:
3059:     targetInsets.top = topInsets.bottom;
3060:     targetInsets.bottom = topInsets.top;
3061:     targetInsets.left = topInsets.left;
3062:     targetInsets.right = topInsets.right;
3063:     break;
3064:       case SwingConstants.RIGHT:
3065:     targetInsets.top = topInsets.left;
3066:     targetInsets.left = topInsets.bottom;
3067:     targetInsets.bottom = topInsets.right;
3068:     targetInsets.right = topInsets.top;
3069:     break;
3070:       }
3071:   }
3072: }