Source for javax.swing.plaf.metal.MetalTabbedPaneUI

   1: /* MetalTabbedPaneUI.java
   2:    Copyright (C) 2005, 2006 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.swing.plaf.metal;
  40: 
  41: import java.awt.Color;
  42: import java.awt.Graphics;
  43: import java.awt.LayoutManager;
  44: import java.awt.Rectangle;
  45: 
  46: import javax.swing.JComponent;
  47: import javax.swing.JTabbedPane;
  48: import javax.swing.UIManager;
  49: import javax.swing.plaf.ComponentUI;
  50: import javax.swing.plaf.UIResource;
  51: import javax.swing.plaf.basic.BasicTabbedPaneUI;
  52: 
  53: /**
  54:  * A UI delegate for the {@link JTabbedPane} component.
  55:  */
  56: public class MetalTabbedPaneUI extends BasicTabbedPaneUI
  57: {
  58: 
  59:   /**
  60:    * A {@link LayoutManager} responsible for placing all the tabs and the 
  61:    * visible component inside the {@link JTabbedPane}. This class is only used 
  62:    * for {@link JTabbedPane#WRAP_TAB_LAYOUT}.
  63:    *
  64:    * @specnote Apparently this class was intended to be protected,
  65:    *           but was made public by a compiler bug and is now
  66:    *           public for compatibility.
  67:    */
  68:   public class TabbedPaneLayout 
  69:     extends BasicTabbedPaneUI.TabbedPaneLayout
  70:   {
  71:     /**
  72:      * Creates a new instance of the layout manager.
  73:      */
  74:     public TabbedPaneLayout()
  75:     {
  76:       // Nothing to do here.
  77:     }
  78:     
  79:     /**
  80:      * Overridden to do nothing, because tab runs are not rotated in the 
  81:      * {@link MetalLookAndFeel}.
  82:      * 
  83:      * @param tabPlacement  the tab placement (one of {@link #TOP}, 
  84:      *        {@link #BOTTOM}, {@link #LEFT} or {@link #RIGHT}).
  85:      * @param selectedRun  the index of the selected run.
  86:      */
  87:     protected void rotateTabRuns(int tabPlacement, int selectedRun)
  88:     {
  89:       // do nothing, because tab runs are not rotated in the MetalLookAndFeel
  90:     }
  91:     
  92:     /**
  93:      * Overridden to do nothing, because the selected tab does not have extra
  94:      * padding in the {@link MetalLookAndFeel}.
  95:      * 
  96:      * @param tabPlacement  the tab placement (one of {@link #TOP}, 
  97:      *        {@link #BOTTOM}, {@link #LEFT} or {@link #RIGHT}).
  98:      * @param selectedIndex  the index of the selected tab.
  99:      */
 100:     protected void padSelectedTab(int tabPlacement, int selectedIndex)
 101:     {
 102:       // do nothing, because the selected tab does not have extra padding in 
 103:       // the MetalLookAndFeel
 104:     }
 105: 
 106:     /**
 107:      * Overridden because tab runs are only normalized for TOP and BOTTOM
 108:      * tab placement in the Metal L&F.
 109:      */
 110:     protected void normalizeTabRuns(int tabPlacement, int tabCount, int start,
 111:                                     int max)
 112:     {
 113:       if (tabPlacement == TOP || tabPlacement == BOTTOM)
 114:         super.normalizeTabRuns(tabPlacement, tabCount, start, max);
 115:     }
 116:   }
 117: 
 118:   /**
 119:    * The minimum tab width.
 120:    */
 121:   protected int minTabWidth;
 122: 
 123:   /**
 124:    * The color for the selected tab.
 125:    */
 126:   protected Color selectColor;
 127: 
 128:   /**
 129:    * The color for a highlighted selected tab.
 130:    */
 131:   protected Color selectHighlight;
 132: 
 133:   /**
 134:    * The background color used for the tab area.
 135:    */
 136:   protected Color tabAreaBackground;
 137: 
 138:   /** The graphics to draw the highlight below the tab. */
 139:   private Graphics hg;
 140: 
 141:   /**
 142:    * Indicates if the tabs are having their background filled.
 143:    */
 144:   private boolean tabsOpaque;
 145: 
 146:   /**
 147:    * Constructs a new instance of MetalTabbedPaneUI.
 148:    */
 149:   public MetalTabbedPaneUI()
 150:   {
 151:     super();
 152:   }
 153: 
 154:   /**
 155:    * Returns an instance of MetalTabbedPaneUI.
 156:    *
 157:    * @param component the component for which we return an UI instance
 158:    *
 159:    * @return an instance of MetalTabbedPaneUI
 160:    */
 161:   public static ComponentUI createUI(JComponent component)
 162:   {
 163:     return new MetalTabbedPaneUI();
 164:   }
 165:   
 166:   /**
 167:    * Creates and returns an instance of {@link TabbedPaneLayout}.
 168:    * 
 169:    * @return A layout manager used by this UI delegate.
 170:    */
 171:   protected LayoutManager createLayoutManager()
 172:   {
 173:     return (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT)
 174:            ? new MetalTabbedPaneUI.TabbedPaneLayout()
 175:            : super.createLayoutManager();
 176:   }
 177:   
 178:   /**
 179:    * Paints the border for a single tab.
 180:    * 
 181:    * @param g  the graphics device.
 182:    * @param tabPlacement  the tab placement ({@link #TOP}, {@link #LEFT}, 
 183:    *        {@link #BOTTOM} or {@link #RIGHT}).
 184:    * @param tabIndex  the index of the tab to draw the border for.
 185:    * @param x  the x-coordinate for the tab's bounding rectangle.
 186:    * @param y  the y-coordinate for the tab's bounding rectangle.
 187:    * @param w  the width for the tab's bounding rectangle.
 188:    * @param h  the height for the tab's bounding rectangle.
 189:    * @param isSelected  indicates whether or not the tab is selected.
 190:    */
 191:   protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex, 
 192:           int x, int y, int w, int h, boolean isSelected) 
 193:   {
 194:     int bottom = y + h - 1;
 195:     int right = x + w - 1;
 196: 
 197:     switch (tabPlacement)
 198:     {
 199:     case LEFT: 
 200:       paintLeftTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
 201:       break;
 202:     case BOTTOM:
 203:       paintBottomTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
 204:       break;
 205:     case  RIGHT:
 206:       paintRightTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
 207:       break;
 208:     case TOP:
 209:     default: 
 210:       paintTopTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
 211:     }
 212:   }
 213: 
 214:   /**
 215:    * Paints the border for a tab assuming that the tab position is at the top
 216:    * ({@link #TOP}).
 217:    * 
 218:    * @param tabIndex  the tab index.
 219:    * @param g  the graphics device.
 220:    * @param x  the x-coordinate for the tab's bounding rectangle.
 221:    * @param y  the y-coordinate for the tab's bounding rectangle.
 222:    * @param w  the width for the tab's bounding rectangle.
 223:    * @param h  the height for the tab's bounding rectangle.
 224:    * @param btm  the y coordinate of the bottom border
 225:    * @param rght the x coordinate of the right border
 226:    * @param isSelected  indicates whether the tab is selected.
 227:    */
 228:   protected void paintTopTabBorder(int tabIndex, Graphics g, int x, int y,
 229:       int w, int h, int btm, int rght, boolean isSelected)
 230:   {
 231:     int tabCount = tabPane.getTabCount();
 232:     int currentRun = getRunForTab(tabCount, tabIndex);
 233:     int right = w - 1;
 234:     int bottom = h - 1;
 235: 
 236:     // Paint gap.
 237:     if (shouldFillGap(currentRun, tabIndex, x, y))
 238:       {
 239:         g.translate(x, y);
 240:         g.setColor(getColorForGap(currentRun, x, y + 1));
 241:         g.fillRect(1, 0, 5, 3);
 242:         g.fillRect(1, 3, 2, 2);
 243:         g.translate(-x, -y);
 244:       }
 245: 
 246:     g.translate(x, y);
 247: 
 248:     boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
 249:     Color oceanSelectedBorder =
 250:       UIManager.getColor("TabbedPane.borderHightlightColor");
 251:     if (isOcean && isSelected)
 252:       g.setColor(oceanSelectedBorder);
 253:     else
 254:       g.setColor(darkShadow);
 255: 
 256:     // Slant
 257:     g.drawLine(1, 5, 6, 0);
 258:     // Top.
 259:     g.drawLine(6, 0, right, 0);
 260:     // Right.
 261:     int lastIndex = lastTabInRun(tabCount, currentRun);
 262:     if (tabIndex == lastIndex)
 263:       g.drawLine(right, 1, right, bottom);
 264:     // Left.
 265:     int selectedIndex = tabPane.getSelectedIndex();
 266:     if (isOcean && tabIndex - 1 == selectedIndex
 267:         && currentRun == getRunForTab(tabCount, selectedIndex))
 268:       {
 269:         g.setColor(oceanSelectedBorder);
 270:       }
 271:     if (tabIndex != tabRuns[runCount - 1])
 272:       {
 273:         if (isOcean && isSelected)
 274:           {
 275:             g.drawLine(0, 6, 0, bottom);
 276:             g.setColor(darkShadow);
 277:             g.drawLine(0, 0, 0, 5);
 278:           }
 279:         else
 280:           {
 281:             g.drawLine(0, 0, 0, bottom);
 282:           }
 283:       }
 284:     else
 285:       {
 286:         g.drawLine(0, 6, 0, bottom);
 287:       }
 288: 
 289:     // Paint the highlight.
 290:     g.setColor(isSelected ? selectHighlight : highlight);
 291:     // Slant.
 292:     g.drawLine(1, 6, 6, 1);
 293:     // Top.
 294:     g.drawLine(6, 1, right, 1);
 295:     // Left.
 296:     g.drawLine(1, 6, 1, bottom);
 297:     int firstIndex = tabRuns[currentRun];
 298:     if (tabIndex == firstIndex && tabIndex != tabRuns[runCount - 1])
 299:       {
 300:         if (tabPane.getSelectedIndex() == tabRuns[currentRun + 1])
 301:           g.setColor(selectHighlight);
 302:         else
 303:           g.setColor(highlight);
 304:         g.drawLine(1, 0, 1, 4);
 305:       }
 306: 
 307:     g.translate(-x, -y);
 308:   }
 309:   
 310:   /**
 311:    * Paints the border for a tab assuming that the tab position is at the left
 312:    * ({@link #LEFT}).
 313:    * 
 314:    * @param tabIndex  the tab index.
 315:    * @param g  the graphics device.
 316:    * @param x  the x-coordinate for the tab's bounding rectangle.
 317:    * @param y  the y-coordinate for the tab's bounding rectangle.
 318:    * @param w  the width for the tab's bounding rectangle.
 319:    * @param h  the height for the tab's bounding rectangle.
 320:    * @param btm  ???
 321:    * @param rght  ???
 322:    * @param isSelected  indicates whether the tab is selected.
 323:    */
 324:   protected void paintLeftTabBorder(int tabIndex, Graphics g, int x, int y,
 325:       int w, int h, int btm, int rght, boolean isSelected)
 326:   {
 327:     g.translate(x, y);
 328:     int bottom = h - 1;
 329:     int right = w - 1;
 330: 
 331:     int tabCount = tabPane.getTabCount();
 332:     int currentRun = getRunForTab(tabCount, tabIndex);
 333:     int firstIndex = tabRuns[currentRun];
 334: 
 335:     // Paint the part of the above tab.
 336:     if (tabIndex != firstIndex && tabIndex > 0 && tabsOpaque)
 337:       {
 338:         Color c;
 339:         if (tabPane.getSelectedIndex() == tabIndex - 1)
 340:           c = selectColor;
 341:         else
 342:           c = getUnselectedBackground(tabIndex - 1);
 343:         g.setColor(c);
 344:         g.fillRect(2, 0, 4, 3);
 345:         g.drawLine(2, 3, 2, 3);
 346:       }
 347: 
 348:     // Paint the highlight.
 349:     boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
 350:     if (isOcean)
 351:       {
 352:         g.setColor(isSelected ? selectHighlight : MetalLookAndFeel.getWhite());
 353:       }
 354:     else
 355:       {
 356:         g.setColor(isSelected ? selectHighlight : highlight);
 357:       }
 358:     // Slant.
 359:     g.drawLine(1, 6, 6, 1);
 360:     // Left.
 361:     g.drawLine(1, 6, 1, bottom);
 362:     // Top.
 363:     g.drawLine(6, 1, right, 1);
 364:     if (tabIndex != firstIndex)
 365:       {
 366:         if (isOcean)
 367:           {
 368:             g.setColor(MetalLookAndFeel.getWhite());
 369:           }
 370:         g.drawLine(1, 0, 1, 4);
 371:       }
 372: 
 373:     // Paint border.
 374:     Color oceanSelectedBorder =
 375:       UIManager.getColor("TabbedPane.borderHightlightColor");
 376:     if (isOcean && isSelected)
 377:       {
 378:         g.setColor(oceanSelectedBorder);
 379:       }
 380:     else
 381:       {
 382:         g.setColor(darkShadow);
 383:       }
 384: 
 385:     // Slant.
 386:     g.drawLine(1, 5, 6, 0);
 387:     // Top.
 388:     g.drawLine(6, 0, right, 0);
 389:     // Bottom.
 390:     int lastIndex = lastTabInRun(tabCount, currentRun);
 391:     if (tabIndex == lastIndex)
 392:       {
 393:         g.drawLine(0, bottom, right, bottom);
 394:       }
 395:     // Left.
 396:     if (isOcean)
 397:       {
 398:         if (tabPane.getSelectedIndex() == tabIndex - 1)
 399:           {
 400:             g.drawLine(0, 6, 0, bottom);
 401:             if (tabIndex != firstIndex)
 402:               {
 403:                 g.setColor(oceanSelectedBorder);
 404:                 g.drawLine(0, 0, 0, 5);
 405:               }
 406:           }
 407:         else if (isSelected)
 408:           {
 409:             g.drawLine(0, 5, 0, bottom);
 410:             if (tabIndex != firstIndex)
 411:               {
 412:                 g.setColor(darkShadow);
 413:                 g.drawLine(0, 0, 0, 5);
 414:               }
 415:           }
 416:         else if (tabIndex != firstIndex)
 417:           {
 418:             g.drawLine(0, 0, 0, bottom);
 419:           }
 420:         else
 421:           {
 422:             g.drawLine(0, 6, 0, bottom);
 423:           }
 424:       }
 425:     else
 426:       {
 427:         if (tabIndex != firstIndex)
 428:           {
 429:             g.drawLine(0, 0, 0, bottom);
 430:           }
 431:         else
 432:           {
 433:             g.drawLine(0, 6, 0, bottom);
 434:           }
 435:       }
 436: 
 437:     g.translate(-x, -y);
 438:   }
 439:   
 440:   /**
 441:    * Paints the border for a tab assuming that the tab position is at the right
 442:    * ({@link #RIGHT}).
 443:    * 
 444:    * @param tabIndex  the tab index.
 445:    * @param g  the graphics device.
 446:    * @param x  the x-coordinate for the tab's bounding rectangle.
 447:    * @param y  the y-coordinate for the tab's bounding rectangle.
 448:    * @param w  the width for the tab's bounding rectangle.
 449:    * @param h  the height for the tab's bounding rectangle.
 450:    * @param btm  ???
 451:    * @param rght  ???
 452:    * @param isSelected  indicates whether the tab is selected.
 453:    */
 454:   protected void paintRightTabBorder(int tabIndex, Graphics g, int x, int y,
 455:       int w, int h, int btm, int rght, boolean isSelected)
 456:   {
 457:     g.translate(x, y);
 458:     int bottom = h - 1;
 459:     int right = w - 1;
 460: 
 461:     int tabCount = tabPane.getTabCount();
 462:     int currentRun = getRunForTab(tabCount, tabIndex);
 463:     int firstIndex = tabRuns[currentRun];
 464: 
 465:     // Paint part of the above tab.
 466:     if (tabIndex != firstIndex && tabIndex > 0 && tabsOpaque)
 467:       {
 468:         Color c;
 469:         if (tabPane.getSelectedIndex() == tabIndex - 1)
 470:           c = selectColor;
 471:         else
 472:           c = getUnselectedBackground(tabIndex - 1);
 473:         g.setColor(c);
 474:         g.fillRect(right - 5, 0, 5, 3);
 475:         g.fillRect(right - 2, 3, 2, 2);
 476:       }
 477: 
 478:     // Paint highlight.
 479:     g.setColor(isSelected ? selectHighlight : highlight);
 480: 
 481:     // Slant.
 482:     g.drawLine(right - 6, 1, right - 1, 6);
 483:     // Top.
 484:     g.drawLine(0, 1, right - 6, 1);
 485:     // Left.
 486:     if (! isSelected)
 487:       {
 488:         g.drawLine(0, 1, 0, bottom);
 489:       }
 490: 
 491:     // Paint border.
 492:     boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
 493:     Color oceanSelectedBorder =
 494:       UIManager.getColor("TabbedPane.borderHightlightColor");
 495:     if (isOcean && isSelected)
 496:       {
 497:         g.setColor(oceanSelectedBorder);
 498:       }
 499:     else
 500:       {
 501:         g.setColor(darkShadow);
 502:       }
 503: 
 504:     // Bottom.
 505:     int lastIndex = lastTabInRun(tabCount, currentRun);
 506:     if (tabIndex == lastIndex)
 507:       {
 508:         g.drawLine(0, bottom, right, bottom);
 509:       }
 510:     // Slant.
 511:     if (isOcean && tabPane.getSelectedIndex() == tabIndex - 1)
 512:       {
 513:         g.setColor(oceanSelectedBorder);
 514:       }
 515:     g.drawLine(right - 6, 0, right, 6);
 516:     // Top.
 517:     g.drawLine(0, 0, right - 6, 0);
 518:     // Right.
 519:     if (isOcean && isSelected)
 520:       {
 521:         g.drawLine(right, 6, right, bottom);
 522:         if (tabIndex != firstIndex)
 523:           {
 524:             g.setColor(darkShadow);
 525:             g.drawLine(right, 0, right, 5);
 526:           }
 527:       }
 528:     else if (isOcean && tabPane.getSelectedIndex() == tabIndex - 1)
 529:       {
 530:         if (tabIndex != firstIndex)
 531:           {
 532:             g.setColor(oceanSelectedBorder);
 533:             g.drawLine(right, 0, right, 6);
 534:           }
 535:         g.setColor(darkShadow);
 536:         g.drawLine(right, 7, right, bottom);
 537:       }
 538:     else if (tabIndex != firstIndex)
 539:       {
 540:         g.drawLine(right, 0, right, bottom);
 541:       }
 542:     else
 543:       {
 544:         g.drawLine(right, 6, right, bottom);
 545:       }
 546:     g.translate(-x, -y);
 547:   }
 548:   
 549:   /**
 550:    * Paints the border for a tab assuming that the tab position is at the bottom
 551:    * ({@link #BOTTOM}).
 552:    * 
 553:    * @param tabIndex  the tab index.
 554:    * @param g  the graphics device.
 555:    * @param x  the x-coordinate for the tab's bounding rectangle.
 556:    * @param y  the y-coordinate for the tab's bounding rectangle.
 557:    * @param w  the width for the tab's bounding rectangle.
 558:    * @param h  the height for the tab's bounding rectangle.
 559:    * @param btm  ???
 560:    * @param rght  ???
 561:    * @param isSelected  indicates whether the tab is selected.
 562:    */
 563:   protected void paintBottomTabBorder(int tabIndex, Graphics g, int x, int y,
 564:       int w, int h, int btm, int rght, boolean isSelected)
 565:   {
 566:     int bottom = h - 1;
 567:     int right = w - 1;
 568: 
 569:     int tabCount = tabPane.getTabCount();
 570:     int currentRun = getRunForTab(tabCount, tabIndex);
 571:     // Paint gap if necessary.
 572:     if (shouldFillGap(currentRun, tabIndex, x, y))
 573:       {
 574:         g.translate(x, y);
 575:         g.setColor(getColorForGap(currentRun, x, y));
 576:         g.fillRect(1, bottom - 4, 3, 5);
 577:         g.fillRect(4, bottom - 1, 2, 2);
 578:         g.translate(-x, -y);
 579:       }
 580: 
 581:     g.translate(x, y);
 582: 
 583:     // Paint border.
 584:     boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
 585:     Color oceanSelectedBorder =
 586:       UIManager.getColor("TabbedPane.borderHightlightColor");
 587:     if (isOcean && isSelected)
 588:       {
 589:         g.setColor(oceanSelectedBorder);
 590:       }
 591:     else
 592:       {
 593:         g.setColor(darkShadow);
 594:       }
 595:     // Slant.
 596:     g.drawLine(1, bottom - 5, 6, bottom);
 597:     // Bottom.
 598:     g.drawLine(6, bottom, right, bottom);
 599:     // Right.
 600:     int lastIndex = lastTabInRun(tabCount, currentRun);
 601:     if (tabIndex == lastIndex)
 602:       {
 603:         g.drawLine(right, 0, right, bottom);
 604:       }
 605:     // Left.
 606:     if (isOcean && isSelected)
 607:       {
 608:         g.drawLine(0, 0, 0, bottom - 5);
 609:         
 610:         // Paint a connecting line to the tab below for all
 611:         // but the first tab in the last run.
 612:         if (tabIndex != tabRuns[runCount-1])
 613:           {
 614:             g.setColor(darkShadow);
 615:             g.drawLine(0, bottom - 5, 0, bottom);
 616:           }
 617:       }
 618:     else
 619:       {
 620:         if (isOcean && tabIndex == tabPane.getSelectedIndex() + 1)
 621:           {
 622:             g.setColor(oceanSelectedBorder);
 623:           }
 624:         if (tabIndex != tabRuns[runCount - 1])
 625:           {
 626:             g.drawLine(0, 0, 0, bottom);
 627:           }
 628:         else
 629:           {
 630:             g.drawLine(0, 0, 0, bottom - 6);
 631:           }
 632:       }
 633: 
 634:     // Paint highlight.
 635:     g.setColor(isSelected ? selectHighlight : highlight);
 636:     // Slant.
 637:     g.drawLine(1, bottom - 6, 6, bottom - 1);
 638:     // Left.
 639:     g.drawLine(1, 0, 1, bottom - 6);
 640: 
 641:     int firstIndex = tabRuns[currentRun];
 642:     if (tabIndex == firstIndex && tabIndex != tabRuns[runCount - 1])
 643:       {
 644:         if (tabPane.getSelectedIndex() == tabRuns[currentRun + 1])
 645:           {
 646:             g.setColor(selectHighlight);
 647:           }
 648:         else
 649:           {
 650:             g.setColor(highlight);
 651:           }
 652:         g.drawLine(1, bottom - 4, 1, bottom);
 653:       }
 654: 
 655:     g.translate(-x, -y);
 656:   }
 657: 
 658:   /**
 659:    * Paints the background for a tab.
 660:    * 
 661:    * @param g  the graphics device.
 662:    * @param tabPlacement  the tab placement ({@link #TOP}, {@link #LEFT}, 
 663:    *        {@link #BOTTOM} or {@link #RIGHT}).
 664:    * @param tabIndex  the index of the tab to draw the border for.
 665:    * @param x  the x-coordinate for the tab's bounding rectangle.
 666:    * @param y  the y-coordinate for the tab's bounding rectangle.
 667:    * @param w  the width for the tab's bounding rectangle.
 668:    * @param h  the height for the tab's bounding rectangle.
 669:    * @param isSelected  indicates whether or not the tab is selected.
 670:    */
 671:   protected void paintTabBackground(Graphics g, int tabPlacement,
 672:       int tabIndex, int x, int y, int w, int h, boolean isSelected)
 673:   {
 674:     if (isSelected)
 675:       g.setColor(selectColor);
 676:     else
 677:       g.setColor(getUnselectedBackground(tabIndex));
 678: 
 679:     switch (tabPlacement)
 680:     {
 681:       case LEFT:
 682:         g.fillRect(x + 5, y + 1, w - 5, h - 1);
 683:         g.fillRect(x + 2, y + 4, 3, h - 4);
 684:         break;
 685:       case BOTTOM:
 686:         g.fillRect(x + 2, y, w - 2, h - 3);
 687:         g.fillRect(x + 5, y + h - 4, w - 5, 3);
 688:         break;
 689:       case RIGHT:
 690:         g.fillRect(x, y + 1, w - 4, h - 1);
 691:         g.fillRect(x + w - 4, y + 5, 3, h - 5);
 692:         break;
 693:       case TOP:
 694:       default:
 695:         g.fillRect(x + 4, y + 2, w - 4, h - 2);
 696:         g.fillRect(x + 2, y + 5, 2, h - 5);
 697:     }
 698:   }
 699:   
 700:   /**
 701:    * This method paints the focus rectangle around the selected tab.
 702:    *
 703:    * @param g The Graphics object to paint with.
 704:    * @param tabPlacement The JTabbedPane's tab placement.
 705:    * @param rects The array of rectangles keeping track of size and position.
 706:    * @param tabIndex The tab index.
 707:    * @param iconRect The icon bounds.
 708:    * @param textRect The text bounds.
 709:    * @param isSelected Whether this tab is selected.
 710:    */
 711:   protected void paintFocusIndicator(Graphics g, int tabPlacement,
 712:                                      Rectangle[] rects, int tabIndex,
 713:                                      Rectangle iconRect, Rectangle textRect,
 714:                                      boolean isSelected)
 715:   {
 716:     if (tabPane.hasFocus() && isSelected)
 717:       {
 718:         Rectangle rect = rects[tabIndex];
 719: 
 720:         g.setColor(focus);
 721:         g.translate(rect.x, rect.y);
 722:         
 723:         switch (tabPlacement)
 724:           {
 725:           case LEFT:
 726:             // Top line
 727:             g.drawLine(7, 2, rect.width-2, 2);
 728:             
 729:             // Right line
 730:             g.drawLine(rect.width-1, 2, rect.width-1, rect.height-3);
 731:             
 732:             // Bottom line
 733:             g.drawLine(rect.width-2, rect.height-2, 3, rect.height-2);
 734:             
 735:             // Left line
 736:             g.drawLine(2, rect.height-3, 2, 7);
 737: 
 738:             // Slant
 739:             g.drawLine(2, 6, 6, 2);
 740:             break;
 741:           case RIGHT:
 742:             // Top line
 743:             g.drawLine(1, 2, rect.width-8, 2);
 744:             
 745:             // Slant
 746:             g.drawLine(rect.width-7, 2, rect.width-3, 6);
 747:             
 748:             // Right line
 749:             g.drawLine(rect.width-3, 7, rect.width-3, rect.height-3);
 750:             
 751:             // Bottom line
 752:             g.drawLine(rect.width-3, rect.height-2, 2, rect.height-2);
 753: 
 754:             // Left line
 755:             g.drawLine(1, rect.height-2, 1, 2);
 756:             break;
 757:           case BOTTOM:
 758:             // Top line
 759:             g.drawLine(2, 1, rect.width-2, 1);
 760:             
 761:             // Right line
 762:             g.drawLine(rect.width-1, 2, rect.width-1, rect.height-3);
 763:             
 764:             // Bottom line
 765:             g.drawLine(7, rect.height-3, rect.width-2, rect.height-3);
 766:             
 767:             // Slant
 768:             g.drawLine(6, rect.height-3, 2, rect.height-7);
 769:             
 770:             // Left line
 771:             g.drawLine(2, rect.height-8, 2, 2);
 772:             
 773:             break;
 774:           case TOP:
 775:           default:
 776:             // Top line
 777:             g.drawLine(6, 2, rect.width-2, 2);
 778:             
 779:             // Right line
 780:             g.drawLine(rect.width-1, 2, rect.width-1, rect.height-3);
 781:             
 782:             // Bottom line
 783:             g.drawLine(3, rect.height-3, rect.width-2, rect.height-3);
 784:             
 785:             // Left line
 786:             g.drawLine(2, rect.height-3, 2, 7);
 787:             
 788:             // Slant
 789:             g.drawLine(2, 6, 6, 2);
 790:             
 791:           }
 792:         
 793:         g.translate(-rect.x, -rect.y);
 794:       }
 795:   }
 796:   
 797:   /**
 798:    * Returns <code>true</code> if the tabs in the specified run should be 
 799:    * padded to make the run fill the width/height of the {@link JTabbedPane}.
 800:    * 
 801:    * @param tabPlacement  the tab placement for the {@link JTabbedPane} (one of
 802:    *        {@link #TOP}, {@link #BOTTOM}, {@link #LEFT} and {@link #RIGHT}).
 803:    * @param run  the run index.
 804:    * 
 805:    * @return A boolean.
 806:    */
 807:   protected boolean shouldPadTabRun(int tabPlacement, int run)
 808:   {
 809:     // as far as I can tell, all runs should be padded except the last run
 810:     // (which is drawn at the very top for tabPlacement == TOP)
 811:     return run < this.runCount - 1;
 812:   }
 813: 
 814:   /**
 815:    * Installs the defaults for this UI. This method calls super.installDefaults
 816:    * and then loads the Metal specific defaults for TabbedPane.
 817:    */
 818:   protected void installDefaults()
 819:   {
 820:     super.installDefaults();
 821:     selectColor = UIManager.getColor("TabbedPane.selected");
 822:     selectHighlight = UIManager.getColor("TabbedPane.selectHighlight");
 823:     tabAreaBackground = UIManager.getColor("TabbedPane.tabAreaBackground");
 824:     tabsOpaque = UIManager.getBoolean("TabbedPane.tabsOpaque");
 825:     minTabWidth = 0;
 826:   }
 827:   
 828:   /**
 829:    * Returns the color for the gap.
 830:    * 
 831:    * @param currentRun - The current run to return the color for
 832:    * @param x - The x position of the current run
 833:    * @param y - The y position of the current run
 834:    * 
 835:    * @return the color for the gap in the current run.
 836:    */
 837:   protected Color getColorForGap(int currentRun, int x, int y)
 838:   {
 839:     int index = tabForCoordinate(tabPane, x, y);
 840:     int selected = tabPane.getSelectedIndex();
 841:     if (selected == index)
 842:       return selectColor;
 843:     return tabAreaBackground;
 844:   }
 845:   
 846:   /**
 847:    * Returns true if the gap should be filled in.
 848:    * 
 849:    * @param currentRun - The current run
 850:    * @param tabIndex - The current tab
 851:    * @param x - The x position of the tab
 852:    * @param y - The y position of the tab
 853:    * 
 854:    * @return true if the gap at the current run should be filled 
 855:    */
 856:   protected boolean shouldFillGap(int currentRun, int tabIndex, int x, int y)
 857:   {
 858:     // As far as I can tell, the gap is never filled in.
 859:     return false;
 860:   }
 861:   
 862:   /**
 863:    * Paints the highlight below the tab, if there is one.
 864:    */
 865:   protected void paintHighlightBelowTab()
 866:   {
 867:     int selected = tabPane.getSelectedIndex();
 868:     int tabPlacement = tabPane.getTabPlacement();
 869:     Rectangle bounds = getTabBounds(tabPane, selected);
 870:     
 871:     hg.setColor(selectColor);
 872:     int x = bounds.x;
 873:     int y = bounds.y;
 874:     int w = bounds.width;
 875:     int h = bounds.height;
 876: 
 877:     if (tabPlacement == TOP) 
 878:         hg.fillRect(x, y + h - 2, w, 30);
 879:     else if (tabPlacement == LEFT)
 880:         hg.fillRect(x + w - 1, y, 20, h);
 881:     else if (tabPlacement == BOTTOM)
 882:         hg.fillRect(x, y - h + 2, w, 30);
 883:     else if (tabPlacement == RIGHT)
 884:         hg.fillRect(x - 18, y, 20, h);
 885:     else 
 886:       throw new AssertionError("Unrecognised 'tabPlacement' argument.");
 887:     hg = null;
 888:   }
 889:   
 890:   /**
 891:    * Returns true if we should rotate the tab runs. 
 892:    * 
 893:    * @param tabPlacement - The current tab placement.
 894:    * @param selectedRun - The selected run.
 895:    * 
 896:    * @return true if the tab runs should be rotated.
 897:    */
 898:   protected boolean shouldRotateTabRuns(int tabPlacement,
 899:                                         int selectedRun)
 900:   {
 901:     // false because tab runs are not rotated in the MetalLookAndFeel
 902:     return false;
 903:   }
 904: 
 905:   protected int calculateMaxTabHeight(int tabPlacement)
 906:   {
 907:     // FIXME: Why is this overridden?
 908:     return super.calculateMaxTabHeight(tabPlacement);
 909:   }
 910: 
 911:   /**
 912:    * Returns the amount of overlay among the tabs. In
 913:    * the Metal L&F the overlay for LEFT and RIGHT placement
 914:    * is half of the maxTabHeight. For TOP and BOTTOM placement
 915:    * the tabs do not overlay.
 916:    *
 917:    * @param tabPlacement the placement
 918:    *
 919:    * @return the amount of overlay among the tabs
 920:    */
 921:   protected int getTabRunOverlay(int tabPlacement)
 922:   {
 923:     int overlay = 0;
 924:     if (tabPlacement == LEFT || tabPlacement == RIGHT)
 925:       {
 926:         int maxHeight = calculateMaxTabHeight(tabPlacement);
 927:         overlay = maxTabHeight / 2;
 928:       }
 929:     return overlay;
 930:   }
 931: 
 932:   /**
 933:    * Paints the upper edge of the content border.
 934:    *
 935:    * @param g the graphics to use for painting
 936:    * @param tabPlacement the tab placement
 937:    * @param selectedIndex the index of the selected tab
 938:    * @param x the upper left coordinate of the content area
 939:    * @param y the upper left coordinate of the content area
 940:    * @param w the width of the content area
 941:    * @param h the height of the content area
 942:    */
 943:   protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
 944:                                            int selectedIndex, int x, int y,
 945:                                            int w, int h)
 946:   {
 947:     Color oceanSelectedBorder =
 948:       UIManager.getColor("TabbedPane.borderHightlightColor");
 949:     boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
 950:     if (isOcean)
 951:       {
 952:         g.setColor(oceanSelectedBorder);
 953:       }
 954:     else
 955:       {
 956:         g.setColor(selectHighlight);
 957:       }
 958: 
 959:     Rectangle rect = selectedIndex < 0 ? null :
 960:                                          getTabBounds(selectedIndex, calcRect);
 961: 
 962:     // If tabs are not placed on TOP, or if the selected tab is not in the
 963:     // run directly above the content or the selected tab is not visible,
 964:     // then we draw an unbroken line.
 965:     if (tabPlacement != TOP || selectedIndex < 0
 966:         || rect.y  + rect.height + 1 < y || rect.x < x || rect.x > x + w)
 967:       {
 968:         g.drawLine(x, y, x + w - 2, y);
 969:         if (isOcean && tabPlacement == TOP)
 970:           {
 971:             g.setColor(MetalLookAndFeel.getWhite());
 972:             g.drawLine(x, y + 1, x + w - 2, y + 1);
 973:           }
 974:       }
 975:     else
 976:       {
 977:         boolean isLast = isLastTabInRun(selectedIndex);
 978:         if (isLast)
 979:           {
 980:             g.drawLine(x, y, rect.x + 1, y);
 981:           }
 982:         else
 983:           {
 984:             g.drawLine(x, y, rect.x, y);
 985:           }
 986: 
 987:         int right = x + w - 1;
 988:         if (rect.x + rect.width < right - 1)
 989:           {
 990:             if (isLast)
 991:               {
 992:                 g.drawLine(rect.x + rect.width - 1, y, right - 1, y);
 993:               }
 994:             else
 995:               {
 996:                 g.drawLine(rect.x + rect.width, y, right - 1, y);
 997:               }
 998:           }
 999:         else
1000:           {
1001:             g.setColor(shadow);
1002:             g.drawLine(x + w - 2, y, x + w - 2, y);
1003:           }
1004: 
1005:         // When in OceanTheme, draw another white line.
1006:         if (isOcean)
1007:           {
1008:             g.setColor(MetalLookAndFeel.getWhite());
1009:             if (isLast)
1010:               {
1011:                 g.drawLine(x, y + 1, rect.x + 1, y + 1);
1012:               }
1013:             else
1014:               {
1015:                 g.drawLine(x, y + 1, rect.x, y + 1);
1016:               }
1017: 
1018:             if (rect.x + rect.width < right - 1)
1019:               {
1020:                 if (isLast)
1021:                   {
1022:                     g.drawLine(rect.x + rect.width - 1, y + 1, right - 1,
1023:                                y + 1);
1024:                   }
1025:                 else
1026:                   {
1027:                     g.drawLine(rect.x + rect.width, y + 1, right - 1, y + 1);
1028:                   }
1029:               }
1030:             else
1031:               {
1032:                 g.setColor(shadow);
1033:                 g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1);
1034:               }
1035:           }
1036:       }
1037:   }
1038: 
1039:   /**
1040:    * Paints the lower edge of the content border.
1041:    *
1042:    * @param g the graphics to use for painting
1043:    * @param tabPlacement the tab placement
1044:    * @param selectedIndex the index of the selected tab
1045:    * @param x the upper left coordinate of the content area
1046:    * @param y the upper left coordinate of the content area
1047:    * @param w the width of the content area
1048:    * @param h the height of the content area
1049:    */
1050:   protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
1051:                                               int selectedIndex, int x, int y,
1052:                                               int w, int h)
1053:   {
1054:     g.setColor(darkShadow);
1055:     
1056:     // If tabs are not placed on BOTTOM, or if the selected tab is not in the
1057:     // run directly below the content or the selected tab is not visible,
1058:     // then we draw an unbroken line.
1059:     Rectangle rect = selectedIndex < 0 ? null :
1060:                                          getTabBounds(selectedIndex, calcRect);
1061:     boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
1062:     Color oceanSelectedBorder =
1063:       UIManager.getColor("TabbedPane.borderHightlightColor");
1064:     if (tabPlacement != BOTTOM || selectedIndex < 0 || rect.y - 1 > h
1065:         || rect.x < x || rect.x > x + w)
1066:       {
1067:         if (isOcean && tabPlacement == BOTTOM)
1068:           {
1069:             g.setColor(oceanSelectedBorder);
1070:           }
1071:         g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
1072:       }
1073:     else
1074:       {
1075:         boolean isLast = isLastTabInRun(selectedIndex);
1076:         if (isOcean)
1077:           {
1078:             g.setColor(oceanSelectedBorder);
1079:           }
1080: 
1081:         int bottom = y + h - 1;
1082:         int right = x + w - 1;
1083:         if (isLast)
1084:           {
1085:             g.drawLine(x, bottom, rect.x, bottom);
1086:           }
1087:         else
1088:           {
1089:             g.drawLine(x, bottom, rect.x - 1, bottom);
1090:           }
1091: 
1092:         if (rect.x + rect.width < x + w - 2)
1093:           {
1094:             if (isLast)
1095:               {
1096:                 g.drawLine(rect.x + rect.width - 1, bottom, right, bottom);
1097:               }
1098:             else
1099:               {
1100:                 g.drawLine(rect.x + rect.width, bottom, right, bottom);
1101:               }
1102:           }
1103:       }
1104:   }
1105: 
1106:   /**
1107:    * Paints the left edge of the content border.
1108:    *
1109:    * @param g the graphics to use for painting
1110:    * @param tabPlacement the tab placement
1111:    * @param selectedIndex the index of the selected tab
1112:    * @param x the upper left coordinate of the content area
1113:    * @param y the upper left coordinate of the content area
1114:    * @param w the width of the content area
1115:    * @param h the height of the content area
1116:    */
1117:   protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
1118:                                             int selectedIndex, int x, int y,
1119:                                             int w, int h)
1120:   {
1121:     boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
1122:     Color oceanSelectedBorder =
1123:       UIManager.getColor("TabbedPane.borderHightlightColor");
1124:     Rectangle rect = selectedIndex < 0 ? null :
1125:       getTabBounds(selectedIndex, calcRect);
1126: 
1127:     if (isOcean)
1128:       {
1129:         g.setColor(oceanSelectedBorder);
1130:       }
1131:     else
1132:       {
1133:         g.setColor(selectHighlight);
1134:       }
1135: 
1136:     // If tabs are not placed on LEFT, or if the selected tab is not in the
1137:     // run directly left to the content or the selected tab is not visible,
1138:     // then we draw an unbroken line.
1139:     if (tabPlacement != LEFT || selectedIndex < 0
1140:         || rect.x + rect.width + 1 < x || rect.y < y || rect.y > y + h)
1141:       {
1142:         g.drawLine(x, y + 1, x, y + h - 2);
1143:         if (isOcean && tabPlacement == LEFT)
1144:           {
1145:             g.setColor(MetalLookAndFeel.getWhite());
1146:             g.drawLine(x, y + 1, x, y + h - 2);
1147:           }       
1148:       }
1149:     else
1150:       {
1151:         g.drawLine(x, y, x, rect.y + 1);
1152:         if (rect.y + rect.height < y + h - 2)
1153:           {
1154:             g.drawLine(x, rect.y + rect.height + 1, x, y + h + 2);
1155:           }
1156:         if (isOcean)
1157:           {
1158:             g.setColor(MetalLookAndFeel.getWhite());
1159:             g.drawLine(x + 1, y + 1, x + 1, rect.y + 1);
1160:             if (rect.y + rect.height < y + h - 2)
1161:               {
1162:                 g.drawLine(x + 1, rect.y + rect.height + 1, x + 1, y + h + 2);
1163:               }
1164:           }
1165:       }
1166:     
1167:   }
1168: 
1169:   /**
1170:    * Paints the right edge of the content border.
1171:    *
1172:    * @param g the graphics to use for painting
1173:    * @param tabPlacement the tab placement
1174:    * @param selectedIndex the index of the selected tab
1175:    * @param x the upper left coordinate of the content area
1176:    * @param y the upper left coordinate of the content area
1177:    * @param w the width of the content area
1178:    * @param h the height of the content area
1179:    */
1180:   protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
1181:                                              int selectedIndex, int x, int y,
1182:                                              int w, int h)
1183:   {
1184:     g.setColor(darkShadow);
1185:     Rectangle rect = selectedIndex < 0 ? null :
1186:       getTabBounds(selectedIndex, calcRect);
1187:     boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
1188:     Color oceanSelectedBorder =
1189:       UIManager.getColor("TabbedPane.borderHightlightColor");
1190: 
1191:     // If tabs are not placed on RIGHT, or if the selected tab is not in the
1192:     // run directly right to the content or the selected tab is not visible,
1193:     // then we draw an unbroken line.
1194:     if (tabPlacement != RIGHT || selectedIndex < 0 || rect.x - 1 > w
1195:         || rect.y < y || rect.y > y + h)
1196:       {
1197:         if (isOcean && tabPlacement == RIGHT)
1198:           {
1199:             g.setColor(oceanSelectedBorder);
1200:           }
1201:         g.drawLine(x + w - 1, y, x + w - 1, y + h - 1);
1202:       }
1203:     else
1204:       {
1205:         if (isOcean)
1206:           {
1207:             g.setColor(oceanSelectedBorder);
1208:           }
1209:         g.drawLine(x + w - 1, y, x + w - 1, rect.y);
1210: 
1211:         if (rect.y + rect.height < y + h - 2)
1212:           {
1213:             g.drawLine(x + w - 1, rect.y + rect.height, x + w - 1, y + h - 2);
1214:           }
1215:       }
1216:   }
1217: 
1218:   /**
1219:    * Determines if the specified tab is the last tab in its tab run.
1220:    *
1221:    * @param tabIndex the index of the tab
1222:    *
1223:    * @return if the specified tab is the last tab in its tab run
1224:    */
1225:   private boolean isLastTabInRun(int tabIndex)
1226:   {
1227:     int count = tabPane.getTabCount();
1228:     int run = getRunForTab(count, tabIndex);
1229:     int lastIndex = lastTabInRun(count, run);
1230:     return tabIndex == lastIndex;
1231:   }
1232: 
1233:   /**
1234:    * Returns the background for an unselected tab. This first asks the
1235:    * JTabbedPane for the background at the specified tab index, if this
1236:    * is an UIResource (that means, it is inherited from the JTabbedPane)
1237:    * and the TabbedPane.unselectedBackground UI property is not null,
1238:    * this returns the value of the TabbedPane.unselectedBackground property,
1239:    * otherwise the value returned by the JTabbedPane.
1240:    *
1241:    * @param tabIndex the index of the tab for which we query the background
1242:    *
1243:    * @return the background for an unselected tab
1244:    */
1245:   private Color getUnselectedBackground(int tabIndex)
1246:   {
1247:     Color bg = tabPane.getBackgroundAt(tabIndex);
1248:     Color unselectedBackground =
1249:       UIManager.getColor("TabbedPane.unselectedBackground");
1250:     if (bg instanceof UIResource && unselectedBackground != null)
1251:       bg = unselectedBackground;
1252:     return bg;
1253:   }
1254:   
1255:   protected int getTabLabelShiftX(int tabPlacement,
1256:                                   int index,
1257:                                   boolean isSelected)
1258:   {
1259:     return 0;
1260:   }
1261: 
1262:   protected int getTabLabelShiftY(int tabPlacement,
1263:                                   int index,
1264:                                   boolean isSelected)
1265:   {
1266:     return 0;
1267:   }
1268:   
1269: }