Source for javax.swing.plaf.metal.MetalTabbedPaneUI

   1: /* MetalTabbedPaneUI.java
   2:    Copyright (C) 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.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 new TabbedPaneLayout();
 174:   }
 175:   
 176:   /**
 177:    * Paints the border for a single tab.
 178:    * 
 179:    * @param g  the graphics device.
 180:    * @param tabPlacement  the tab placement ({@link #TOP}, {@link #LEFT}, 
 181:    *        {@link #BOTTOM} or {@link #RIGHT}).
 182:    * @param tabIndex  the index of the tab to draw the border for.
 183:    * @param x  the x-coordinate for the tab's bounding rectangle.
 184:    * @param y  the y-coordinate for the tab's bounding rectangle.
 185:    * @param w  the width for the tab's bounding rectangle.
 186:    * @param h  the height for the tab's bounding rectangle.
 187:    * @param isSelected  indicates whether or not the tab is selected.
 188:    */
 189:   protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex, 
 190:           int x, int y, int w, int h, boolean isSelected) 
 191:   {
 192:     int bottom = y + h - 1;
 193:     int right = x + w - 1;
 194: 
 195:     switch (tabPlacement)
 196:     {
 197:     case LEFT: 
 198:       paintLeftTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
 199:       break;
 200:     case BOTTOM:
 201:       paintBottomTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
 202:       break;
 203:     case  RIGHT:
 204:       paintRightTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
 205:       break;
 206:     case TOP:
 207:     default: 
 208:       paintTopTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
 209:     }
 210:   }
 211: 
 212:   /**
 213:    * Paints the border for a tab assuming that the tab position is at the top
 214:    * ({@link #TOP}).
 215:    * 
 216:    * @param tabIndex  the tab index.
 217:    * @param g  the graphics device.
 218:    * @param x  the x-coordinate for the tab's bounding rectangle.
 219:    * @param y  the y-coordinate for the tab's bounding rectangle.
 220:    * @param w  the width for the tab's bounding rectangle.
 221:    * @param h  the height for the tab's bounding rectangle.
 222:    * @param btm  the y coordinate of the bottom border
 223:    * @param rght the x coordinate of the right border
 224:    * @param isSelected  indicates whether the tab is selected.
 225:    */
 226:   protected void paintTopTabBorder(int tabIndex, Graphics g, int x, int y,
 227:       int w, int h, int btm, int rght, boolean isSelected)
 228:   {
 229:     int tabCount = tabPane.getTabCount();
 230:     int currentRun = getRunForTab(tabCount, tabIndex);
 231:     int right = w - 1;
 232:     int bottom = h - 1;
 233: 
 234:     // Paint gap.
 235:     if (shouldFillGap(currentRun, tabIndex, x, y))
 236:       {
 237:         g.translate(x, y);
 238:         g.setColor(getColorForGap(currentRun, x, y + 1));
 239:         g.fillRect(1, 0, 5, 3);
 240:         g.fillRect(1, 3, 2, 2);
 241:         g.translate(-x, -y);
 242:       }
 243: 
 244:     g.translate(x, y);
 245: 
 246:     boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
 247:     Color oceanSelectedBorder =
 248:       UIManager.getColor("TabbedPane.borderHightlightColor");
 249:     if (isOcean && isSelected)
 250:       g.setColor(oceanSelectedBorder);
 251:     else
 252:       g.setColor(darkShadow);
 253: 
 254:     // Slant
 255:     g.drawLine(1, 5, 6, 0);
 256:     // Top.
 257:     g.drawLine(6, 0, right, 0);
 258:     // Right.
 259:     int lastIndex = lastTabInRun(tabCount, currentRun);
 260:     if (tabIndex == lastIndex)
 261:       g.drawLine(right, 1, right, bottom);
 262:     // Left.
 263:     int selectedIndex = tabPane.getSelectedIndex();
 264:     if (isOcean && tabIndex - 1 == selectedIndex
 265:         && currentRun == getRunForTab(tabCount, selectedIndex))
 266:       {
 267:         g.setColor(oceanSelectedBorder);
 268:       }
 269:     if (tabIndex != tabRuns[runCount - 1])
 270:       {
 271:         if (isOcean && isSelected)
 272:           {
 273:             g.drawLine(0, 6, 0, bottom);
 274:             g.setColor(darkShadow);
 275:             g.drawLine(0, 0, 0, 5);
 276:           }
 277:         else
 278:           {
 279:             g.drawLine(0, 0, 0, bottom);
 280:           }
 281:       }
 282:     else
 283:       {
 284:         g.drawLine(0, 6, 0, bottom);
 285:       }
 286: 
 287:     // Paint the highlight.
 288:     g.setColor(isSelected ? selectHighlight : highlight);
 289:     // Slant.
 290:     g.drawLine(1, 6, 6, 1);
 291:     // Top.
 292:     g.drawLine(6, 1, right, 1);
 293:     // Left.
 294:     g.drawLine(1, 6, 1, bottom);
 295:     int firstIndex = tabRuns[currentRun];
 296:     if (tabIndex == firstIndex && tabIndex != tabRuns[runCount - 1])
 297:       {
 298:         if (tabPane.getSelectedIndex() == tabRuns[currentRun + 1])
 299:           g.setColor(selectHighlight);
 300:         else
 301:           g.setColor(highlight);
 302:         g.drawLine(1, 0, 1, 4);
 303:       }
 304: 
 305:     g.translate(-x, -y);
 306:   }
 307:   
 308:   /**
 309:    * Paints the border for a tab assuming that the tab position is at the left
 310:    * ({@link #LEFT}).
 311:    * 
 312:    * @param tabIndex  the tab index.
 313:    * @param g  the graphics device.
 314:    * @param x  the x-coordinate for the tab's bounding rectangle.
 315:    * @param y  the y-coordinate for the tab's bounding rectangle.
 316:    * @param w  the width for the tab's bounding rectangle.
 317:    * @param h  the height for the tab's bounding rectangle.
 318:    * @param btm  ???
 319:    * @param rght  ???
 320:    * @param isSelected  indicates whether the tab is selected.
 321:    */
 322:   protected void paintLeftTabBorder(int tabIndex, Graphics g, int x, int y,
 323:       int w, int h, int btm, int rght, boolean isSelected)
 324:   {
 325:     g.translate(x, y);
 326:     int bottom = h - 1;
 327:     int right = w - 1;
 328: 
 329:     
 330:     int tabCount = tabPane.getTabCount();
 331:     int currentRun = getRunForTab(tabCount, tabIndex);
 332:     int firstIndex = tabRuns[currentRun];
 333: 
 334:     // Paint the part of the above tab.
 335:     if (tabIndex != firstIndex && tabIndex > 0 && tabsOpaque)
 336:       {
 337:         Color c;
 338:         if (tabPane.getSelectedIndex() == tabIndex - 1)
 339:           c = selectColor;
 340:         else
 341:           c = getUnselectedBackground(tabIndex - 1);
 342:         g.setColor(c);
 343:         g.fillRect(2, 0, 4, 3);
 344:         g.drawLine(2, 3, 2, 3);
 345:       }
 346: 
 347:     // Paint the highlight.
 348:     boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
 349:     if (isOcean)
 350:       {
 351:         g.setColor(isSelected ? selectHighlight : MetalLookAndFeel.getWhite());
 352:       }
 353:     else
 354:       {
 355:         g.setColor(isSelected ? selectHighlight : highlight);
 356:       }
 357:     // Slant.
 358:     g.drawLine(1, 6, 6, 1);
 359:     // Left.
 360:     g.drawLine(1, 6, 1, bottom);
 361:     // Top.
 362:     g.drawLine(6, 1, right, 1);
 363:     if (tabIndex != firstIndex)
 364:       {
 365:         if (isOcean)
 366:           {
 367:             g.setColor(MetalLookAndFeel.getWhite());
 368:           }
 369:         g.drawLine(1, 0, 1, 4);
 370:       }
 371: 
 372:     // Paint border.
 373:     Color oceanSelectedBorder =
 374:       UIManager.getColor("TabbedPane.borderHightlightColor");
 375:     if (isOcean && isSelected)
 376:       {
 377:         g.setColor(oceanSelectedBorder);
 378:       }
 379:     else
 380:       {
 381:         g.setColor(darkShadow);
 382:       }
 383: 
 384:     // Slant.
 385:     g.drawLine(1, 5, 6, 0);
 386:     // Top.
 387:     g.drawLine(6, 0, right, 0);
 388:     // Bottom.
 389:     int lastIndex = lastTabInRun(tabCount, currentRun);
 390:     if (tabIndex == lastIndex)
 391:       {
 392:         g.drawLine(0, bottom, right, bottom);
 393:       }
 394:     // Left.
 395:     if (isOcean)
 396:       {
 397:         if (tabPane.getSelectedIndex() == tabIndex - 1)
 398:           {
 399:             g.drawLine(0, 5, 0, bottom);
 400:             g.setColor(oceanSelectedBorder);
 401:             g.drawLine(0, 0, 0, 5);
 402:           }
 403:         else if (isSelected)
 404:           {
 405:             g.drawLine(0, 5, 0, bottom);
 406:             if (tabIndex != 0)
 407:               {
 408:                 g.setColor(darkShadow);
 409:                 g.drawLine(0, 0, 0, 5);
 410:               }
 411:           }
 412:         else if (tabIndex != firstIndex)
 413:           {
 414:             g.drawLine(0, 0, 0, bottom);
 415:           }
 416:         else
 417:           {
 418:             g.drawLine(0, 6, 0, bottom);
 419:           }
 420:       }
 421:     else
 422:       {
 423:         if (tabIndex != firstIndex)
 424:           {
 425:             g.drawLine(0, 0, 0, bottom);
 426:           }
 427:         else
 428:           {
 429:             g.drawLine(0, 6, 0, bottom);
 430:           }
 431:       }
 432: 
 433:     g.translate(-x, -y);
 434:   }
 435:   
 436:   /**
 437:    * Paints the border for a tab assuming that the tab position is at the right
 438:    * ({@link #RIGHT}).
 439:    * 
 440:    * @param tabIndex  the tab index.
 441:    * @param g  the graphics device.
 442:    * @param x  the x-coordinate for the tab's bounding rectangle.
 443:    * @param y  the y-coordinate for the tab's bounding rectangle.
 444:    * @param w  the width for the tab's bounding rectangle.
 445:    * @param h  the height for the tab's bounding rectangle.
 446:    * @param btm  ???
 447:    * @param rght  ???
 448:    * @param isSelected  indicates whether the tab is selected.
 449:    */
 450:   protected void paintRightTabBorder(int tabIndex, Graphics g, int x, int y,
 451:       int w, int h, int btm, int rght, boolean isSelected)
 452:   {
 453:     g.translate(x, y);
 454:     int bottom = h - 1;
 455:     int right = w - 1;
 456: 
 457:     int tabCount = tabPane.getTabCount();
 458:     int currentRun = getRunForTab(tabCount, tabIndex);
 459:     int firstIndex = tabRuns[currentRun];
 460: 
 461:     // Paint part of the above tab.
 462:     if (tabIndex != firstIndex && tabIndex > 0 && tabsOpaque)
 463:       {
 464:         Color c;
 465:         if (tabPane.getSelectedIndex() == tabIndex - 1)
 466:           c = UIManager.getColor("TabbedPane.tabAreaBackground");
 467:         else
 468:           c = getUnselectedBackground(tabIndex - 1);
 469:         g.fillRect(right - 5, 0, 5, 3);
 470:         g.fillRect(right - 2, 3, 2, 2);
 471:       }
 472: 
 473:     // Paint highlight.
 474:     g.setColor(isSelected ? selectHighlight : highlight);
 475: 
 476:     // Slant.
 477:     g.drawLine(right - 6, 1, right - 1, 6);
 478:     // Top.
 479:     g.drawLine(0, 1, right - 6, 1);
 480:     // Left.
 481:     if (! isSelected)
 482:       {
 483:         g.drawLine(0, 1, 0, bottom);
 484:       }
 485: 
 486:     // Paint border.
 487:     boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
 488:     Color oceanSelectedBorder =
 489:       UIManager.getColor("TabbedPane.borderHightlightColor");
 490:     if (isOcean && isSelected)
 491:       {
 492:         g.setColor(oceanSelectedBorder);
 493:       }
 494:     else
 495:       {
 496:         g.setColor(darkShadow);
 497:       }
 498: 
 499:     // Bottom.
 500:     int lastIndex = lastTabInRun(tabCount, currentRun);
 501:     if (tabIndex == lastIndex)
 502:       {
 503:         g.drawLine(0, bottom, right, bottom);
 504:       }
 505:     // Slant.
 506:     if (isOcean && tabPane.getSelectedIndex() == tabIndex - 1)
 507:       {
 508:         g.setColor(oceanSelectedBorder);
 509:       }
 510:     g.drawLine(right - 6, 0, right, 6);
 511:     // Top.
 512:     g.drawLine(0, 0, right - 6, 0);
 513:     // Right.
 514:     if (isOcean && isSelected)
 515:       {
 516:         g.drawLine(right, 6, right, bottom);
 517:         if (tabIndex != firstIndex)
 518:           {
 519:             g.setColor(darkShadow);
 520:             g.drawLine(right, 0, right, 5);
 521:           }
 522:       }
 523:     else if (isOcean && tabPane.getSelectedIndex() == tabIndex - 1)
 524:       {
 525:         g.setColor(oceanSelectedBorder);
 526:         g.drawLine(right, 0, right, 6);
 527:         g.setColor(darkShadow);
 528:         g.drawLine(right, 6, right, bottom);
 529:       }
 530:     else if (tabIndex != firstIndex)
 531:       {
 532:         g.drawLine(right, 0, right, bottom);
 533:       }
 534:     else
 535:       {
 536:         g.drawLine(right, 6, right, bottom);
 537:       }
 538:     g.translate(-x, -y);
 539:   }
 540:   
 541:   /**
 542:    * Paints the border for a tab assuming that the tab position is at the bottom
 543:    * ({@link #BOTTOM}).
 544:    * 
 545:    * @param tabIndex  the tab index.
 546:    * @param g  the graphics device.
 547:    * @param x  the x-coordinate for the tab's bounding rectangle.
 548:    * @param y  the y-coordinate for the tab's bounding rectangle.
 549:    * @param w  the width for the tab's bounding rectangle.
 550:    * @param h  the height for the tab's bounding rectangle.
 551:    * @param btm  ???
 552:    * @param rght  ???
 553:    * @param isSelected  indicates whether the tab is selected.
 554:    */
 555:   protected void paintBottomTabBorder(int tabIndex, Graphics g, int x, int y,
 556:       int w, int h, int btm, int rght, boolean isSelected)
 557:   {
 558:     int bottom = h - 1;
 559:     int right = w - 1;
 560: 
 561:     int tabCount = tabPane.getTabCount();
 562:     int currentRun = getRunForTab(tabCount, tabIndex);
 563:     // Paint gap if necessary.
 564:     if (shouldFillGap(currentRun, tabIndex, x, y))
 565:       {
 566:         g.translate(x, y);
 567:         g.setColor(getColorForGap(currentRun, x, y));
 568:         g.fillRect(1, bottom - 4, 3, 5);
 569:         g.fillRect(4, bottom - 1, 2, 2);
 570:         g.translate(-x, -y);
 571:       }
 572: 
 573:     g.translate(x, y);
 574: 
 575:     // Paint border.
 576:     boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
 577:     Color oceanSelectedBorder =
 578:       UIManager.getColor("TabbedPane.borderHightlightColor");
 579:     if (isOcean && isSelected)
 580:       {
 581:         g.setColor(oceanSelectedBorder);
 582:       }
 583:     else
 584:       {
 585:         g.setColor(darkShadow);
 586:       }
 587:     // Slant.
 588:     g.drawLine(1, bottom - 5, 6, bottom);
 589:     // Bottom.
 590:     g.drawLine(6, bottom, right, bottom);
 591:     // Right.
 592:     int lastIndex = lastTabInRun(tabCount, currentRun);
 593:     if (tabIndex == lastIndex)
 594:       {
 595:         g.drawLine(right, 0, right, bottom);
 596:       }
 597:     // Left.
 598:     if (isOcean && isSelected)
 599:       {
 600:         g.drawLine(0, 0, 0, bottom - 5);
 601:         if ((currentRun == 0 && tabIndex != 0)
 602:             || (currentRun > 0 && tabIndex != tabRuns[currentRun - 1]))
 603:           {
 604:             g.setColor(darkShadow);
 605:             g.drawLine(0, bottom - 5, 0, bottom);
 606:           }
 607:       }
 608:     else
 609:       {
 610:         if (isOcean && tabIndex == tabPane.getSelectedIndex() + 1)
 611:           {
 612:             g.setColor(oceanSelectedBorder);
 613:           }
 614:         if (tabIndex != tabRuns[runCount - 1])
 615:           {
 616:             g.drawLine(0, 0, 0, bottom);
 617:           }
 618:         else
 619:           {
 620:             g.drawLine(0, 0, 0, bottom - 6);
 621:           }
 622:       }
 623: 
 624:     // Paint highlight.
 625:     g.setColor(isSelected ? selectHighlight : highlight);
 626:     // Slant.
 627:     g.drawLine(1, bottom - 6, 6, bottom - 1);
 628:     // Left.
 629:     g.drawLine(1, 0, 1, bottom - 6);
 630: 
 631:     int firstIndex = tabRuns[currentRun];
 632:     if (tabIndex == firstIndex && tabIndex != tabRuns[runCount - 1])
 633:       {
 634:         if (tabPane.getSelectedIndex() == tabRuns[currentRun + 1])
 635:           {
 636:             g.setColor(selectHighlight);
 637:           }
 638:         else
 639:           {
 640:             g.setColor(highlight);
 641:           }
 642:         g.drawLine(1, bottom - 4, 1, bottom);
 643:       }
 644: 
 645:     g.translate(-x, -y);
 646:   }
 647: 
 648:   /**
 649:    * Paints the background for a tab.
 650:    * 
 651:    * @param g  the graphics device.
 652:    * @param tabPlacement  the tab placement ({@link #TOP}, {@link #LEFT}, 
 653:    *        {@link #BOTTOM} or {@link #RIGHT}).
 654:    * @param tabIndex  the index of the tab to draw the border for.
 655:    * @param x  the x-coordinate for the tab's bounding rectangle.
 656:    * @param y  the y-coordinate for the tab's bounding rectangle.
 657:    * @param w  the width for the tab's bounding rectangle.
 658:    * @param h  the height for the tab's bounding rectangle.
 659:    * @param isSelected  indicates whether or not the tab is selected.
 660:    */
 661:   protected void paintTabBackground(Graphics g, int tabPlacement,
 662:       int tabIndex, int x, int y, int w, int h, boolean isSelected)
 663:   {
 664:     if (isSelected)
 665:       g.setColor(selectColor);
 666:     else
 667:       g.setColor(getUnselectedBackground(tabIndex));
 668: 
 669:     switch (tabPlacement)
 670:     {
 671:       case LEFT:
 672:         g.fillRect(x + 5, y + 1, w - 5, h - 1);
 673:         g.fillRect(x + 2, y + 4, 3, h - 4);
 674:         break;
 675:       case BOTTOM:
 676:         g.fillRect(x + 2, y, w - 2, h - 3);
 677:         g.fillRect(x + 5, y + h - 4, w - 5, 3);
 678:         break;
 679:       case RIGHT:
 680:         g.fillRect(x, y + 1, w - 4, h - 1);
 681:         g.fillRect(x + w - 4, y + 5, 3, h - 5);
 682:         break;
 683:       case TOP:
 684:       default:
 685:         g.fillRect(x + 4, y + 2, w - 4, h - 2);
 686:         g.fillRect(x + 2, y + 5, 2, h - 5);
 687:     }
 688:   }
 689:   
 690:   /**
 691:    * Returns <code>true</code> if the tabs in the specified run should be 
 692:    * padded to make the run fill the width/height of the {@link JTabbedPane}.
 693:    * 
 694:    * @param tabPlacement  the tab placement for the {@link JTabbedPane} (one of
 695:    *        {@link #TOP}, {@link #BOTTOM}, {@link #LEFT} and {@link #RIGHT}).
 696:    * @param run  the run index.
 697:    * 
 698:    * @return A boolean.
 699:    */
 700:   protected boolean shouldPadTabRun(int tabPlacement, int run)
 701:   {
 702:     // as far as I can tell, all runs should be padded except the last run
 703:     // (which is drawn at the very top for tabPlacement == TOP)
 704:     return run < this.runCount - 1;
 705:   }
 706: 
 707:   /**
 708:    * Installs the defaults for this UI. This method calls super.installDefaults
 709:    * and then loads the Metal specific defaults for TabbedPane.
 710:    */
 711:   protected void installDefaults()
 712:   {
 713:     super.installDefaults();
 714:     selectColor = UIManager.getColor("TabbedPane.selected");
 715:     selectHighlight = UIManager.getColor("TabbedPane.selectHighlight");
 716:     tabAreaBackground = UIManager.getColor("TabbedPane.tabAreaBackground");
 717:     tabsOpaque = UIManager.getBoolean("TabbedPane.tabsOpaque");
 718:     minTabWidth = 0;
 719:   }
 720:   
 721:   /**
 722:    * Returns the color for the gap.
 723:    * 
 724:    * @param currentRun - The current run to return the color for
 725:    * @param x - The x position of the current run
 726:    * @param y - The y position of the current run
 727:    * 
 728:    * @return the color for the gap in the current run.
 729:    */
 730:   protected Color getColorForGap(int currentRun, int x, int y)
 731:   {
 732:     int index = tabForCoordinate(tabPane, x, y);
 733:     int selected = tabPane.getSelectedIndex();
 734:     if (selected == index)
 735:       return selectColor;
 736:     return tabAreaBackground;
 737:   }
 738:   
 739:   /**
 740:    * Returns true if the gap should be filled in.
 741:    * 
 742:    * @param currentRun - The current run
 743:    * @param tabIndex - The current tab
 744:    * @param x - The x position of the tab
 745:    * @param y - The y position of the tab
 746:    * 
 747:    * @return true if the gap at the current run should be filled 
 748:    */
 749:   protected boolean shouldFillGap(int currentRun, int tabIndex, int x, int y)
 750:   {
 751:     // As far as I can tell, the gap is never filled in.
 752:     return false;
 753:   }
 754:   
 755:   /**
 756:    * Paints the highlight below the tab, if there is one.
 757:    */
 758:   protected void paintHighlightBelowTab()
 759:   {
 760:     int selected = tabPane.getSelectedIndex();
 761:     int tabPlacement = tabPane.getTabPlacement();
 762:     Rectangle bounds = getTabBounds(tabPane, selected);
 763:     
 764:     hg.setColor(selectColor);
 765:     int x = bounds.x;
 766:     int y = bounds.y;
 767:     int w = bounds.width;
 768:     int h = bounds.height;
 769: 
 770:     if (tabPlacement == TOP) 
 771:         hg.fillRect(x, y + h - 2, w, 30);
 772:     else if (tabPlacement == LEFT)
 773:         hg.fillRect(x + w - 1, y, 20, h);
 774:     else if (tabPlacement == BOTTOM)
 775:         hg.fillRect(x, y - h + 2, w, 30);
 776:     else if (tabPlacement == RIGHT)
 777:         hg.fillRect(x - 18, y, 20, h);
 778:     else 
 779:       throw new AssertionError("Unrecognised 'tabPlacement' argument.");
 780:     hg = null;
 781:   }
 782:   
 783:   /**
 784:    * Returns true if we should rotate the tab runs. 
 785:    * 
 786:    * @param tabPlacement - The current tab placement.
 787:    * @param selectedRun - The selected run.
 788:    * 
 789:    * @return true if the tab runs should be rotated.
 790:    */
 791:   protected boolean shouldRotateTabRuns(int tabPlacement,
 792:                                         int selectedRun)
 793:   {
 794:     // false because tab runs are not rotated in the MetalLookAndFeel
 795:     return false;
 796:   }
 797: 
 798:   protected int calculateMaxTabHeight(int tabPlacement)
 799:   {
 800:     // FIXME: Why is this overridden?
 801:     return super.calculateMaxTabHeight(tabPlacement);
 802:   }
 803: 
 804:   /**
 805:    * Returns the amount of overlay among the tabs. In
 806:    * the Metal L&F the overlay for LEFT and RIGHT placement
 807:    * is half of the maxTabHeight. For TOP and BOTTOM placement
 808:    * the tabs do not overlay.
 809:    *
 810:    * @param tabPlacement the placement
 811:    *
 812:    * @return the amount of overlay among the tabs
 813:    */
 814:   protected int getTabRunOverlay(int tabPlacement)
 815:   {
 816:     int overlay = 0;
 817:     if (tabPlacement == LEFT || tabPlacement == RIGHT)
 818:       {
 819:         int maxHeight = calculateMaxTabHeight(tabPlacement);
 820:         overlay = maxTabHeight / 2;
 821:       }
 822:     return overlay;
 823:   }
 824: 
 825:   /**
 826:    * Paints the upper edge of the content border.
 827:    *
 828:    * @param g the graphics to use for painting
 829:    * @param tabPlacement the tab placement
 830:    * @param selectedIndex the index of the selected tab
 831:    * @param x the upper left coordinate of the content area
 832:    * @param y the upper left coordinate of the content area
 833:    * @param w the width of the content area
 834:    * @param h the height of the content area
 835:    */
 836:   protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
 837:                                            int selectedIndex, int x, int y,
 838:                                            int w, int h)
 839:   {
 840:     Color oceanSelectedBorder =
 841:       UIManager.getColor("TabbedPane.borderHightlightColor");
 842:     boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
 843:     if (isOcean)
 844:       {
 845:         g.setColor(oceanSelectedBorder);
 846:       }
 847:     else
 848:       {
 849:         g.setColor(selectHighlight);
 850:       }
 851: 
 852:     Rectangle rect = selectedIndex < 0 ? null :
 853:                                          getTabBounds(selectedIndex, calcRect);
 854: 
 855:     // If tabs are not placed on TOP, or if the selected tab is not in the
 856:     // run directly above the content or the selected tab is not visible,
 857:     // then we draw an unbroken line.
 858:     if (tabPlacement != TOP || selectedIndex < 0
 859:         || rect.y  + rect.height + 1 < y || rect.x < x || rect.x > x + w)
 860:       {
 861:         g.drawLine(x, y, x + w - 2, y);
 862:         if (isOcean && tabPlacement == TOP)
 863:           {
 864:             g.setColor(MetalLookAndFeel.getWhite());
 865:             g.drawLine(x, y + 1, x + w - 2, y + 1);
 866:           }
 867:       }
 868:     else
 869:       {
 870:         boolean isLast = isLastTabInRun(selectedIndex);
 871:         if (isLast)
 872:           {
 873:             g.drawLine(x, y, rect.x + 1, y);
 874:           }
 875:         else
 876:           {
 877:             g.drawLine(x, y, rect.x, y);
 878:           }
 879: 
 880:         int right = x + w - 1;
 881:         if (rect.x + rect.width < right - 1)
 882:           {
 883:             if (isLast)
 884:               {
 885:                 g.drawLine(rect.x + rect.width - 1, y, right - 1, y);
 886:               }
 887:             else
 888:               {
 889:                 g.drawLine(rect.x + rect.width, y, right - 1, y);
 890:               }
 891:           }
 892:         else
 893:           {
 894:             g.setColor(shadow);
 895:             g.drawLine(x + w - 2, y, x + w - 2, y);
 896:           }
 897: 
 898:         // When in OceanTheme, draw another white line.
 899:         if (isOcean)
 900:           {
 901:             g.setColor(MetalLookAndFeel.getWhite());
 902:             if (isLast)
 903:               {
 904:                 g.drawLine(x, y + 1, rect.x + 1, y + 1);
 905:               }
 906:             else
 907:               {
 908:                 g.drawLine(x, y + 1, rect.x, y + 1);
 909:               }
 910: 
 911:             if (rect.x + rect.width < right - 1)
 912:               {
 913:                 if (isLast)
 914:                   {
 915:                     g.drawLine(rect.x + rect.width - 1, y + 1, right - 1,
 916:                                y + 1);
 917:                   }
 918:                 else
 919:                   {
 920:                     g.drawLine(rect.x + rect.width, y + 1, right - 1, y + 1);
 921:                   }
 922:               }
 923:             else
 924:               {
 925:                 g.setColor(shadow);
 926:                 g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1);
 927:               }
 928:           }
 929:       }
 930:   }
 931: 
 932:   /**
 933:    * Paints the lower 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 paintContentBorderBottomEdge(Graphics g, int tabPlacement,
 944:                                               int selectedIndex, int x, int y,
 945:                                               int w, int h)
 946:   {
 947:     g.setColor(darkShadow);
 948:     
 949:     // If tabs are not placed on BOTTOM, or if the selected tab is not in the
 950:     // run directly below the content or the selected tab is not visible,
 951:     // then we draw an unbroken line.
 952:     Rectangle rect = selectedIndex < 0 ? null :
 953:                                          getTabBounds(selectedIndex, calcRect);
 954:     boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
 955:     Color oceanSelectedBorder =
 956:       UIManager.getColor("TabbedPane.borderHightlightColor");
 957:     if (tabPlacement != BOTTOM || selectedIndex < 0 || rect.y - 1 > h
 958:         || rect.x < x || rect.x > x + w)
 959:       {
 960:         if (isOcean && tabPlacement == BOTTOM)
 961:           {
 962:             g.setColor(oceanSelectedBorder);
 963:           }
 964:         g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
 965:       }
 966:     else
 967:       {
 968:         boolean isLast = isLastTabInRun(selectedIndex);
 969:         if (isOcean)
 970:           {
 971:             g.setColor(oceanSelectedBorder);
 972:           }
 973: 
 974:         int bottom = y + h - 1;
 975:         int right = x + w - 1;
 976:         if (isLast)
 977:           {
 978:             g.drawLine(x, bottom, rect.x, bottom);
 979:           }
 980:         else
 981:           {
 982:             g.drawLine(x, bottom, rect.x - 1, bottom);
 983:           }
 984: 
 985:         if (rect.x + rect.width < x + w - 2)
 986:           {
 987:             if (isLast)
 988:               {
 989:                 g.drawLine(rect.x + rect.width - 1, bottom, right, bottom);
 990:               }
 991:             else
 992:               {
 993:                 g.drawLine(rect.x + rect.width, bottom, right, bottom);
 994:               }
 995:           }
 996:       }
 997:   }
 998: 
 999:   /**
1000:    * Paints the left edge of the content border.
1001:    *
1002:    * @param g the graphics to use for painting
1003:    * @param tabPlacement the tab placement
1004:    * @param selectedIndex the index of the selected tab
1005:    * @param x the upper left coordinate of the content area
1006:    * @param y the upper left coordinate of the content area
1007:    * @param w the width of the content area
1008:    * @param h the height of the content area
1009:    */
1010:   protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
1011:                                             int selectedIndex, int x, int y,
1012:                                             int w, int h)
1013:   {
1014:     boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
1015:     Color oceanSelectedBorder =
1016:       UIManager.getColor("TabbedPane.borderHightlightColor");
1017:     Rectangle rect = selectedIndex < 0 ? null :
1018:       getTabBounds(selectedIndex, calcRect);
1019: 
1020:     if (isOcean)
1021:       {
1022:         g.setColor(oceanSelectedBorder);
1023:       }
1024:     else
1025:       {
1026:         g.setColor(selectHighlight);
1027:       }
1028: 
1029:     // If tabs are not placed on LEFT, or if the selected tab is not in the
1030:     // run directly left to the content or the selected tab is not visible,
1031:     // then we draw an unbroken line.
1032:     if (tabPlacement != LEFT || selectedIndex < 0
1033:         || rect.x + rect.width + 1 < x || rect.y < y || rect.y > y + h)
1034:       {
1035:         g.drawLine(x, y + 1, x, y + h - 2);
1036:         if (isOcean && tabPlacement == LEFT)
1037:           {
1038:             g.setColor(MetalLookAndFeel.getWhite());
1039:             g.drawLine(x, y + 1, x, y + h - 2);
1040:           }       
1041:       }
1042:     else
1043:       {
1044:         g.drawLine(x, y, x, rect.y + 1);
1045:         if (rect.y + rect.height < y + h - 2)
1046:           {
1047:             g.drawLine(x, rect.y + rect.height + 1, x, y + h + 2);
1048:           }
1049:         if (isOcean)
1050:           {
1051:             g.setColor(MetalLookAndFeel.getWhite());
1052:             g.drawLine(x + 1, y + 1, x + 1, rect.y + 1);
1053:             if (rect.y + rect.height < y + h - 2)
1054:               {
1055:                 g.drawLine(x + y, rect.y + rect.height + 1, x + 1, y + h + 2);
1056:               }
1057:           }
1058:       }
1059:     
1060:   }
1061: 
1062:   /**
1063:    * Paints the right edge of the content border.
1064:    *
1065:    * @param g the graphics to use for painting
1066:    * @param tabPlacement the tab placement
1067:    * @param selectedIndex the index of the selected tab
1068:    * @param x the upper left coordinate of the content area
1069:    * @param y the upper left coordinate of the content area
1070:    * @param w the width of the content area
1071:    * @param h the height of the content area
1072:    */
1073:   protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
1074:                                              int selectedIndex, int x, int y,
1075:                                              int w, int h)
1076:   {
1077:     g.setColor(darkShadow);
1078:     Rectangle rect = selectedIndex < 0 ? null :
1079:       getTabBounds(selectedIndex, calcRect);
1080:     boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
1081:     Color oceanSelectedBorder =
1082:       UIManager.getColor("TabbedPane.borderHightlightColor");
1083: 
1084:     // If tabs are not placed on RIGHT, or if the selected tab is not in the
1085:     // run directly right to the content or the selected tab is not visible,
1086:     // then we draw an unbroken line.
1087:     if (tabPlacement != RIGHT || selectedIndex < 0 || rect.x - 1 > w
1088:         || rect.y < y || rect.y > y + h)
1089:       {
1090:         if (isOcean && tabPlacement == RIGHT)
1091:           {
1092:             g.setColor(oceanSelectedBorder);
1093:           }
1094:         g.drawLine(x + w - 1, y, x + w - 1, y + h - 1);
1095:       }
1096:     else
1097:       {
1098:         if (isOcean)
1099:           {
1100:             g.setColor(oceanSelectedBorder);
1101:           }
1102:         g.drawLine(x + w - 1, y, x + w - 1, rect.y);
1103: 
1104:         if (rect.y + rect.height < y + h - 2)
1105:           {
1106:             g.drawLine(x + w - 1, rect.y + rect.height, x + w - 1, y + h - 2);
1107:           }
1108:       }
1109:   }
1110: 
1111:   /**
1112:    * Determines if the specified tab is the last tab in its tab run.
1113:    *
1114:    * @param tabIndex the index of the tab
1115:    *
1116:    * @return if the specified tab is the last tab in its tab run
1117:    */
1118:   private boolean isLastTabInRun(int tabIndex)
1119:   {
1120:     int count = tabPane.getTabCount();
1121:     int run = getRunForTab(count, tabIndex);
1122:     int lastIndex = lastTabInRun(count, run);
1123:     return tabIndex == lastIndex;
1124:   }
1125: 
1126:   /**
1127:    * Returns the background for an unselected tab. This first asks the
1128:    * JTabbedPane for the background at the specified tab index, if this
1129:    * is an UIResource (that means, it is inherited from the JTabbedPane)
1130:    * and the TabbedPane.unselectedBackground UI property is not null,
1131:    * this returns the value of the TabbedPane.unselectedBackground property,
1132:    * otherwise the value returned by the JTabbedPane.
1133:    *
1134:    * @param tabIndex the index of the tab for which we query the background
1135:    *
1136:    * @return the background for an unselected tab
1137:    */
1138:   private Color getUnselectedBackground(int tabIndex)
1139:   {
1140:     Color bg = tabPane.getBackgroundAt(tabIndex);
1141:     Color unselectedBackground =
1142:       UIManager.getColor("TabbedPane.unselectedBackground");
1143:     if (bg instanceof UIResource && unselectedBackground != null)
1144:       bg = unselectedBackground;
1145:     return bg;
1146:   }
1147: }