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 preferred size of the JTabbedPane.
1684:    *
1685:    * @param c The JComponent to find a size for.
1686:    *
1687:    * @return The preferred size.
1688:    */
1689:   public Dimension getPreferredSize(JComponent c)
1690:   {
1691:     return layoutManager.preferredLayoutSize(tabPane);
1692:   }
1693: 
1694:   /**
1695:    * This method returns the minimum size of the JTabbedPane.
1696:    *
1697:    * @param c The JComponent to find a size for.
1698:    *
1699:    * @return The minimum size.
1700:    */
1701:   public Dimension getMinimumSize(JComponent c)
1702:   {
1703:     return layoutManager.minimumLayoutSize(tabPane);
1704:   }
1705: 
1706:   /**
1707:    * This method returns the maximum size of the JTabbedPane.
1708:    *
1709:    * @param c The JComponent to find a size for.
1710:    *
1711:    * @return The maximum size.
1712:    */
1713:   public Dimension getMaximumSize(JComponent c)
1714:   {
1715:     return new Dimension(Short.MAX_VALUE, Short.MAX_VALUE);
1716:   }
1717: 
1718:   /**
1719:    * This method paints the JTabbedPane.
1720:    *
1721:    * @param g The Graphics object to paint with.
1722:    * @param c The JComponent to paint.
1723:    */
1724:   public void paint(Graphics g, JComponent c)
1725:   {
1726:     if (tabPane.getTabCount() == 0)
1727:       return;
1728:     if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT)
1729:       paintTabArea(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex());
1730:     paintContentBorder(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex());
1731:   }
1732: 
1733:   /**
1734:    * This method paints the tab area. This includes painting the rectangles
1735:    * that make up the tabs.
1736:    *
1737:    * @param g The Graphics object to paint with.
1738:    * @param tabPlacement The JTabbedPane's tab placement.
1739:    * @param selectedIndex The selected index.
1740:    */
1741:   protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex)
1742:   {
1743:     Rectangle ir = new Rectangle();
1744:     Rectangle tr = new Rectangle();
1745: 
1746:     boolean isScroll = tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT;
1747: 
1748:     // Please note: the ordering of the painting is important. 
1749:     // we WANT to paint the outermost run first and then work our way in.
1750:     int tabCount = tabPane.getTabCount();
1751:     int currRun = 1;
1752:     if (tabCount < 1)
1753:       return;
1754: 
1755:     if (runCount > 1)
1756:       currRun = 0;
1757:     for (int i = 0; i < runCount; i++)
1758:       {
1759:     int first = lastTabInRun(tabCount, getPreviousTabRun(currRun)) + 1;
1760:     if (isScroll)
1761:       first = currentScrollLocation;
1762:     else if (first == tabCount)
1763:       first = 0;
1764:     int last = lastTabInRun(tabCount, currRun);
1765:     if (isScroll)
1766:       {
1767:         for (int k = first; k < tabCount; k++)
1768:           {
1769:         if (rects[k].x + rects[k].width - rects[first].x > viewport
1770:                                                            .getWidth())
1771:           {
1772:             last = k;
1773:             break;
1774:           }
1775:           }
1776:       }
1777: 
1778:     for (int j = first; j <= last; j++)
1779:       {
1780:         if (j != selectedIndex || isScroll)
1781:           paintTab(g, tabPlacement, rects, j, ir, tr);
1782:       }
1783:     currRun = getPreviousTabRun(currRun);
1784:       }
1785:     if (! isScroll)
1786:       paintTab(g, tabPlacement, rects, selectedIndex, ir, tr);
1787:   }
1788: 
1789:   /**
1790:    * This method paints an individual tab.
1791:    *
1792:    * @param g The Graphics object to paint with.
1793:    * @param tabPlacement The JTabbedPane's tab placement.
1794:    * @param rects The array of rectangles that keep the size and position of
1795:    *        the tabs.
1796:    * @param tabIndex The tab index to paint.
1797:    * @param iconRect The rectangle to use for the icon.
1798:    * @param textRect The rectangle to use for the text.
1799:    */
1800:   protected void paintTab(Graphics g, int tabPlacement, Rectangle[] rects,
1801:                           int tabIndex, Rectangle iconRect, Rectangle textRect)
1802:   {
1803:     FontMetrics fm = getFontMetrics();
1804:     Icon icon = getIconForTab(tabIndex);
1805:     String title = tabPane.getTitleAt(tabIndex);
1806:     boolean isSelected = tabIndex == tabPane.getSelectedIndex();
1807:     calcRect = getTabBounds(tabPane, tabIndex);
1808: 
1809:     int x = calcRect.x;
1810:     int y = calcRect.y;
1811:     int w = calcRect.width;
1812:     int h = calcRect.height;
1813:     if (getRunForTab(tabPane.getTabCount(), tabIndex) == 1)
1814:       {
1815:     Insets insets = getTabAreaInsets(tabPlacement);
1816:     switch (tabPlacement)
1817:       {
1818:       case TOP:
1819:         h += insets.bottom;
1820:         break;
1821:       case LEFT:
1822:         w += insets.right;
1823:         break;
1824:       case BOTTOM:
1825:         y -= insets.top;
1826:         h += insets.top;
1827:         break;
1828:       case RIGHT:
1829:         x -= insets.left;
1830:         w += insets.left;
1831:         break;
1832:       }
1833:       }
1834: 
1835:     layoutLabel(tabPlacement, fm, tabIndex, title, icon, calcRect, iconRect,
1836:                 textRect, isSelected);
1837:     paintTabBackground(g, tabPlacement, tabIndex, x, y, w, h, isSelected);
1838:     paintTabBorder(g, tabPlacement, tabIndex, x, y, w, h, isSelected);
1839: 
1840:     // FIXME: Paint little folding corner and jagged edge clipped tab.
1841:     if (icon != null)
1842:       paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected);
1843:     if (title != null && ! title.equals(""))
1844:       paintText(g, tabPlacement, tabPane.getFont(), fm, tabIndex, title,
1845:                 textRect, isSelected);
1846:   }
1847: 
1848:   /**
1849:    * This method lays out the tab and finds the location to paint the  icon
1850:    * and text.
1851:    *
1852:    * @param tabPlacement The JTabbedPane's tab placement.
1853:    * @param metrics The font metrics for the font to paint with.
1854:    * @param tabIndex The tab index to paint.
1855:    * @param title The string painted.
1856:    * @param icon The icon painted.
1857:    * @param tabRect The tab bounds.
1858:    * @param iconRect The calculated icon bounds.
1859:    * @param textRect The calculated text bounds.
1860:    * @param isSelected Whether this tab is selected.
1861:    */
1862:   protected void layoutLabel(int tabPlacement, FontMetrics metrics,
1863:                              int tabIndex, String title, Icon icon,
1864:                              Rectangle tabRect, Rectangle iconRect,
1865:                              Rectangle textRect, boolean isSelected)
1866:   {
1867:     SwingUtilities.layoutCompoundLabel(metrics, title, icon,
1868:                                        SwingConstants.CENTER,
1869:                                        SwingConstants.CENTER,
1870:                                        SwingConstants.CENTER,
1871:                                        SwingConstants.CENTER, tabRect,
1872:                                        iconRect, textRect, textIconGap);
1873: 
1874:     int shiftX = getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
1875:     int shiftY = getTabLabelShiftY(tabPlacement, tabIndex, isSelected);
1876: 
1877:     iconRect.x += shiftX;
1878:     iconRect.y += shiftY;
1879: 
1880:     textRect.x += shiftX;
1881:     textRect.y += shiftY;
1882:   }
1883: 
1884:   /**
1885:    * This method paints the icon.
1886:    *
1887:    * @param g The Graphics object to paint.
1888:    * @param tabPlacement The JTabbedPane's tab placement.
1889:    * @param tabIndex The tab index to paint.
1890:    * @param icon The icon to paint.
1891:    * @param iconRect The bounds of the icon.
1892:    * @param isSelected Whether this tab is selected.
1893:    */
1894:   protected void paintIcon(Graphics g, int tabPlacement, int tabIndex,
1895:                            Icon icon, Rectangle iconRect, boolean isSelected)
1896:   {
1897:     icon.paintIcon(tabPane, g, iconRect.x, iconRect.y);
1898:   }
1899: 
1900:   /**
1901:    * This method paints the text for the given tab.
1902:    *
1903:    * @param g The Graphics object to paint with.
1904:    * @param tabPlacement The JTabbedPane's tab placement.
1905:    * @param font The font to paint with.
1906:    * @param metrics The fontmetrics of the given font.
1907:    * @param tabIndex The tab index.
1908:    * @param title The string to paint.
1909:    * @param textRect The bounds of the string.
1910:    * @param isSelected Whether this tab is selected.
1911:    */
1912:   protected void paintText(Graphics g, int tabPlacement, Font font,
1913:                            FontMetrics metrics, int tabIndex, String title,
1914:                            Rectangle textRect, boolean isSelected)
1915:   {
1916:     View textView = getTextViewForTab(tabIndex);
1917:     if (textView != null)
1918:       {
1919:     textView.paint(g, textRect);
1920:     return;
1921:       }
1922: 
1923:     Color fg = tabPane.getForegroundAt(tabIndex);
1924:     if (fg == null)
1925:       fg = tabPane.getForeground();
1926:     Color bg = tabPane.getBackgroundAt(tabIndex);
1927:     if (bg == null)
1928:       bg = tabPane.getBackground();
1929: 
1930:     Color saved_color = g.getColor();
1931:     Font f = g.getFont();
1932:     g.setFont(font);
1933: 
1934:     if (tabPane.isEnabledAt(tabIndex))
1935:       {
1936:     g.setColor(fg);
1937: 
1938:     int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
1939: 
1940:     if (mnemIndex != -1)
1941:       BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
1942:                                                    textRect.x,
1943:                                                    textRect.y
1944:                                                    + metrics.getAscent());
1945:     else
1946:       g.drawString(title, textRect.x, textRect.y + metrics.getAscent());
1947:       }
1948:     else
1949:       {
1950:     g.setColor(bg.brighter());
1951: 
1952:     int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
1953: 
1954:     if (mnemIndex != -1)
1955:       BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
1956:                                                    textRect.x, textRect.y);
1957:     else
1958:       g.drawString(title, textRect.x, textRect.y);
1959: 
1960:     g.setColor(bg.darker());
1961:     if (mnemIndex != -1)
1962:       BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
1963:                                                    textRect.x + 1,
1964:                                                    textRect.y + 1);
1965:     else
1966:       g.drawString(title, textRect.x + 1, textRect.y + 1);
1967:       }
1968: 
1969:     g.setColor(saved_color);
1970:     g.setFont(f);
1971:   }
1972: 
1973:   /**
1974:    * This method returns how much the label for the tab should shift in the X
1975:    * direction.
1976:    *
1977:    * @param tabPlacement The JTabbedPane's tab placement.
1978:    * @param tabIndex The tab index being painted.
1979:    * @param isSelected Whether this tab is selected.
1980:    *
1981:    * @return The amount the label should shift by in the X direction.
1982:    */
1983:   protected int getTabLabelShiftX(int tabPlacement, int tabIndex,
1984:                                   boolean isSelected)
1985:   {
1986:     // No reason to shift.
1987:     return 0;
1988:   }
1989: 
1990:   /**
1991:    * This method returns how much the label for the tab should shift in the Y
1992:    * direction.
1993:    *
1994:    * @param tabPlacement The JTabbedPane's tab placement.
1995:    * @param tabIndex The tab index being painted.
1996:    * @param isSelected Whether this tab is selected.
1997:    *
1998:    * @return The amount the label should shift by in the Y direction.
1999:    */
2000:   protected int getTabLabelShiftY(int tabPlacement, int tabIndex,
2001:                                   boolean isSelected)
2002:   {
2003:     // No reason to shift.
2004:     return 0;
2005:   }
2006: 
2007:   /**
2008:    * This method paints the focus rectangle around the selected tab.
2009:    *
2010:    * @param g The Graphics object to paint with.
2011:    * @param tabPlacement The JTabbedPane's tab placement.
2012:    * @param rects The array of rectangles keeping track of size and position.
2013:    * @param tabIndex The tab index.
2014:    * @param iconRect The icon bounds.
2015:    * @param textRect The text bounds.
2016:    * @param isSelected Whether this tab is selected.
2017:    */
2018:   protected void paintFocusIndicator(Graphics g, int tabPlacement,
2019:                                      Rectangle[] rects, int tabIndex,
2020:                                      Rectangle iconRect, Rectangle textRect,
2021:                                      boolean isSelected)
2022:   {
2023:     Color saved = g.getColor();
2024:     calcRect = iconRect.union(textRect);
2025: 
2026:     g.setColor(focus);
2027: 
2028:     g.drawRect(calcRect.x, calcRect.y, calcRect.width, calcRect.height);
2029: 
2030:     g.setColor(saved);
2031:   }
2032: 
2033:   /**
2034:    * This method paints the border for an individual tab.
2035:    *
2036:    * @param g The Graphics object to paint with.
2037:    * @param tabPlacement The JTabbedPane's tab placement.
2038:    * @param tabIndex The tab index.
2039:    * @param x The x position of the tab.
2040:    * @param y The y position of the tab.
2041:    * @param w The width of the tab.
2042:    * @param h The height of the tab.
2043:    * @param isSelected Whether the tab is selected.
2044:    */
2045:   protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex,
2046:                                 int x, int y, int w, int h, boolean isSelected)
2047:   {
2048:     Color saved = g.getColor();
2049: 
2050:     if (! isSelected || tabPlacement != SwingConstants.TOP)
2051:       {
2052:     g.setColor(shadow);
2053:     g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1);
2054:     g.setColor(darkShadow);
2055:     g.drawLine(x, y + h, x + w, y + h);
2056:       }
2057: 
2058:     if (! isSelected || tabPlacement != SwingConstants.LEFT)
2059:       {
2060:     g.setColor(darkShadow);
2061:     g.drawLine(x + w, y, x + w, y + h);
2062:     g.setColor(shadow);
2063:     g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1);
2064:       }
2065: 
2066:     if (! isSelected || tabPlacement != SwingConstants.RIGHT)
2067:       {
2068:     g.setColor(lightHighlight);
2069:     g.drawLine(x, y, x, y + h);
2070:       }
2071: 
2072:     if (! isSelected || tabPlacement != SwingConstants.BOTTOM)
2073:       {
2074:     g.setColor(lightHighlight);
2075:     g.drawLine(x, y, x + w, y);
2076:       }
2077: 
2078:     g.setColor(saved);
2079:   }
2080: 
2081:   /**
2082:    * This method paints the background for an individual tab.
2083:    *
2084:    * @param g The Graphics object to paint with.
2085:    * @param tabPlacement The JTabbedPane's tab placement.
2086:    * @param tabIndex The tab index.
2087:    * @param x The x position of the tab.
2088:    * @param y The y position of the tab.
2089:    * @param w The width of the tab.
2090:    * @param h The height of the tab.
2091:    * @param isSelected Whether the tab is selected.
2092:    */
2093:   protected void paintTabBackground(Graphics g, int tabPlacement,
2094:                                     int tabIndex, int x, int y, int w, int h,
2095:                                     boolean isSelected)
2096:   {
2097:     Color saved = g.getColor();
2098:     if (isSelected)
2099:       g.setColor(Color.LIGHT_GRAY);
2100:     else
2101:       {
2102:     Color bg = tabPane.getBackgroundAt(tabIndex);
2103:     if (bg == null)
2104:       bg = Color.GRAY;
2105:     g.setColor(bg);
2106:       }
2107: 
2108:     g.fillRect(x, y, w, h);
2109: 
2110:     g.setColor(saved);
2111:   }
2112: 
2113:   /**
2114:    * This method paints the border around the content area.
2115:    *
2116:    * @param g The Graphics object to paint with.
2117:    * @param tabPlacement The JTabbedPane's tab placement.
2118:    * @param selectedIndex The index of the selected tab.
2119:    */
2120:   protected void paintContentBorder(Graphics g, int tabPlacement,
2121:                                     int selectedIndex)
2122:   {
2123:     Insets insets = getContentBorderInsets(tabPlacement);
2124:     int x = contentRect.x;
2125:     int y = contentRect.y;
2126:     int w = contentRect.width;
2127:     int h = contentRect.height;
2128:     paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2129:     paintContentBorderLeftEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2130:     paintContentBorderBottomEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2131:     paintContentBorderRightEdge(g, tabPlacement, selectedIndex, x, y, w, h);
2132:   }
2133: 
2134:   /**
2135:    * This method paints the top edge of the content border.
2136:    *
2137:    * @param g The Graphics object to paint with.
2138:    * @param tabPlacement The JTabbedPane's tab placement.
2139:    * @param selectedIndex The selected tab index.
2140:    * @param x The x coordinate for the content area.
2141:    * @param y The y coordinate for the content area.
2142:    * @param w The width of the content area.
2143:    * @param h The height of the content area.
2144:    */
2145:   protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
2146:                                            int selectedIndex, int x, int y,
2147:                                            int w, int h)
2148:   {
2149:     Color saved = g.getColor();
2150:     g.setColor(lightHighlight);
2151: 
2152:     int startgap = rects[selectedIndex].x;
2153:     int endgap = rects[selectedIndex].x + rects[selectedIndex].width;
2154: 
2155:     int diff = 0;
2156: 
2157:     if (tabPlacement == SwingConstants.TOP)
2158:       {
2159:     if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
2160:       {
2161:         Point p = findPointForIndex(currentScrollLocation);
2162:         diff = p.x;
2163:       }
2164: 
2165:     g.drawLine(x, y, startgap - diff, y);
2166:     g.drawLine(endgap - diff, y, x + w, y);
2167:       }
2168:     else
2169:       g.drawLine(x, y, x + w, y);
2170: 
2171:     g.setColor(saved);
2172:   }
2173: 
2174:   /**
2175:    * This method paints the left edge of the content border.
2176:    *
2177:    * @param g The Graphics object to paint with.
2178:    * @param tabPlacement The JTabbedPane's tab placement.
2179:    * @param selectedIndex The selected tab index.
2180:    * @param x The x coordinate for the content area.
2181:    * @param y The y coordinate for the content area.
2182:    * @param w The width of the content area.
2183:    * @param h The height of the content area.
2184:    */
2185:   protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
2186:                                             int selectedIndex, int x, int y,
2187:                                             int w, int h)
2188:   {
2189:     Color saved = g.getColor();
2190:     g.setColor(lightHighlight);
2191: 
2192:     int startgap = rects[selectedIndex].y;
2193:     int endgap = rects[selectedIndex].y + rects[selectedIndex].height;
2194: 
2195:     int diff = 0;
2196: 
2197:     if (tabPlacement == SwingConstants.LEFT)
2198:       {
2199:     if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
2200:       {
2201:         Point p = findPointForIndex(currentScrollLocation);
2202:         diff = p.y;
2203:       }
2204: 
2205:     g.drawLine(x, y, x, startgap - diff);
2206:     g.drawLine(x, endgap - diff, x, y + h);
2207:       }
2208:     else
2209:       g.drawLine(x, y, x, y + h);
2210: 
2211:     g.setColor(saved);
2212:   }
2213: 
2214:   /**
2215:    * This method paints the bottom edge of the content border.
2216:    *
2217:    * @param g The Graphics object to paint with.
2218:    * @param tabPlacement The JTabbedPane's tab placement.
2219:    * @param selectedIndex The selected tab index.
2220:    * @param x The x coordinate for the content area.
2221:    * @param y The y coordinate for the content area.
2222:    * @param w The width of the content area.
2223:    * @param h The height of the content area.
2224:    */
2225:   protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
2226:                                               int selectedIndex, int x, int y,
2227:                                               int w, int h)
2228:   {
2229:     Color saved = g.getColor();
2230: 
2231:     int startgap = rects[selectedIndex].x;
2232:     int endgap = rects[selectedIndex].x + rects[selectedIndex].width;
2233: 
2234:     int diff = 0;
2235: 
2236:     if (tabPlacement == SwingConstants.BOTTOM)
2237:       {
2238:     if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
2239:       {
2240:         Point p = findPointForIndex(currentScrollLocation);
2241:         diff = p.x;
2242:       }
2243: 
2244:     g.setColor(shadow);
2245:     g.drawLine(x + 1, y + h - 1, startgap - diff, y + h - 1);
2246:     g.drawLine(endgap - diff, y + h - 1, x + w - 1, y + h - 1);
2247: 
2248:     g.setColor(darkShadow);
2249:     g.drawLine(x, y + h, startgap - diff, y + h);
2250:     g.drawLine(endgap - diff, y + h, x + w, y + h);
2251:       }
2252:     else
2253:       {
2254:     g.setColor(shadow);
2255:     g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1);
2256:     g.setColor(darkShadow);
2257:     g.drawLine(x, y + h, x + w, y + h);
2258:       }
2259: 
2260:     g.setColor(saved);
2261:   }
2262: 
2263:   /**
2264:    * This method paints the right edge of the content border.
2265:    *
2266:    * @param g The Graphics object to paint with.
2267:    * @param tabPlacement The JTabbedPane's tab placement.
2268:    * @param selectedIndex The selected tab index.
2269:    * @param x The x coordinate for the content area.
2270:    * @param y The y coordinate for the content area.
2271:    * @param w The width of the content area.
2272:    * @param h The height of the content area.
2273:    */
2274:   protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
2275:                                              int selectedIndex, int x, int y,
2276:                                              int w, int h)
2277:   {
2278:     Color saved = g.getColor();
2279:     int startgap = rects[selectedIndex].y;
2280:     int endgap = rects[selectedIndex].y + rects[selectedIndex].height;
2281: 
2282:     int diff = 0;
2283: 
2284:     if (tabPlacement == SwingConstants.RIGHT)
2285:       {
2286:     if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
2287:       {
2288:         Point p = findPointForIndex(currentScrollLocation);
2289:         diff = p.y;
2290:       }
2291: 
2292:     g.setColor(shadow);
2293:     g.drawLine(x + w - 1, y + 1, x + w - 1, startgap - diff);
2294:     g.drawLine(x + w - 1, endgap - diff, x + w - 1, y + h - 1);
2295: 
2296:     g.setColor(darkShadow);
2297:     g.drawLine(x + w, y, x + w, startgap - diff);
2298:     g.drawLine(x + w, endgap - diff, x + w, y + h);
2299:       }
2300:     else
2301:       {
2302:     g.setColor(shadow);
2303:     g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1);
2304:     g.setColor(darkShadow);
2305:     g.drawLine(x + w, y, x + w, y + h);
2306:       }
2307: 
2308:     g.setColor(saved);
2309:   }
2310: 
2311:   /**
2312:    * This method returns the tab bounds for the given index.
2313:    *
2314:    * @param pane The JTabbedPane.
2315:    * @param i The index to look for.
2316:    *
2317:    * @return The bounds of the tab with the given index.
2318:    */
2319:   public Rectangle getTabBounds(JTabbedPane pane, int i)
2320:   {
2321:     return rects[i];
2322:   }
2323: 
2324:   /**
2325:    * This method returns the number of runs.
2326:    *
2327:    * @param pane The JTabbedPane.
2328:    *
2329:    * @return The number of runs.
2330:    */
2331:   public int getTabRunCount(JTabbedPane pane)
2332:   {
2333:     return runCount;
2334:   }
2335: 
2336:   /**
2337:    * This method returns the tab index given a coordinate.
2338:    *
2339:    * @param pane The JTabbedPane.
2340:    * @param x The x coordinate.
2341:    * @param y The y coordinate.
2342:    *
2343:    * @return The tab index that the coordinate lands in.
2344:    */
2345:   public int tabForCoordinate(JTabbedPane pane, int x, int y)
2346:   {
2347:     Point p = new Point(x, y);
2348:     int tabCount = tabPane.getTabCount();
2349:     int currRun = 1;
2350:     for (int i = 0; i < runCount; i++)
2351:       {
2352:     int first = lastTabInRun(tabCount, getPreviousTabRun(currRun)) + 1;
2353:     if (first == tabCount)
2354:       first = 0;
2355:     int last = lastTabInRun(tabCount, currRun);
2356:     for (int j = first; j <= last; j++)
2357:       {
2358:         if (getTabBounds(pane, j).contains(p))
2359:           return j;
2360:       }
2361:     currRun = getNextTabRun(currRun);
2362:       }
2363:     return -1;
2364:   }
2365: 
2366:   /**
2367:    * This method returns the tab bounds in the given rectangle.
2368:    *
2369:    * @param tabIndex The index to get bounds for.
2370:    * @param dest The rectangle to store bounds in.
2371:    *
2372:    * @return The rectangle passed in.
2373:    */
2374:   protected Rectangle getTabBounds(int tabIndex, Rectangle dest)
2375:   {
2376:     dest.setBounds(getTabBounds(tabPane, tabIndex));
2377:     return dest;
2378:   }
2379: 
2380:   /**
2381:    * This method returns the component that is shown in  the content area.
2382:    *
2383:    * @return The component that is shown in the content area.
2384:    */
2385:   protected Component getVisibleComponent()
2386:   {
2387:     return tabPane.getComponentAt(tabPane.getSelectedIndex());
2388:   }
2389: 
2390:   /**
2391:    * This method sets the visible component.
2392:    *
2393:    * @param component The component to be set visible.
2394:    */
2395:   protected void setVisibleComponent(Component component)
2396:   {
2397:     component.setVisible(true);
2398:     tabPane.setSelectedComponent(component);
2399:   }
2400: 
2401:   /**
2402:    * This method assures that enough rectangles are created given the
2403:    * tabCount. The old array is copied to the  new one.
2404:    *
2405:    * @param tabCount The number of tabs.
2406:    */
2407:   protected void assureRectsCreated(int tabCount)
2408:   {
2409:     if (rects == null)
2410:       rects = new Rectangle[tabCount];
2411:     if (tabCount == rects.length)
2412:       return;
2413:     else
2414:       {
2415:     int numToCopy = Math.min(tabCount, rects.length);
2416:     Rectangle[] tmp = new Rectangle[tabCount];
2417:     System.arraycopy(rects, 0, tmp, 0, numToCopy);
2418:     rects = tmp;
2419:       }
2420:   }
2421: 
2422:   /**
2423:    * This method expands the tabRuns array to give it more room. The old array
2424:    * is copied to the new one.
2425:    */
2426:   protected void expandTabRunsArray()
2427:   {
2428:     // This method adds another 10 index positions to the tabRuns array.
2429:     if (tabRuns == null)
2430:       tabRuns = new int[10];
2431:     else
2432:       {
2433:     int[] newRuns = new int[tabRuns.length + 10];
2434:     System.arraycopy(tabRuns, 0, newRuns, 0, tabRuns.length);
2435:     tabRuns = newRuns;
2436:       }
2437:   }
2438: 
2439:   /**
2440:    * This method returns which run a particular tab belongs to.
2441:    *
2442:    * @param tabCount The number of tabs.
2443:    * @param tabIndex The tab to find.
2444:    *
2445:    * @return The tabRuns index that it belongs to.
2446:    */
2447:   protected int getRunForTab(int tabCount, int tabIndex)
2448:   {
2449:     if (runCount == 1 && tabIndex < tabCount && tabIndex >= 0)
2450:       return 1;
2451:     for (int i = 0; i < runCount; i++)
2452:       {
2453:     int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1;
2454:     if (first == tabCount)
2455:       first = 0;
2456:     int last = lastTabInRun(tabCount, i);
2457:     if (last >= tabIndex && first <= tabIndex)
2458:       return i;
2459:       }
2460:     return -1;
2461:   }
2462: 
2463:   /**
2464:    * This method returns the index of the last tab in  a run.
2465:    *
2466:    * @param tabCount The number of tabs.
2467:    * @param run The run to check.
2468:    *
2469:    * @return The last tab in the given run.
2470:    */
2471:   protected int lastTabInRun(int tabCount, int run)
2472:   {
2473:     if (tabRuns[run] == 0)
2474:       return tabCount - 1;
2475:     else
2476:       return tabRuns[run] - 1;
2477:   }
2478: 
2479:   /**
2480:    * This method returns the tab run overlay.
2481:    *
2482:    * @param tabPlacement The JTabbedPane's tab placement.
2483:    *
2484:    * @return The tab run overlay.
2485:    */
2486:   protected int getTabRunOverlay(int tabPlacement)
2487:   {
2488:     return tabRunOverlay;
2489:   }
2490: 
2491:   /**
2492:    * This method returns the tab run indent. It is used in WRAP_TAB_LAYOUT and
2493:    * makes each tab run start indented by a certain amount.
2494:    *
2495:    * @param tabPlacement The JTabbedPane's tab placement.
2496:    * @param run The run to get indent for.
2497:    *
2498:    * @return The amount a run should be indented.
2499:    */
2500:   protected int getTabRunIndent(int tabPlacement, int run)
2501:   {
2502:     return 0;
2503:   }
2504: 
2505:   /**
2506:    * This method returns whether a tab run should be padded.
2507:    *
2508:    * @param tabPlacement The JTabbedPane's tab placement.
2509:    * @param run The run to check.
2510:    *
2511:    * @return Whether the given run should be padded.
2512:    */
2513:   protected boolean shouldPadTabRun(int tabPlacement, int run)
2514:   {
2515:     return true;
2516:   }
2517: 
2518:   /**
2519:    * This method returns whether the tab runs should be rotated.
2520:    *
2521:    * @param tabPlacement The JTabbedPane's tab placement.
2522:    *
2523:    * @return Whether runs should be rotated.
2524:    */
2525:   protected boolean shouldRotateTabRuns(int tabPlacement)
2526:   {
2527:     return true;
2528:   }
2529: 
2530:   /**
2531:    * This method returns an icon for the tab. If the tab is disabled, it
2532:    * should return the disabledIcon. If it is enabled, then it should return
2533:    * the default icon.
2534:    *
2535:    * @param tabIndex The tab index to get an icon for.
2536:    *
2537:    * @return The icon for the tab index.
2538:    */
2539:   protected Icon getIconForTab(int tabIndex)
2540:   {
2541:     if (tabPane.isEnabledAt(tabIndex))
2542:       return tabPane.getIconAt(tabIndex);
2543:     else
2544:       return tabPane.getDisabledIconAt(tabIndex);
2545:   }
2546: 
2547:   /**
2548:    * This method returns a view that can paint the text for the label.
2549:    *
2550:    * @param tabIndex The tab index to get a view for.
2551:    *
2552:    * @return The view for the tab index.
2553:    */
2554:   protected View getTextViewForTab(int tabIndex)
2555:   {
2556:     return null;
2557:   }
2558: 
2559:   /**
2560:    * This method returns the tab height, including insets, for the given index
2561:    * and fontheight.
2562:    *
2563:    * @param tabPlacement The JTabbedPane's tab placement.
2564:    * @param tabIndex The index of the tab to calculate.
2565:    * @param fontHeight The font height.
2566:    *
2567:    * @return This tab's height.
2568:    */
2569:   protected int calculateTabHeight(int tabPlacement, int tabIndex,
2570:                                    int fontHeight)
2571:   {
2572:     Icon icon = getIconForTab(tabIndex);
2573:     Insets insets = getTabInsets(tabPlacement, tabIndex);
2574: 
2575:     if (icon != null)
2576:       {
2577:     Rectangle vr = new Rectangle();
2578:     Rectangle ir = new Rectangle();
2579:     Rectangle tr = new Rectangle();
2580:     layoutLabel(tabPlacement, getFontMetrics(), tabIndex,
2581:                 tabPane.getTitleAt(tabIndex), icon, vr, ir, tr,
2582:                 tabIndex == tabPane.getSelectedIndex());
2583:     calcRect = tr.union(ir);
2584:       }
2585:     else
2586:       calcRect.height = fontHeight;
2587: 
2588:     calcRect.height += insets.top + insets.bottom;
2589:     return calcRect.height;
2590:   }
2591: 
2592:   /**
2593:    * This method returns the max tab height.
2594:    *
2595:    * @param tabPlacement The JTabbedPane's tab placement.
2596:    *
2597:    * @return The maximum tab height.
2598:    */
2599:   protected int calculateMaxTabHeight(int tabPlacement)
2600:   {
2601:     maxTabHeight = 0;
2602: 
2603:     FontMetrics fm = getFontMetrics();
2604:     int fontHeight = fm.getHeight();
2605: 
2606:     for (int i = 0; i < tabPane.getTabCount(); i++)
2607:       maxTabHeight = Math.max(calculateTabHeight(tabPlacement, i, fontHeight),
2608:                               maxTabHeight);
2609: 
2610:     return maxTabHeight;
2611:   }
2612: 
2613:   /**
2614:    * This method calculates the tab width, including insets, for the given tab
2615:    * index and font metrics.
2616:    *
2617:    * @param tabPlacement The JTabbedPane's tab placement.
2618:    * @param tabIndex The tab index to calculate for.
2619:    * @param metrics The font's metrics.
2620:    *
2621:    * @return The tab width for the given index.
2622:    */
2623:   protected int calculateTabWidth(int tabPlacement, int tabIndex,
2624:                                   FontMetrics metrics)
2625:   {
2626:     Icon icon = getIconForTab(tabIndex);
2627:     Insets insets = getTabInsets(tabPlacement, tabIndex);
2628: 
2629:     if (icon != null)
2630:       {
2631:     Rectangle vr = new Rectangle();
2632:     Rectangle ir = new Rectangle();
2633:     Rectangle tr = new Rectangle();
2634:     layoutLabel(tabPlacement, getFontMetrics(), tabIndex,
2635:                 tabPane.getTitleAt(tabIndex), icon, vr, ir, tr,
2636:                 tabIndex == tabPane.getSelectedIndex());
2637:     calcRect = tr.union(ir);
2638:       }
2639:     else
2640:       calcRect.width = metrics.stringWidth(tabPane.getTitleAt(tabIndex));
2641: 
2642:     calcRect.width += insets.left + insets.right;
2643:     return calcRect.width;
2644:   }
2645: 
2646:   /**
2647:    * This method calculates the max tab width.
2648:    *
2649:    * @param tabPlacement The JTabbedPane's tab placement.
2650:    *
2651:    * @return The maximum tab width.
2652:    */
2653:   protected int calculateMaxTabWidth(int tabPlacement)
2654:   {
2655:     maxTabWidth = 0;
2656: 
2657:     FontMetrics fm = getFontMetrics();
2658: 
2659:     for (int i = 0; i < tabPane.getTabCount(); i++)
2660:       maxTabWidth = Math.max(calculateTabWidth(tabPlacement, i, fm),
2661:                              maxTabWidth);
2662: 
2663:     return maxTabWidth;
2664:   }
2665: 
2666:   /**
2667:    * This method calculates the tab area height, including insets, for the
2668:    * given amount of runs and tab height.
2669:    *
2670:    * @param tabPlacement The JTabbedPane's tab placement.
2671:    * @param horizRunCount The number of runs.
2672:    * @param maxTabHeight The max tab height.
2673:    *
2674:    * @return The tab area height.
2675:    */
2676:   protected int calculateTabAreaHeight(int tabPlacement, int horizRunCount,
2677:                                        int maxTabHeight)
2678:   {
2679:     Insets insets = getTabAreaInsets(tabPlacement);
2680:     int tabAreaHeight = horizRunCount * maxTabHeight
2681:                         - (horizRunCount - 1) * tabRunOverlay;
2682: 
2683:     tabAreaHeight += insets.top + insets.bottom;
2684: 
2685:     return tabAreaHeight;
2686:   }
2687: 
2688:   /**
2689:    * This method calculates the tab area width, including insets, for the
2690:    * given amount of runs and tab width.
2691:    *
2692:    * @param tabPlacement The JTabbedPane's tab placement.
2693:    * @param vertRunCount The number of runs.
2694:    * @param maxTabWidth The max tab width.
2695:    *
2696:    * @return The tab area width.
2697:    */
2698:   protected int calculateTabAreaWidth(int tabPlacement, int vertRunCount,
2699:                                       int maxTabWidth)
2700:   {
2701:     Insets insets = getTabAreaInsets(tabPlacement);
2702:     int tabAreaWidth = vertRunCount * maxTabWidth
2703:                        - (vertRunCount - 1) * tabRunOverlay;
2704: 
2705:     tabAreaWidth += insets.left + insets.right;
2706: 
2707:     return tabAreaWidth;
2708:   }
2709: 
2710:   /**
2711:    * This method returns the tab insets appropriately rotated.
2712:    *
2713:    * @param tabPlacement The JTabbedPane's tab placement.
2714:    * @param tabIndex The tab index.
2715:    *
2716:    * @return The tab insets for the given index.
2717:    */
2718:   protected Insets getTabInsets(int tabPlacement, int tabIndex)
2719:   {
2720:     Insets target = new Insets(0, 0, 0, 0);
2721:     rotateInsets(tabInsets, target, tabPlacement);
2722:     return target;
2723:   }
2724: 
2725:   /**
2726:    * This method returns the selected tab pad insets appropriately rotated.
2727:    *
2728:    * @param tabPlacement The JTabbedPane's tab placement.
2729:    *
2730:    * @return The selected tab pad insets.
2731:    */
2732:   protected Insets getSelectedTabPadInsets(int tabPlacement)
2733:   {
2734:     Insets target = new Insets(0, 0, 0, 0);
2735:     rotateInsets(selectedTabPadInsets, target, tabPlacement);
2736:     return target;
2737:   }
2738: 
2739:   /**
2740:    * This method returns the tab area insets appropriately rotated.
2741:    *
2742:    * @param tabPlacement The JTabbedPane's tab placement.
2743:    *
2744:    * @return The tab area insets.
2745:    */
2746:   protected Insets getTabAreaInsets(int tabPlacement)
2747:   {
2748:     Insets target = new Insets(0, 0, 0, 0);
2749:     rotateInsets(tabAreaInsets, target, tabPlacement);
2750:     return target;
2751:   }
2752: 
2753:   /**
2754:    * This method returns the content border insets appropriately rotated.
2755:    *
2756:    * @param tabPlacement The JTabbedPane's tab placement.
2757:    *
2758:    * @return The content border insets.
2759:    */
2760:   protected Insets getContentBorderInsets(int tabPlacement)
2761:   {
2762:     Insets target = new Insets(0, 0, 0, 0);
2763:     rotateInsets(contentBorderInsets, target, tabPlacement);
2764:     return target;
2765:   }
2766: 
2767:   /**
2768:    * This method returns the fontmetrics for the font of the JTabbedPane.
2769:    *
2770:    * @return The font metrics for the JTabbedPane.
2771:    */
2772:   protected FontMetrics getFontMetrics()
2773:   {
2774:     FontMetrics fm = tabPane.getToolkit().getFontMetrics(tabPane.getFont());
2775:     return fm;
2776:   }
2777: 
2778:   /**
2779:    * This method navigates from the selected tab into the given direction. As
2780:    * a result, a new tab will be selected (if possible).
2781:    *
2782:    * @param direction The direction to navigate in.
2783:    */
2784:   protected void navigateSelectedTab(int direction)
2785:   {
2786:     int tabPlacement = tabPane.getTabPlacement();
2787:     if (tabPlacement == SwingConstants.TOP
2788:         || tabPlacement == SwingConstants.BOTTOM)
2789:       {
2790:     if (direction == SwingConstants.WEST)
2791:       selectPreviousTabInRun(tabPane.getSelectedIndex());
2792:     else if (direction == SwingConstants.EAST)
2793:       selectNextTabInRun(tabPane.getSelectedIndex());
2794: 
2795:     else
2796:       {
2797:         int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(),
2798:                                      tabPane.getSelectedIndex(),
2799:                                      (tabPlacement == SwingConstants.RIGHT)
2800:                                      ? true : false);
2801:         selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(),
2802:                              offset);
2803:       }
2804:       }
2805:     if (tabPlacement == SwingConstants.LEFT
2806:         || tabPlacement == SwingConstants.RIGHT)
2807:       {
2808:     if (direction == SwingConstants.NORTH)
2809:       selectPreviousTabInRun(tabPane.getSelectedIndex());
2810:     else if (direction == SwingConstants.SOUTH)
2811:       selectNextTabInRun(tabPane.getSelectedIndex());
2812:     else
2813:       {
2814:         int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(),
2815:                                      tabPane.getSelectedIndex(),
2816:                                      (tabPlacement == SwingConstants.RIGHT)
2817:                                      ? true : false);
2818:         selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(),
2819:                              offset);
2820:       }
2821:       }
2822:   }
2823: 
2824:   /**
2825:    * This method selects the next tab in the run.
2826:    *
2827:    * @param current The current selected index.
2828:    */
2829:   protected void selectNextTabInRun(int current)
2830:   {
2831:     tabPane.setSelectedIndex(getNextTabIndexInRun(tabPane.getTabCount(),
2832:                                                   current));
2833:   }
2834: 
2835:   /**
2836:    * This method selects the previous tab in the run.
2837:    *
2838:    * @param current The current selected index.
2839:    */
2840:   protected void selectPreviousTabInRun(int current)
2841:   {
2842:     tabPane.setSelectedIndex(getPreviousTabIndexInRun(tabPane.getTabCount(),
2843:                                                       current));
2844:   }
2845: 
2846:   /**
2847:    * This method selects the next tab (regardless of runs).
2848:    *
2849:    * @param current The current selected index.
2850:    */
2851:   protected void selectNextTab(int current)
2852:   {
2853:     tabPane.setSelectedIndex(getNextTabIndex(current));
2854:   }
2855: 
2856:   /**
2857:    * This method selects the previous tab (regardless of runs).
2858:    *
2859:    * @param current The current selected index.
2860:    */
2861:   protected void selectPreviousTab(int current)
2862:   {
2863:     tabPane.setSelectedIndex(getPreviousTabIndex(current));
2864:   }
2865: 
2866:   /**
2867:    * This method selects the correct tab given an offset from the current tab
2868:    * index. If the tab placement is TOP or BOTTOM, the offset will be in the
2869:    * y direction, otherwise, it will be in the x direction. A new coordinate
2870:    * will be found by adding the offset to the current location of the tab.
2871:    * The tab that the new location will be selected.
2872:    *
2873:    * @param tabPlacement The JTabbedPane's tab placement.
2874:    * @param tabIndex The tab to start from.
2875:    * @param offset The coordinate offset.
2876:    */
2877:   protected void selectAdjacentRunTab(int tabPlacement, int tabIndex,
2878:                                       int offset)
2879:   {
2880:     int x = rects[tabIndex].x + rects[tabIndex].width / 2;
2881:     int y = rects[tabIndex].y + rects[tabIndex].height / 2;
2882: 
2883:     switch (tabPlacement)
2884:       {
2885:       case SwingConstants.TOP:
2886:       case SwingConstants.BOTTOM:
2887:     y += offset;
2888:     break;
2889:       case SwingConstants.RIGHT:
2890:       case SwingConstants.LEFT:
2891:     x += offset;
2892:     break;
2893:       }
2894: 
2895:     int index = tabForCoordinate(tabPane, x, y);
2896:     if (index != -1)
2897:       tabPane.setSelectedIndex(index);
2898:   }
2899: 
2900:   // This method is called when you press up/down to cycle through tab runs.
2901:   // it returns the distance (between the two runs' x/y position.
2902:   // where one run is the current selected run and the other run is the run in the
2903:   // direction of the scroll (dictated by the forward flag)
2904:   // the offset is an absolute value of the difference
2905: 
2906:   /**
2907:    * This method calculates the offset distance for use in
2908:    * selectAdjacentRunTab. The offset returned will be a difference in the y
2909:    * coordinate between the run in  the desired direction and the current run
2910:    * (for tabPlacement in TOP or BOTTOM). Use x coordinate for LEFT and
2911:    * RIGHT.
2912:    *
2913:    * @param tabPlacement The JTabbedPane's tab placement.
2914:    * @param tabCount The number of tabs.
2915:    * @param tabIndex The starting index.
2916:    * @param forward If forward, the run in the desired direction will be the
2917:    *        next run.
2918:    *
2919:    * @return The offset between the two runs.
2920:    */
2921:   protected int getTabRunOffset(int tabPlacement, int tabCount, int tabIndex,
2922:                                 boolean forward)
2923:   {
2924:     int currRun = getRunForTab(tabCount, tabIndex);
2925:     int offset;
2926:     int nextRun = (forward) ? getNextTabRun(currRun) : getPreviousTabRun(currRun);
2927:     if (tabPlacement == SwingConstants.TOP
2928:         || tabPlacement == SwingConstants.BOTTOM)
2929:       offset = rects[lastTabInRun(tabCount, nextRun)].y
2930:                - rects[lastTabInRun(tabCount, currRun)].y;
2931:     else
2932:       offset = rects[lastTabInRun(tabCount, nextRun)].x
2933:                - rects[lastTabInRun(tabCount, currRun)].x;
2934:     return offset;
2935:   }
2936: 
2937:   /**
2938:    * This method returns the previous tab index.
2939:    *
2940:    * @param base The index to start from.
2941:    *
2942:    * @return The previous tab index.
2943:    */
2944:   protected int getPreviousTabIndex(int base)
2945:   {
2946:     base--;
2947:     if (base < 0)
2948:       return tabPane.getTabCount() - 1;
2949:     return base;
2950:   }
2951: 
2952:   /**
2953:    * This method returns the next tab index.
2954:    *
2955:    * @param base The index to start from.
2956:    *
2957:    * @return The next tab index.
2958:    */
2959:   protected int getNextTabIndex(int base)
2960:   {
2961:     base++;
2962:     if (base == tabPane.getTabCount())
2963:       return 0;
2964:     return base;
2965:   }
2966: 
2967:   /**
2968:    * This method returns the next tab index in the run. If the next index is
2969:    * out of this run, it will return the starting tab index for the run.
2970:    *
2971:    * @param tabCount The number of tabs.
2972:    * @param base The index to start from.
2973:    *
2974:    * @return The next tab index in the run.
2975:    */
2976:   protected int getNextTabIndexInRun(int tabCount, int base)
2977:   {
2978:     int index = getNextTabIndex(base);
2979:     int run = getRunForTab(tabCount, base);
2980:     if (index == lastTabInRun(tabCount, run) + 1)
2981:       index = lastTabInRun(tabCount, getPreviousTabRun(run)) + 1;
2982:     return getNextTabIndex(base);
2983:   }
2984: 
2985:   /**
2986:    * This method returns the previous tab index in the run. If the previous
2987:    * index is out of this run, it will return the last index for the run.
2988:    *
2989:    * @param tabCount The number of tabs.
2990:    * @param base The index to start from.
2991:    *
2992:    * @return The previous tab index in the run.
2993:    */
2994:   protected int getPreviousTabIndexInRun(int tabCount, int base)
2995:   {
2996:     int index = getPreviousTabIndex(base);
2997:     int run = getRunForTab(tabCount, base);
2998:     if (index == lastTabInRun(tabCount, getPreviousTabRun(run)))
2999:       index = lastTabInRun(tabCount, run);
3000:     return getPreviousTabIndex(base);
3001:   }
3002: 
3003:   /**
3004:    * This method returns the index of the previous run.
3005:    *
3006:    * @param baseRun The run to start from.
3007:    *
3008:    * @return The index of the previous run.
3009:    */
3010:   protected int getPreviousTabRun(int baseRun)
3011:   {
3012:     if (getTabRunCount(tabPane) == 1)
3013:       return 1;
3014: 
3015:     int prevRun = --baseRun;
3016:     if (prevRun < 0)
3017:       prevRun = getTabRunCount(tabPane) - 1;
3018:     return prevRun;
3019:   }
3020: 
3021:   /**
3022:    * This method returns the index of the next run.
3023:    *
3024:    * @param baseRun The run to start from.
3025:    *
3026:    * @return The index of the next run.
3027:    */
3028:   protected int getNextTabRun(int baseRun)
3029:   {
3030:     if (getTabRunCount(tabPane) == 1)
3031:       return 1;
3032: 
3033:     int nextRun = ++baseRun;
3034:     if (nextRun == getTabRunCount(tabPane))
3035:       nextRun = 0;
3036:     return nextRun;
3037:   }
3038: 
3039:   /**
3040:    * This method rotates the insets given a direction to rotate them in.
3041:    * Target placement should be one of TOP, LEFT, BOTTOM, RIGHT. The  rotated
3042:    * insets will be stored in targetInsets. Passing in TOP as  the direction
3043:    * does nothing. Passing in LEFT switches top and left, right and bottom.
3044:    * Passing in BOTTOM switches top and bottom. Passing in RIGHT switches top
3045:    * for left, left for bottom, bottom for right, and right for top.
3046:    *
3047:    * @param topInsets The reference insets.
3048:    * @param targetInsets An Insets object to store the new insets.
3049:    * @param targetPlacement The rotation direction.
3050:    */
3051:   protected static void rotateInsets(Insets topInsets, Insets targetInsets,
3052:                                      int targetPlacement)
3053:   {
3054:     // Sun's version will happily throw an NPE if params are null,
3055:     // so I won't check it either.
3056:     switch (targetPlacement)
3057:       {
3058:       case SwingConstants.TOP:
3059:     targetInsets.top = topInsets.top;
3060:     targetInsets.left = topInsets.left;
3061:     targetInsets.right = topInsets.right;
3062:     targetInsets.bottom = topInsets.bottom;
3063:     break;
3064:       case SwingConstants.LEFT:
3065:     targetInsets.left = topInsets.top;
3066:     targetInsets.top = topInsets.left;
3067:     targetInsets.right = topInsets.bottom;
3068:     targetInsets.bottom = topInsets.right;
3069:     break;
3070:       case SwingConstants.BOTTOM:
3071:     targetInsets.top = topInsets.bottom;
3072:     targetInsets.bottom = topInsets.top;
3073:     targetInsets.left = topInsets.left;
3074:     targetInsets.right = topInsets.right;
3075:     break;
3076:       case SwingConstants.RIGHT:
3077:     targetInsets.top = topInsets.left;
3078:     targetInsets.left = topInsets.bottom;
3079:     targetInsets.bottom = topInsets.right;
3080:     targetInsets.right = topInsets.top;
3081:     break;
3082:       }
3083:   }
3084: }