Source for javax.swing.border.TitledBorder

   1: /* TitledBorder.java -- 
   2:    Copyright (C) 2003, 2004, 2005, 2006,  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.swing.border;
  40: 
  41: import java.awt.Color;
  42: import java.awt.Component;
  43: import java.awt.Dimension;
  44: import java.awt.Font;
  45: import java.awt.FontMetrics;
  46: import java.awt.Graphics;
  47: import java.awt.Insets;
  48: import java.awt.Point;
  49: import java.awt.Rectangle;
  50: 
  51: import javax.swing.SwingUtilities;
  52: import javax.swing.UIManager;
  53: 
  54: 
  55: /**
  56:  * A border that paints a title on top of another border.
  57:  *
  58:  * @author Sascha Brawer (brawer@dandelis.ch)
  59:  */
  60: public class TitledBorder extends AbstractBorder
  61: {
  62:   /**
  63:    * A value for the <code>titlePosition</code> property that vertically
  64:    * positions the title text at the default vertical position, which
  65:    * is in the middle of the top line of the border.
  66:    *
  67:    * @see #getTitlePosition()
  68:    * @see #setTitlePosition(int)
  69:    */
  70:   public static final int DEFAULT_POSITION = 0;
  71: 
  72: 
  73:   /**
  74:    * A value for the <code>titlePosition</code> property that vertically
  75:    * positions the title text above the top line of the border.
  76:    *
  77:    * @see #getTitlePosition()
  78:    * @see #setTitlePosition(int)
  79:    */
  80:   public static final int ABOVE_TOP = 1;
  81: 
  82: 
  83:   /**
  84:    * A value for the <code>titlePosition</code> property that vertically
  85:    * positions the title text at the middle of the top line
  86:    * of the border.
  87:    *
  88:    * @see #getTitlePosition()
  89:    * @see #setTitlePosition(int)
  90:    */
  91:   public static final int TOP = 2;
  92: 
  93: 
  94:   /**
  95:    * A value for the <code>titlePosition</code> property that vertically
  96:    * positions the title text below the top line of the border.
  97:    *
  98:    * @see #getTitlePosition()
  99:    * @see #setTitlePosition(int)
 100:    */
 101:   public static final int BELOW_TOP = 3;
 102: 
 103: 
 104:   /**
 105:    * A value for the <code>titlePosition</code> property that vertically
 106:    * positions the title text above the bottom line of the border.
 107:    *
 108:    * @see #getTitlePosition()
 109:    * @see #setTitlePosition(int)
 110:    */
 111:   public static final int ABOVE_BOTTOM = 4;
 112: 
 113: 
 114:   /**
 115:    * A value for the <code>titlePosition</code> property that vertically
 116:    * positions the title text at the center of the bottom line
 117:    * of the border.
 118:    *
 119:    * @see #getTitlePosition()
 120:    * @see #setTitlePosition(int)
 121:    */
 122:   public static final int BOTTOM = 5;
 123: 
 124: 
 125:   /**
 126:    * A value for the <code>titlePosition</code> property that vertically
 127:    * positions the title text below the bottom line of the border.
 128:    *
 129:    * @see #getTitlePosition()
 130:    * @see #setTitlePosition(int)
 131:    */
 132:   public static final int BELOW_BOTTOM = 6;
 133: 
 134: 
 135:   /**
 136:    * A value for the <code>titleJustification</code> property that
 137:    * horizontally aligns the title text with either the left or the
 138:    * right edge of the border, depending on the orientation of the
 139:    * component nested into the border. If the component orientation
 140:    * is left-to-right, the title text is aligned with the left edge;
 141:    * otherwise, it is aligned with the right edge.  This is the same
 142:    * behavior as with {@link #LEADING}.
 143:    *
 144:    * @see #getTitleJustification()
 145:    * @see #setTitleJustification(int)
 146:    * @see java.awt.ComponentOrientation#isLeftToRight()
 147:    */
 148:   public static final int DEFAULT_JUSTIFICATION = 0;
 149: 
 150: 
 151:   /**
 152:    * A value for the <code>titleJustification</code> property that
 153:    * horizontally aligns the title text with the left-hand edge of
 154:    * the border.
 155:    *
 156:    * @see #getTitleJustification()
 157:    * @see #setTitleJustification(int)
 158:    */
 159:   public static final int LEFT = 1;
 160: 
 161: 
 162:   /**
 163:    * A value for the <code>titleJustification</code> property that
 164:    * horizontally aligns the title text with the center of the border.
 165:    *
 166:    * @see #getTitleJustification()
 167:    * @see #setTitleJustification(int)
 168:    */
 169:   public static final int CENTER = 2;
 170: 
 171: 
 172:   /**
 173:    * A value for the <code>titleJustification</code> property that
 174:    * horizontally aligns the title text with the right-hand edge of
 175:    * the border.
 176:    *
 177:    * @see #getTitleJustification()
 178:    * @see #setTitleJustification(int)
 179:    */
 180:   public static final int RIGHT = 3;
 181: 
 182: 
 183:   /**
 184:    * A value for the <code>titleJustification</code> property that
 185:    * horizontally aligns the title text with either the left or the
 186:    * right edge of the border, depending on the orientation of the
 187:    * component nested into the border. If the component orientation
 188:    * is left-to-right, the title text is aligned with the left edge;
 189:    * otherwise, it is aligned with the right edge. This is the same
 190:    * behavior as with {@link #DEFAULT_JUSTIFICATION}.
 191:    *
 192:    * @see #getTitleJustification()
 193:    * @see #setTitleJustification(int)
 194:    * @see java.awt.ComponentOrientation#isLeftToRight()
 195:    */
 196:   public static final int LEADING = 4;
 197: 
 198: 
 199:   /**
 200:    * A value for the <code>titleJustification</code> property that
 201:    * horizontally aligns the title text with either the right or the
 202:    * left edge of the border, depending on the orientation of the
 203:    * component nested into the border. If the component orientation
 204:    * is left-to-right, the title text is aligned with the right edge;
 205:    * otherwise, it is aligned with the left edge.
 206:    *
 207:    * @see #getTitleJustification()
 208:    * @see #setTitleJustification(int)
 209:    * @see java.awt.ComponentOrientation#isLeftToRight()
 210:    */
 211:   public static final int TRAILING = 5;
 212: 
 213: 
 214:   /**
 215:    * The number of pixels between the inside of {@link #border}
 216:    * and the bordered component.
 217:    */
 218:   protected static final int EDGE_SPACING = 2;
 219: 
 220: 
 221:   /**
 222:    * The number of pixels between the outside of this TitledBorder
 223:    * and the beginning (if left-aligned) or end (if right-aligned)
 224:    * of the title text.
 225:    */
 226:   protected static final int TEXT_INSET_H = 5;
 227: 
 228: 
 229:   /**
 230:    * The number of pixels between the title text and {@link #border}.
 231:    * This value is only relevant if the title text does not intersect
 232:    * {@link #border}. No intersection occurs if {@link #titlePosition}
 233:    * is one of {@link #ABOVE_TOP}, {@link #BELOW_TOP}, {@link #ABOVE_BOTTOM},
 234:    * or {@link #BELOW_BOTTOM}.
 235:    */
 236:   protected static final int TEXT_SPACING = 2;
 237: 
 238: 
 239:   /**
 240:    * Determined using the <code>serialver</code> tool of Apple/Sun JDK 1.3.1
 241:    * on MacOS X 10.1.5.
 242:    */
 243:   static final long serialVersionUID = 8012999415147721601L;
 244:   
 245: 
 246:   /**
 247:    * The title, or <code>null</code> to display no title.
 248:    */
 249:   protected String title;
 250: 
 251: 
 252:   /**
 253:    * The border underneath the title. If this value is
 254:    * <code>null</code>, the border will be retrieved from the {@link
 255:    * javax.swing.UIManager}&#x2019;s defaults table using the key
 256:    * <code>TitledBorder.border</code>.
 257:    */
 258:   protected Border border;
 259: 
 260:   
 261:   /**
 262:    * The vertical position of the title text relative to the border,
 263:    * which is one of {@link #ABOVE_TOP}, {@link #TOP}, {@link
 264:    * #BELOW_TOP}, {@link #ABOVE_BOTTOM}, {@link #BOTTOM}, {@link
 265:    * #BELOW_BOTTOM}, or {@link #DEFAULT_POSITION}.
 266:    */
 267:   protected int titlePosition;
 268: 
 269: 
 270:   /**
 271:    * The horizontal alignment of the title text in relation to the
 272:    * border, which is one of {@link #LEFT}, {@link #CENTER}, {@link
 273:    * #RIGHT}, {@link #LEADING}, {@link #TRAILING}, or {@link
 274:    * #DEFAULT_JUSTIFICATION}.
 275:    */
 276:   protected int titleJustification;
 277: 
 278: 
 279:   /**
 280:    * The font for displaying the title text. If this value is
 281:    * <code>null</code>, the font will be retrieved from the {@link
 282:    * javax.swing.UIManager}&#x2019;s defaults table using the key
 283:    * <code>TitledBorder.font</code>.
 284:    */
 285:   protected Font titleFont;
 286: 
 287: 
 288:   /**
 289:    * The color for displaying the title text. If this value is
 290:    * <code>null</code>, the color will be retrieved from the {@link
 291:    * javax.swing.UIManager}&#x2019;s defaults table using the key
 292:    * <code>TitledBorder.titleColor</code>.
 293:    */
 294:   protected Color titleColor;
 295: 
 296: 
 297:   /**
 298:    * Constructs a TitledBorder given the text of its title.
 299:    *
 300:    * @param title the title text, or <code>null</code> to use no title text.
 301:    */
 302:   public TitledBorder(String title)
 303:   {
 304:     this(/* border */ null,
 305:          title, LEADING, TOP,
 306:          /* titleFont */ null, /* titleColor */ null);
 307:   }
 308: 
 309: 
 310:   /**
 311:    * Constructs an initially untitled TitledBorder given another border.
 312:    *
 313:    * @param border the border underneath the title, or <code>null</code>
 314:    *        to use a default from the current look and feel.
 315:    */
 316:   public TitledBorder(Border border)
 317:   {
 318:     this(border, /* title */ "", LEADING, TOP,
 319:          /* titleFont */ null, /* titleColor */ null);
 320:   }
 321:   
 322: 
 323:   /**
 324:    * Constructs a TitledBorder given its border and title text.
 325:    *
 326:    * @param border the border underneath the title, or <code>null</code>
 327:    *        to use a default from the current look and feel.
 328:    *
 329:    * @param title the title text, or <code>null</code> to use no title
 330:    *        text.
 331:    */
 332:   public TitledBorder(Border border, String title)
 333:   {
 334:     this(border, title, LEADING, TOP,
 335:          /* titleFont */ null, /* titleColor */ null);
 336:   }
 337:   
 338: 
 339:   /**
 340:    * Constructs a TitledBorder given its border, title text, horizontal
 341:    * alignment, and vertical position.
 342:    *
 343:    * @param border the border underneath the title, or <code>null</code>
 344:    *        to use a default from the current look and feel.
 345:    *
 346:    * @param title the title text, or <code>null</code> to use no title
 347:    *        text.
 348:    *
 349:    * @param titleJustification the horizontal alignment of the title
 350:    *        text in relation to the border. The value must be one of
 351:    *        {@link #LEFT}, {@link #CENTER}, {@link #RIGHT}, {@link #LEADING},
 352:    *        {@link #TRAILING}, or {@link #DEFAULT_JUSTIFICATION}.
 353:    
 354:    * @param titlePosition the vertical position of the title text
 355:    *        in relation to the border. The value must be one of
 356:    *        {@link #ABOVE_TOP}, {@link #TOP}, {@link #BELOW_TOP},
 357:    *        {@link #ABOVE_BOTTOM}, {@link #BOTTOM}, {@link #BELOW_BOTTOM},
 358:    *        or {@link #DEFAULT_POSITION}.
 359:    *
 360:    * @throws IllegalArgumentException if <code>titleJustification</code>
 361:    *         or <code>titlePosition</code> have an unsupported value.
 362:    */
 363:   public TitledBorder(Border border, String title, int titleJustification,
 364:                       int titlePosition)
 365:   {
 366:     this(border, title, titleJustification, titlePosition,
 367:          /* titleFont */ null, /* titleColor */ null);
 368:   }
 369:   
 370: 
 371:   /**
 372:    * Constructs a TitledBorder given its border, title text, horizontal
 373:    * alignment, vertical position, and font.
 374:    *
 375:    * @param border the border underneath the title, or <code>null</code>
 376:    *        to use a default from the current look and feel.
 377:    *
 378:    * @param title the title text, or <code>null</code> to use no title
 379:    *        text.
 380:    *
 381:    * @param titleJustification the horizontal alignment of the title
 382:    *        text in relation to the border. The value must be one of
 383:    *        {@link #LEFT}, {@link #CENTER}, {@link #RIGHT}, {@link #LEADING},
 384:    *        {@link #TRAILING}, or {@link #DEFAULT_JUSTIFICATION}.
 385:    *
 386:    * @param titlePosition the vertical position of the title text
 387:    *        in relation to the border. The value must be one of
 388:    *        {@link #ABOVE_TOP}, {@link #TOP}, {@link #BELOW_TOP},
 389:    *        {@link #ABOVE_BOTTOM}, {@link #BOTTOM}, {@link #BELOW_BOTTOM},
 390:    *        or {@link #DEFAULT_POSITION}.
 391:    *
 392:    * @param titleFont the font for the title text, or <code>null</code>
 393:    *        to use a default from the current look and feel.
 394:    *
 395:    * @throws IllegalArgumentException if <code>titleJustification</code>
 396:    *         or <code>titlePosition</code> have an unsupported value.
 397:    */
 398:   public TitledBorder(Border border, String title, int titleJustification,
 399:                       int titlePosition, Font titleFont)
 400:   {
 401:     this(border, title, titleJustification, titlePosition, titleFont,
 402:          /* titleColor */ null);
 403:   }
 404:   
 405: 
 406:   /**
 407:    * Constructs a TitledBorder given its border, title text, horizontal
 408:    * alignment, vertical position, font, and color.
 409:    *
 410:    * @param border the border underneath the title, or <code>null</code>
 411:    *        to use a default from the current look and feel.
 412:    *
 413:    * @param title the title text, or <code>null</code> to use no title
 414:    *        text.
 415:    *
 416:    * @param titleJustification the horizontal alignment of the title
 417:    *        text in relation to the border. The value must be one of
 418:    *        {@link #LEFT}, {@link #CENTER}, {@link #RIGHT}, {@link #LEADING},
 419:    *        {@link #TRAILING}, or {@link #DEFAULT_JUSTIFICATION}.
 420:    *
 421:    * @param titlePosition the vertical position of the title text
 422:    *        in relation to the border. The value must be one of
 423:    *        {@link #ABOVE_TOP}, {@link #TOP}, {@link #BELOW_TOP},
 424:    *        {@link #ABOVE_BOTTOM}, {@link #BOTTOM}, {@link #BELOW_BOTTOM},
 425:    *        or {@link #DEFAULT_POSITION}.
 426:    *
 427:    * @param titleFont the font for the title text, or <code>null</code>
 428:    *        to use a default from the current look and feel.
 429:    *
 430:    * @param titleColor the color for the title text, or <code>null</code>
 431:    *        to use a default from the current look and feel.
 432:    *
 433:    * @throws IllegalArgumentException if <code>titleJustification</code>
 434:    *         or <code>titlePosition</code> have an unsupported value.
 435:    */
 436:   public TitledBorder(Border border, String title, int titleJustification,
 437:                       int titlePosition, Font titleFont, Color titleColor)
 438:   {
 439:     this.border = border;
 440:     this.title = title;
 441: 
 442:     /* Invoking the setter methods ensures that the newly constructed
 443:      * TitledBorder has valid property values.
 444:      */
 445:     setTitleJustification(titleJustification);
 446:     setTitlePosition(titlePosition);
 447: 
 448:     this.titleFont = titleFont;
 449:     this.titleColor = titleColor;
 450:   }
 451:   
 452:   
 453:   /**
 454:    * Paints the border and the title text.
 455:    *
 456:    * @param c the component whose border is to be painted.
 457:    * @param g the graphics for painting.
 458:    * @param x the horizontal position for painting the border.
 459:    * @param y the vertical position for painting the border.
 460:    * @param width the width of the available area for painting the border.
 461:    * @param height the height of the available area for painting the border.
 462:    */
 463:   public void paintBorder(Component c, Graphics  g, 
 464:                           int x, int y, int width, int height)
 465:   {
 466:     Rectangle borderRect = new Rectangle(x + EDGE_SPACING, y + EDGE_SPACING,
 467:                                          width - (EDGE_SPACING * 2),
 468:                                          height - (EDGE_SPACING * 2));
 469:     Point textLoc = new Point();
 470: 
 471:     // Save color and font.
 472:     Color savedColor = g.getColor();
 473:     Font savedFont = g.getFont();
 474: 
 475:     // The font metrics.
 476:     Font font = getFont(c);
 477:     g.setFont(font);
 478:     FontMetrics fm = c.getFontMetrics(font);
 479: 
 480:     layoutBorderWithTitle(c, fm, borderRect, textLoc);
 481:     paintBorderWithTitle(c, g, x, y, width, height, borderRect, textLoc, fm);
 482: 
 483:     g.setColor(getTitleColor());
 484:     g.drawString(getTitle(), textLoc.x, textLoc.y);
 485:     g.setFont(savedFont);
 486:     g.setColor(savedColor);
 487:   }
 488: 
 489:   /**
 490:    * Calculates the bounding box of the inner border and the location of the
 491:    * title string.
 492:    *
 493:    * @param c the component on which to paint the border
 494:    * @param fm the font metrics
 495:    * @param borderRect output parameter, holds the bounding box of the inner
 496:    *        border on method exit
 497:    * @param textLoc output parameter, holds the location of the title text
 498:    *        on method exit
 499:    */
 500:   private void layoutBorderWithTitle(Component c, FontMetrics fm,
 501:                                      Rectangle borderRect,
 502:                                      Point textLoc)
 503:   {
 504:     Border b = getBorder();
 505: 
 506:     // The font metrics.
 507:     int fontHeight = fm.getHeight();
 508:     int fontDescent = fm.getDescent();
 509:     int fontAscent = fm.getAscent();
 510:     int titleWidth = fm.stringWidth(getTitle());
 511: 
 512:     // The base insets.
 513:     Insets insets;
 514:     if (b == null)
 515:       insets = new Insets(0, 0, 0, 0);
 516:     else
 517:       insets = b.getBorderInsets(c);
 518: 
 519:     // The offset of the border rectangle, dependend on the title placement.
 520:     int offset;
 521: 
 522:     // Layout border and text vertically.
 523:     int titlePosition = getTitlePosition();
 524:     switch (titlePosition)
 525:     {
 526:       case ABOVE_BOTTOM:
 527:         textLoc.y = borderRect.y + borderRect.height - insets.bottom
 528:                      - fontDescent - TEXT_SPACING;
 529:         break;
 530:       case BOTTOM:
 531:         borderRect.height -= fontHeight / 2;
 532:         textLoc.y = borderRect.y + borderRect.height - fontDescent
 533:                      + (fontAscent + fontDescent - insets.bottom) / 2;
 534:         break;
 535:       case BELOW_BOTTOM:
 536:         borderRect.height -=  fontHeight;
 537:         textLoc.y = borderRect.y + borderRect.height + fontAscent
 538:                      + TEXT_SPACING;
 539:         break;
 540:       case ABOVE_TOP:
 541:         offset = fontAscent + fontDescent
 542:                  + Math.max(EDGE_SPACING, TEXT_SPACING * 2) - EDGE_SPACING;
 543:         borderRect.y += offset;
 544:         borderRect.height -= offset;
 545:         textLoc.y = borderRect.y - (fontDescent + TEXT_SPACING);
 546:         break;
 547:       case BELOW_TOP:
 548:         textLoc.y = borderRect.y + insets.top + fontAscent + TEXT_SPACING;
 549:         break;
 550:       case TOP:
 551:       case DEFAULT_POSITION:
 552:       default:
 553:         offset = Math.max(0, ((fontAscent / 2) + TEXT_SPACING) - EDGE_SPACING);
 554:         borderRect.y += offset;
 555:         borderRect.height -= offset;
 556:         textLoc.y = borderRect.y - fontDescent
 557:                      + (insets.top + fontAscent + fontDescent) / 2;
 558:         break;
 559:     }
 560: 
 561:     // Layout border and text horizontally.
 562:     int justification = getTitleJustification();
 563:     // Adjust justification for LEADING and TRAILING depending on the direction
 564:     // of the component.
 565:     if (c.getComponentOrientation().isLeftToRight())
 566:       {
 567:         if (justification == LEADING || justification == DEFAULT_JUSTIFICATION)
 568:           justification = LEFT;
 569:         else if (justification == TRAILING)
 570:           justification = RIGHT;
 571:       }
 572:     else
 573:       {
 574:         if (justification == LEADING || justification == DEFAULT_JUSTIFICATION)
 575:           justification = RIGHT;
 576:         else if (justification == TRAILING)
 577:           justification = LEFT;
 578:       }
 579: 
 580:     switch (justification)
 581:     {
 582:       case CENTER:
 583:         textLoc.x = borderRect.x + (borderRect.width - titleWidth) / 2;
 584:         break;
 585:       case RIGHT:
 586:         textLoc.x = borderRect.x + borderRect.width - titleWidth
 587:                      - TEXT_INSET_H - insets.right;
 588:         break;
 589:       case LEFT:
 590:       default:
 591:         textLoc.x = borderRect.x + TEXT_INSET_H + insets.left;
 592:     }
 593:   }
 594: 
 595:   /**
 596:    * Paints the border with the title.
 597:    *
 598:    * @param c the component to paint on
 599:    * @param g the graphics context used for paintin
 600:    * @param x the upper left corner of the whole border
 601:    * @param y the upper left corner of the whole border
 602:    * @param width the width of the whole border
 603:    * @param height the width of the whole border
 604:    * @param borderRect the bounding box of the inner border
 605:    * @param textLoc the location of the border title
 606:    * @param fm the font metrics of the title
 607:    */
 608:   private void paintBorderWithTitle(Component c, Graphics g, int x, int y,
 609:                                     int width, int height,
 610:                                     Rectangle borderRect, Point textLoc,
 611:                                     FontMetrics fm)
 612:   {
 613:     Border b = getBorder();
 614:     int fontDescent = fm.getDescent();
 615:     int fontAscent = fm.getAscent();
 616:     int titleWidth = fm.stringWidth(getTitle());
 617: 
 618:     if (b != null)
 619:       {
 620:         // Paint border in segments, when the title is painted above the
 621:         // border.
 622:         if (((titlePosition == TOP || titlePosition == DEFAULT_POSITION)
 623:             && (borderRect.y > textLoc.y - fontAscent))
 624:             || (titlePosition == BOTTOM
 625:                 && borderRect.y + borderRect.height < textLoc.y + fontDescent))
 626:           {
 627:             Rectangle clip = new Rectangle();
 628:             Rectangle saved = g.getClipBounds();
 629: 
 630:             // Paint border left from the text.
 631:             clip.setBounds(saved);
 632:             SwingUtilities.computeIntersection(x, y, textLoc.x - x - 1,
 633:                                                height, clip);
 634:             if (! clip.isEmpty())
 635:               {
 636:                 g.setClip(clip);
 637:                 b.paintBorder(c, g, borderRect.x, borderRect.y,
 638:                               borderRect.width,
 639:                               borderRect.height);
 640:               }
 641:             // Paint border right from the text.
 642:             clip.setBounds(saved);
 643:             SwingUtilities.computeIntersection(textLoc.x + titleWidth + 1, y,
 644:                 x + width - (textLoc.x + titleWidth + 1), height, clip);
 645:             if (! clip.isEmpty())
 646:               {
 647:                 g.setClip(clip);
 648:                 b.paintBorder(c, g, borderRect.x, borderRect.y,
 649:                               borderRect.width,
 650:                               borderRect.height);
 651:               }
 652: 
 653:             if (titlePosition == TOP || titlePosition == DEFAULT_POSITION)
 654:               {
 655:                 // Paint border below the text.
 656:                 clip.setBounds(saved);
 657:                 SwingUtilities.computeIntersection(textLoc.x - 1,
 658:                                                    textLoc.y + fontDescent,
 659:                                                    titleWidth + 2,
 660:                                                    y + height - textLoc.y - fontDescent,
 661:                                                    clip);
 662:                 if (! clip.isEmpty())
 663:                   {
 664:                     g.setClip(clip);
 665:                     b.paintBorder(c, g, borderRect.x, borderRect.y,
 666:                                   borderRect.width,
 667:                                   borderRect.height);
 668:                   }
 669:                     
 670:               }
 671:             else
 672:               {
 673:                 // Paint border above the text.
 674:                 clip.setBounds(saved);
 675:                 SwingUtilities.computeIntersection(textLoc.x - 1, y,
 676:                                                    titleWidth + 2,
 677:                                                    textLoc.y - fontDescent - y,
 678:                                                    clip);
 679:                 if (! clip.isEmpty())
 680:                   {
 681:                     g.setClip(clip);
 682:                     b.paintBorder(c, g, borderRect.x, borderRect.y,
 683:                                   borderRect.width,
 684:                                   borderRect.height);
 685:                   }
 686:                     
 687:               }
 688:             g.setClip(saved);
 689:           }
 690:         else
 691:           {
 692:             b.paintBorder(c, g, borderRect.x, borderRect.y, borderRect.width,
 693:                           borderRect.height);
 694:           }
 695:       }
 696:   }
 697: 
 698:   /**
 699:    * Measures the width of this border.
 700:    *
 701:    * @param c the component whose border is to be measured.
 702:    *
 703:    * @return an Insets object whose <code>left</code>, <code>right</code>,
 704:    *         <code>top</code> and <code>bottom</code> fields indicate the
 705:    *         width of the border at the respective edge.
 706:    *
 707:    * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
 708:    */
 709:   public Insets getBorderInsets(Component c)
 710:   {
 711:     return getBorderInsets(c, new Insets(0, 0, 0, 0));
 712:   }
 713:   
 714: 
 715:   /**
 716:    * Measures the width of this border, storing the results into a
 717:    * pre-existing Insets object.
 718:    *
 719:    * @param insets an Insets object for holding the result values.
 720:    *        After invoking this method, the <code>left</code>,
 721:    *        <code>right</code>, <code>top</code> and
 722:    *        <code>bottom</code> fields indicate the width of the
 723:    *        border at the respective edge.
 724:    *
 725:    * @return the same object that was passed for <code>insets</code>.
 726:    *
 727:    * @see #getBorderInsets(Component)
 728:    */
 729:   public Insets getBorderInsets(Component c, Insets insets)
 730:   {
 731:     // Initialize insets with the insets from our border.
 732:     Border border = getBorder();
 733:     if (border != null)
 734:       {
 735:         if (border instanceof AbstractBorder)
 736:           {
 737:             AbstractBorder aBorder = (AbstractBorder) border;
 738:             aBorder.getBorderInsets(c, insets);
 739:           }
 740:         else
 741:           {
 742:             Insets i = border.getBorderInsets(c);
 743:             insets.top = i.top;
 744:             insets.bottom = i.bottom;
 745:             insets.left = i.left;
 746:             insets.right = i.right;
 747:           }
 748:       }
 749:     else
 750:       {
 751:         insets.top = 0;
 752:         insets.bottom = 0;
 753:         insets.left = 0;
 754:         insets.right = 0;
 755:       }
 756: 
 757:     // Add spacing.
 758:     insets.top += EDGE_SPACING + TEXT_SPACING;
 759:     insets.bottom += EDGE_SPACING + TEXT_SPACING;
 760:     insets.left += EDGE_SPACING + TEXT_SPACING;
 761:     insets.right += EDGE_SPACING + TEXT_SPACING;
 762: 
 763:     String title = getTitle();
 764:     if (c != null && title != null && !title.equals(""))
 765:       {
 766:         Font font = getFont(c);
 767:         FontMetrics fm = c.getFontMetrics(font);
 768:         int ascent = fm.getAscent();
 769:         int descent = fm.getDescent();
 770:         int height = fm.getHeight();
 771:         switch (getTitlePosition())
 772:         {
 773:           case ABOVE_BOTTOM:
 774:             insets.bottom += ascent + descent + TEXT_SPACING;
 775:             break;
 776:           case BOTTOM:
 777:             insets.bottom += ascent + descent;
 778:             break;
 779:           case BELOW_BOTTOM:
 780:             insets.bottom += height;
 781:             break;
 782:           case ABOVE_TOP:
 783:             insets.top += ascent + descent +
 784:                           Math.max(EDGE_SPACING, TEXT_SPACING * 2)
 785:                           - EDGE_SPACING;
 786:             break;
 787:           case BELOW_TOP:
 788:             insets.top += ascent + descent + TEXT_SPACING;
 789:             break;
 790:           case TOP:
 791:           case DEFAULT_POSITION:
 792:           default:
 793:             insets.top += ascent + descent;
 794:         }
 795:       }
 796:     return insets;
 797:   }
 798:   
 799:   
 800:   /**
 801:    * Returns <code>false</code>, indicating that there are pixels inside
 802:    * the area of this border where the background shines through.
 803:    *
 804:    * @return <code>false</code>.
 805:    */
 806:   public boolean isBorderOpaque()
 807:   {
 808:     /* Note that the AbstractBorder.isBorderOpaque would also return
 809:      * false, so there is actually no need to override the inherited
 810:      * implementation. However, GNU Classpath strives for exact
 811:      * compatibility with the Sun reference implementation, which
 812:      * overrides isBorderOpaque for unknown reasons.
 813:      */
 814:     return false;
 815:   }
 816: 
 817: 
 818:   /**
 819:    * Returns the text of the title.
 820:    *
 821:    * @return the title text, or <code>null</code> if no title is
 822:    *         displayed.
 823:    */
 824:   public String getTitle()
 825:   {
 826:     return title;
 827:   }
 828: 
 829: 
 830:   /**
 831:    * Retrieves the border underneath the title. If no border has been
 832:    * set, or if it has been set to<code>null</code>, the current
 833:    * {@link javax.swing.LookAndFeel} will be asked for a border
 834:    * using the key <code>TitledBorder.border</code>.
 835:    *
 836:    * @return a border, or <code>null</code> if the current LookAndFeel
 837:    *         does not provide a border for the key
 838:    *         <code>TitledBorder.border</code>.
 839:    *
 840:    * @see javax.swing.UIManager#getBorder(Object)
 841:    */
 842:   public Border getBorder()
 843:   {
 844:     if (border != null)
 845:       return border;
 846: 
 847:     return UIManager.getBorder("TitledBorder.border");
 848:   }
 849: 
 850: 
 851:   /**
 852:    * Returns the vertical position of the title text in relation
 853:    * to the border.
 854:    *
 855:    * @return one of the values {@link #ABOVE_TOP}, {@link #TOP},
 856:    *         {@link #BELOW_TOP}, {@link #ABOVE_BOTTOM}, {@link #BOTTOM},
 857:    *         {@link #BELOW_BOTTOM}, or {@link #DEFAULT_POSITION}.
 858:    */
 859:   public int getTitlePosition()
 860:   {
 861:     return titlePosition;
 862:   }
 863: 
 864: 
 865:   /**
 866:    * Returns the horizontal alignment of the title text in relation to
 867:    * the border.
 868:    *
 869:    * @return one of the values {@link #LEFT}, {@link #CENTER}, {@link
 870:    *         #RIGHT}, {@link #LEADING}, {@link #TRAILING}, or {@link
 871:    *         #DEFAULT_JUSTIFICATION}.
 872:    */
 873:   public int getTitleJustification()
 874:   {
 875:     return titleJustification;
 876:   }
 877: 
 878: 
 879:   /**
 880:    * Retrieves the font for displaying the title text. If no font has
 881:    * been set, or if it has been set to<code>null</code>, the current
 882:    * {@link javax.swing.LookAndFeel} will be asked for a font
 883:    * using the key <code>TitledBorder.font</code>.
 884:    *
 885:    * @return a font, or <code>null</code> if the current LookAndFeel
 886:    *         does not provide a font for the key
 887:    *         <code>TitledBorder.font</code>.
 888:    *
 889:    * @see javax.swing.UIManager#getFont(Object)
 890:    */
 891:   public Font getTitleFont()
 892:   {
 893:     if (titleFont != null)
 894:       return titleFont;
 895: 
 896:     return UIManager.getFont("TitledBorder.font");
 897:   }
 898: 
 899: 
 900:   /**
 901:    * Retrieves the color for displaying the title text. If no color has
 902:    * been set, or if it has been set to<code>null</code>, the current
 903:    * {@link javax.swing.LookAndFeel} will be asked for a color
 904:    * using the key <code>TitledBorder.titleColor</code>.
 905:    *
 906:    * @return a color, or <code>null</code> if the current LookAndFeel
 907:    *         does not provide a color for the key
 908:    *         <code>TitledBorder.titleColor</code>.
 909:    *
 910:    * @see javax.swing.UIManager#getColor(Object)
 911:    */
 912:   public Color getTitleColor()
 913:   {
 914:     if (titleColor != null)
 915:       return titleColor;
 916: 
 917:     return UIManager.getColor("TitledBorder.titleColor");
 918:   }
 919: 
 920: 
 921:   /**
 922:    * Sets the text of the title.
 923:    *
 924:    * @param title the new title text, or <code>null</code> for displaying
 925:    *        no text at all.
 926:    */
 927:   public void setTitle(String title)
 928:   {
 929:     // Swing borders are not JavaBeans, thus no need to fire an event.
 930:     this.title = title;
 931:   }
 932: 
 933: 
 934:   /**
 935:    * Sets the border underneath the title.
 936:    *
 937:    * @param border a border, or <code>null</code> to use the
 938:    *        border that is supplied by the current LookAndFeel.
 939:    *
 940:    * @see #getBorder()
 941:    */
 942:   public void setBorder(Border border)
 943:   {
 944:     // Swing borders are not JavaBeans, thus no need to fire an event.
 945:     this.border = border;
 946:   }
 947: 
 948: 
 949:   /**
 950:    * Sets the vertical position of the title text in relation
 951:    * to the border.
 952:    *
 953:    * @param titlePosition one of the values {@link #ABOVE_TOP},
 954:    *        {@link #TOP}, {@link #BELOW_TOP}, {@link #ABOVE_BOTTOM},
 955:    *        {@link #BOTTOM}, {@link #BELOW_BOTTOM},
 956:    *        or {@link #DEFAULT_POSITION}.
 957:    *
 958:    * @throws IllegalArgumentException if an unsupported value is passed
 959:    *         for <code>titlePosition</code>.
 960:    */
 961:   public void setTitlePosition(int titlePosition)
 962:   {
 963:     if ((titlePosition < DEFAULT_POSITION) || (titlePosition > BELOW_BOTTOM))
 964:       throw new IllegalArgumentException(titlePosition 
 965:           + " is not a valid title position.");
 966: 
 967:     // Swing borders are not JavaBeans, thus no need to fire an event.
 968:     this.titlePosition = titlePosition;
 969:   }
 970: 
 971: 
 972:   /**
 973:    * Sets the horizontal alignment of the title text in relation to the border.
 974:    *
 975:    * @param titleJustification the new alignment, which must be one of
 976:    *        {@link #LEFT}, {@link #CENTER}, {@link #RIGHT}, {@link #LEADING},
 977:    *        {@link #TRAILING}, or {@link #DEFAULT_JUSTIFICATION}.
 978:    *
 979:    * @throws IllegalArgumentException if an unsupported value is passed
 980:    *         for <code>titleJustification</code>.
 981:    */
 982:   public void setTitleJustification(int titleJustification)
 983:   {
 984:     if ((titleJustification < DEFAULT_JUSTIFICATION)
 985:         || (titleJustification > TRAILING))
 986:       throw new IllegalArgumentException(titleJustification 
 987:           + " is not a valid title justification.");
 988: 
 989:     // Swing borders are not JavaBeans, thus no need to fire an event.
 990:     this.titleJustification = titleJustification;
 991:   }
 992: 
 993: 
 994:   /**
 995:    * Sets the font for displaying the title text.
 996:    *
 997:    * @param titleFont the font, or <code>null</code> to use the font
 998:    *        provided by the current {@link javax.swing.LookAndFeel}.
 999:    *
1000:    * @see #getTitleFont()
1001:    */
1002:   public void setTitleFont(Font titleFont)
1003:   {
1004:     // Swing borders are not JavaBeans, thus no need to fire an event.
1005:     this.titleFont = titleFont;
1006:   }
1007: 
1008: 
1009:   /**
1010:    * Sets the color for displaying the title text.
1011:    *
1012:    * @param titleColor the color, or <code>null</code> to use the color
1013:    *        provided by the current {@link javax.swing.LookAndFeel}.
1014:    *
1015:    * @see #getTitleColor()
1016:    */
1017:   public void setTitleColor(Color titleColor)
1018:   {
1019:     // Swing borders are not JavaBeans, thus no need to fire an event.
1020:     this.titleColor = titleColor;
1021:   }
1022: 
1023: 
1024:   /**
1025:    * Calculates the minimum size needed for displaying the border
1026:    * and its title.
1027:    *
1028:    * @param c the Component for which this TitledBorder constitutes
1029:    *        a border.
1030:    *        
1031:    * @return The minimum size.
1032:    */
1033:   public Dimension getMinimumSize(Component c)
1034:   {
1035:     Insets i = getBorderInsets(c);
1036:     Dimension minSize = new Dimension(i.left + i.right, i.top + i.bottom);
1037:     Font font = getFont(c);
1038:     FontMetrics fm = c.getFontMetrics(font);
1039:     int titleWidth = fm.stringWidth(getTitle());
1040:     switch (getTitlePosition())
1041:     {
1042:       case ABOVE_TOP:
1043:       case BELOW_BOTTOM:
1044:         minSize.width = Math.max(minSize.width, titleWidth);
1045:         break;
1046:       case BELOW_TOP:
1047:       case ABOVE_BOTTOM:
1048:       case TOP:
1049:       case BOTTOM:
1050:       case DEFAULT_POSITION:
1051:       default:
1052:         minSize.width += titleWidth;
1053:     }
1054:     return minSize;
1055:   }
1056: 
1057: 
1058:   /**
1059:    * Returns the font that is used for displaying the title text for
1060:    * a given Component.
1061:    *
1062:    * @param c the Component for which this TitledBorder is the border.
1063:    *
1064:    * @return The font returned by {@link #getTitleFont()}, or a fallback
1065:    *         if {@link #getTitleFont()} returned <code>null</code>.
1066:    */
1067:   protected Font getFont(Component c)
1068:   {
1069:     Font f;
1070: 
1071:     f = getTitleFont();
1072:     if (f != null)
1073:       return f;
1074: 
1075:     return new Font("Dialog", Font.PLAIN, 12);
1076:   }
1077: 
1078: }