Source for javax.swing.border.TitledBorder

   1: /* TitledBorder.java -- 
   2:    Copyright (C) 2003, 2004  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.Shape;
  49: 
  50: import javax.swing.UIManager;
  51: 
  52: 
  53: /**
  54:  * A border that paints a title on top of another border.
  55:  *
  56:  * @author Sascha Brawer (brawer@dandelis.ch)
  57:  */
  58: public class TitledBorder
  59:   extends AbstractBorder
  60: {
  61:   /**
  62:    * A value for the <code>titlePosition</code> property that vertically
  63:    * positions the title text at the default vertical position, which
  64:    * is in the middle of the top line of the border.
  65:    *
  66:    * @see #getTitlePosition()
  67:    * @see #setTitlePosition(int)
  68:    */
  69:   public static final int DEFAULT_POSITION = 0;
  70: 
  71: 
  72:   /**
  73:    * A value for the <code>titlePosition</code> property that vertically
  74:    * positions the title text above the top line of the border.
  75:    *
  76:    * @see #getTitlePosition()
  77:    * @see #setTitlePosition(int)
  78:    */
  79:   public static final int ABOVE_TOP = 1;
  80: 
  81: 
  82:   /**
  83:    * A value for the <code>titlePosition</code> property that vertically
  84:    * positions the title text at the middle of the top line
  85:    * of the border.
  86:    *
  87:    * @see #getTitlePosition()
  88:    * @see #setTitlePosition(int)
  89:    */
  90:   public static final int TOP = 2;
  91: 
  92: 
  93:   /**
  94:    * A value for the <code>titlePosition</code> property that vertically
  95:    * positions the title text below the top line of the border.
  96:    *
  97:    * @see #getTitlePosition()
  98:    * @see #setTitlePosition(int)
  99:    */
 100:   public static final int BELOW_TOP = 3;
 101: 
 102: 
 103:   /**
 104:    * A value for the <code>titlePosition</code> property that vertically
 105:    * positions the title text above the bottom line of the border.
 106:    *
 107:    * @see #getTitlePosition()
 108:    * @see #setTitlePosition(int)
 109:    */
 110:   public static final int ABOVE_BOTTOM = 4;
 111: 
 112: 
 113:   /**
 114:    * A value for the <code>titlePosition</code> property that vertically
 115:    * positions the title text at the center of the bottom line
 116:    * of the border.
 117:    *
 118:    * @see #getTitlePosition()
 119:    * @see #setTitlePosition(int)
 120:    */
 121:   public static final int BOTTOM = 5;
 122: 
 123: 
 124:   /**
 125:    * A value for the <code>titlePosition</code> property that vertically
 126:    * positions the title text below the bottom line of the border.
 127:    *
 128:    * @see #getTitlePosition()
 129:    * @see #setTitlePosition(int)
 130:    */
 131:   public static final int BELOW_BOTTOM = 6;
 132: 
 133: 
 134:   /**
 135:    * A value for the <code>titleJustification</code> property that
 136:    * horizontally aligns the title text with either the left or the
 137:    * right edge of the border, depending on the orientation of the
 138:    * component nested into the border. If the component orientation
 139:    * is left-to-right, the title text is aligned with the left edge;
 140:    * otherwise, it is aligned with the right edge.  This is the same
 141:    * behavior as with {@link #LEADING}.
 142:    *
 143:    * @see #getTitleJustification()
 144:    * @see #setTitleJustification(int)
 145:    * @see java.awt.ComponentOrientation#isLeftToRight()
 146:    */
 147:   public static final int DEFAULT_JUSTIFICATION = 0;
 148: 
 149: 
 150:   /**
 151:    * A value for the <code>titleJustification</code> property that
 152:    * horizontally aligns the title text with the left-hand edge of
 153:    * the border.
 154:    *
 155:    * @see #getTitleJustification()
 156:    * @see #setTitleJustification(int)
 157:    */
 158:   public static final int LEFT = 1;
 159: 
 160: 
 161:   /**
 162:    * A value for the <code>titleJustification</code> property that
 163:    * horizontally aligns the title text with the center of the border.
 164:    *
 165:    * @see #getTitleJustification()
 166:    * @see #setTitleJustification(int)
 167:    */
 168:   public static final int CENTER = 2;
 169: 
 170: 
 171:   /**
 172:    * A value for the <code>titleJustification</code> property that
 173:    * horizontally aligns the title text with the right-hand edge of
 174:    * the border.
 175:    *
 176:    * @see #getTitleJustification()
 177:    * @see #setTitleJustification(int)
 178:    */
 179:   public static final int RIGHT = 3;
 180: 
 181: 
 182:   /**
 183:    * A value for the <code>titleJustification</code> property that
 184:    * horizontally aligns the title text with either the left or the
 185:    * right edge of the border, depending on the orientation of the
 186:    * component nested into the border. If the component orientation
 187:    * is left-to-right, the title text is aligned with the left edge;
 188:    * otherwise, it is aligned with the right edge. This is the same
 189:    * behavior as with {@link #DEFAULT_JUSTIFICATION}.
 190:    *
 191:    * @see #getTitleJustification()
 192:    * @see #setTitleJustification(int)
 193:    * @see java.awt.ComponentOrientation#isLeftToRight()
 194:    */
 195:   public static final int LEADING = 4;
 196: 
 197: 
 198:   /**
 199:    * A value for the <code>titleJustification</code> property that
 200:    * horizontally aligns the title text with either the right or the
 201:    * left edge of the border, depending on the orientation of the
 202:    * component nested into the border. If the component orientation
 203:    * is left-to-right, the title text is aligned with the right edge;
 204:    * otherwise, it is aligned with the left edge.
 205:    *
 206:    * @see #getTitleJustification()
 207:    * @see #setTitleJustification(int)
 208:    * @see java.awt.ComponentOrientation#isLeftToRight()
 209:    */
 210:   public static final int TRAILING = 5;
 211: 
 212: 
 213:   /**
 214:    * The number of pixels between the inside of {@link #border}
 215:    * and the bordered component.
 216:    */
 217:   protected static final int EDGE_SPACING = 2;
 218: 
 219: 
 220:   /**
 221:    * The number of pixels between the outside of this TitledBorder
 222:    * and the beginning (if left-aligned) or end (if right-aligned)
 223:    * of the title text.
 224:    */
 225:   protected static final int TEXT_INSET_H = 5;
 226: 
 227: 
 228:   /**
 229:    * The number of pixels between the title text and {@link #border}.
 230:    * This value is only relevant if the title text does not intersect
 231:    * {@link #border}. No intersection occurs if {@link #titlePosition}
 232:    * is one of {@link #ABOVE_TOP}, {@link #BELOW_TOP}, {@link #ABOVE_BOTTOM},
 233:    * or {@link #BELOW_BOTTOM}.
 234:    */
 235:   protected static final int TEXT_SPACING = 2;
 236: 
 237: 
 238:   /**
 239:    * Determined using the <code>serialver</code> tool of Apple/Sun JDK 1.3.1
 240:    * on MacOS X 10.1.5.
 241:    */
 242:   static final long serialVersionUID = 8012999415147721601L;
 243:   
 244: 
 245:   /**
 246:    * The title, or <code>null</code> to display no title.
 247:    */
 248:   protected String title;
 249: 
 250: 
 251:   /**
 252:    * The border underneath the title. If this value is
 253:    * <code>null</code>, the border will be retrieved from the {@link
 254:    * javax.swing.UIManager}&#x2019;s defaults table using the key
 255:    * <code>TitledBorder.border</code>.
 256:    */
 257:   protected Border border;
 258: 
 259:   
 260:   /**
 261:    * The vertical position of the title text relative to the border,
 262:    * which is one of {@link #ABOVE_TOP}, {@link #TOP}, {@link
 263:    * #BELOW_TOP}, {@link #ABOVE_BOTTOM}, {@link #BOTTOM}, {@link
 264:    * #BELOW_BOTTOM}, or {@link #DEFAULT_POSITION}.
 265:    */
 266:   protected int titlePosition;
 267: 
 268: 
 269:   /**
 270:    * The horizontal alignment of the title text in relation to the
 271:    * border, which is one of {@link #LEFT}, {@link #CENTER}, {@link
 272:    * #RIGHT}, {@link #LEADING}, {@link #TRAILING}, or {@link
 273:    * #DEFAULT_JUSTIFICATION}.
 274:    */
 275:   protected int titleJustification;
 276: 
 277: 
 278:   /**
 279:    * The font for displaying the title text. If this value is
 280:    * <code>null</code>, the font will be retrieved from the {@link
 281:    * javax.swing.UIManager}&#x2019;s defaults table using the key
 282:    * <code>TitledBorder.font</code>.
 283:    */
 284:   protected Font titleFont;
 285: 
 286: 
 287:   /**
 288:    * The color for displaying the title text. If this value is
 289:    * <code>null</code>, the color will be retrieved from the {@link
 290:    * javax.swing.UIManager}&#x2019;s defaults table using the key
 291:    * <code>TitledBorder.titleColor</code>.
 292:    */
 293:   protected Color titleColor;
 294: 
 295: 
 296:   /**
 297:    * Constructs a TitledBorder given the text of its title.
 298:    *
 299:    * @param title the title text, or <code>null</code> to use no title text.
 300:    */
 301:   public TitledBorder(String title)
 302:   {
 303:     this(/* border */ null,
 304:          title, DEFAULT_JUSTIFICATION, DEFAULT_POSITION,
 305:          /* titleFont */ null, /* titleColor */ null);
 306:   }
 307: 
 308: 
 309:   /**
 310:    * Constructs an initially untitled TitledBorder given another border.
 311:    *
 312:    * @param border the border underneath the title, or <code>null</code>
 313:    *        to use a default from the current look and feel.
 314:    */
 315:   public TitledBorder(Border border)
 316:   {
 317:     this(border, /* title */ "", DEFAULT_JUSTIFICATION, DEFAULT_POSITION,
 318:          /* titleFont */ null, /* titleColor */ null);
 319:   }
 320:   
 321: 
 322:   /**
 323:    * Constructs a TitledBorder given its border and title text.
 324:    *
 325:    * @param border the border underneath the title, or <code>null</code>
 326:    *        to use a default from the current look and feel.
 327:    *
 328:    * @param title the title text, or <code>null</code> to use no title
 329:    *        text.
 330:    */
 331:   public TitledBorder(Border border, String title)
 332:   {
 333:     this(border, title, DEFAULT_JUSTIFICATION, DEFAULT_POSITION,
 334:          /* titleFont */ null, /* titleColor */ null);
 335:   }
 336:   
 337: 
 338:   /**
 339:    * Constructs a TitledBorder given its border, title text, horizontal
 340:    * alignment, and vertical position.
 341:    *
 342:    * @param border the border underneath the title, or <code>null</code>
 343:    *        to use a default from the current look and feel.
 344:    *
 345:    * @param title the title text, or <code>null</code> to use no title
 346:    *        text.
 347:    *
 348:    * @param titleJustification the horizontal alignment of the title
 349:    *        text in relation to the border. The value must be one of
 350:    *        {@link #LEFT}, {@link #CENTER}, {@link #RIGHT}, {@link #LEADING},
 351:    *        {@link #TRAILING}, or {@link #DEFAULT_JUSTIFICATION}.
 352:    
 353:    * @param titlePosition the vertical position of the title text
 354:    *        in relation to the border. The value must be one of
 355:    *        {@link #ABOVE_TOP}, {@link #TOP}, {@link #BELOW_TOP},
 356:    *        {@link #ABOVE_BOTTOM}, {@link #BOTTOM}, {@link #BELOW_BOTTOM},
 357:    *        or {@link #DEFAULT_POSITION}.
 358:    *
 359:    * @throws IllegalArgumentException if <code>titleJustification</code>
 360:    *         or <code>titlePosition</code> have an unsupported value.
 361:    */
 362:   public TitledBorder(Border border, String title, int titleJustification,
 363:                       int titlePosition)
 364:   {
 365:     this(border, title, titleJustification, titlePosition,
 366:          /* titleFont */ null, /* titleColor */ null);
 367:   }
 368:   
 369: 
 370:   /**
 371:    * Constructs a TitledBorder given its border, title text, horizontal
 372:    * alignment, vertical position, and font.
 373:    *
 374:    * @param border the border underneath the title, or <code>null</code>
 375:    *        to use a default from the current look and feel.
 376:    *
 377:    * @param title the title text, or <code>null</code> to use no title
 378:    *        text.
 379:    *
 380:    * @param titleJustification the horizontal alignment of the title
 381:    *        text in relation to the border. The value must be one of
 382:    *        {@link #LEFT}, {@link #CENTER}, {@link #RIGHT}, {@link #LEADING},
 383:    *        {@link #TRAILING}, or {@link #DEFAULT_JUSTIFICATION}.
 384:    *
 385:    * @param titlePosition the vertical position of the title text
 386:    *        in relation to the border. The value must be one of
 387:    *        {@link #ABOVE_TOP}, {@link #TOP}, {@link #BELOW_TOP},
 388:    *        {@link #ABOVE_BOTTOM}, {@link #BOTTOM}, {@link #BELOW_BOTTOM},
 389:    *        or {@link #DEFAULT_POSITION}.
 390:    *
 391:    * @param titleFont the font for the title text, or <code>null</code>
 392:    *        to use a default from the current look and feel.
 393:    *
 394:    * @throws IllegalArgumentException if <code>titleJustification</code>
 395:    *         or <code>titlePosition</code> have an unsupported value.
 396:    */
 397:   public TitledBorder(Border border, String title, int titleJustification,
 398:                       int titlePosition, Font titleFont)
 399:   {
 400:     this(border, title, titleJustification, titlePosition, titleFont,
 401:          /* titleColor */ null);
 402:   }
 403:   
 404: 
 405:   /**
 406:    * Constructs a TitledBorder given its border, title text, horizontal
 407:    * alignment, vertical position, font, and color.
 408:    *
 409:    * @param border the border underneath the title, or <code>null</code>
 410:    *        to use a default from the current look and feel.
 411:    *
 412:    * @param title the title text, or <code>null</code> to use no title
 413:    *        text.
 414:    *
 415:    * @param titleJustification the horizontal alignment of the title
 416:    *        text in relation to the border. The value must be one of
 417:    *        {@link #LEFT}, {@link #CENTER}, {@link #RIGHT}, {@link #LEADING},
 418:    *        {@link #TRAILING}, or {@link #DEFAULT_JUSTIFICATION}.
 419:    *
 420:    * @param titlePosition the vertical position of the title text
 421:    *        in relation to the border. The value must be one of
 422:    *        {@link #ABOVE_TOP}, {@link #TOP}, {@link #BELOW_TOP},
 423:    *        {@link #ABOVE_BOTTOM}, {@link #BOTTOM}, {@link #BELOW_BOTTOM},
 424:    *        or {@link #DEFAULT_POSITION}.
 425:    *
 426:    * @param titleFont the font for the title text, or <code>null</code>
 427:    *        to use a default from the current look and feel.
 428:    *
 429:    * @param titleColor the color for the title text, or <code>null</code>
 430:    *        to use a default from the current look and feel.
 431:    *
 432:    * @throws IllegalArgumentException if <code>titleJustification</code>
 433:    *         or <code>titlePosition</code> have an unsupported value.
 434:    */
 435:   public TitledBorder(Border border, String title, int titleJustification,
 436:                       int titlePosition, Font titleFont, Color titleColor)
 437:   {
 438:     this.border = border;
 439:     this.title = title;
 440: 
 441:     /* Invoking the setter methods ensures that the newly constructed
 442:      * TitledBorder has valid property values.
 443:      */
 444:     setTitleJustification(titleJustification);
 445:     setTitlePosition(titlePosition);
 446: 
 447:     this.titleFont = titleFont;
 448:     this.titleColor = titleColor;
 449:   }
 450:   
 451:   
 452:   /**
 453:    * Paints the border and the title text.
 454:    *
 455:    * @param c the component whose border is to be painted.
 456:    * @param g the graphics for painting.
 457:    * @param x the horizontal position for painting the border.
 458:    * @param y the vertical position for painting the border.
 459:    * @param width the width of the available area for painting the border.
 460:    * @param height the height of the available area for painting the border.
 461:    */
 462:   public void paintBorder(Component c, Graphics  g, 
 463:                           int x, int y, int width, int height)
 464:   {
 465:     Measurements mes = getMeasurements(c);
 466:     Font oldFont = g.getFont();
 467:     Color oldColor = g.getColor();
 468: 
 469:     /**
 470:      * A local helper class for painting the border without changing
 471:      * any pixels inside the rectangle of the title text.
 472:      */
 473:     class BorderPainter
 474:     {
 475:       private Component c;
 476:       private Border b;
 477:       private int x, y, width, height;
 478: 
 479:       /**
 480:        * Constructs a BorderPainter.
 481:        *
 482:        * @param c the component whose border is being painted.
 483:        * @param b the border object.
 484:        * @param x the x coordinate of the rectangle delimiting the border.
 485:        * @param y the y coordinate of the rectangle delimiting the border.
 486:        * @param width the width of the rectangle delimiting the border.
 487:        * @param height the width of the rectangle delimiting the border.
 488:        */
 489:       public BorderPainter(Component c, Border b,
 490:                            int x, int y, int width, int height)
 491:       {
 492:         this.c = c;
 493:         this.b = b;
 494:         this.x = x;
 495:         this.y = y;
 496:         this.width = width;
 497:         this.height = height;
 498:       }
 499: 
 500: 
 501:       /**
 502:        * Paints the entire border.
 503:        */
 504:       public void paint(Graphics g)
 505:       {
 506:         if (b != null)
 507:           b.paintBorder(c, g, x, y, width - 1, height - 1);
 508:       }
 509: 
 510: 
 511:       /**
 512:        * Paints the border, clipping the drawing operation to a
 513:        * given rectangular area.
 514:        */
 515:       private void paint(Graphics g,
 516:                          int clipX, int clipY, int clipWidth, int clipHeight)
 517:       {
 518:         Shape oldClip = g.getClip();
 519:         try
 520:         {
 521:           g.clipRect(clipX, clipY, clipWidth, clipHeight);
 522:           paint(g);
 523:         }
 524:         finally
 525:         {
 526:           g.setClip(oldClip);
 527:         }
 528:       }
 529: 
 530: 
 531:       /**
 532:        * Paints the border without affecting a given rectangular area.
 533:        * This is used for painting the border without drawing anything
 534:        * underneath the title text.
 535:        *
 536:        * <p>Since we do not want to introduce unnecessary dependencies
 537:        * on Java 2D, we perform the clipping without constructive geometry
 538:        * (provided by java.awt.geom.Area). Instead, the border&#x2019;s
 539:        * bounding rectangle is split into smaller parts, which are then
 540:        * clipped and painted individually.:
 541:        *
 542:        * <p><pre>
 543:        *    +--------------------+          +--------------------+
 544:        *    |                    |          |        1           |
 545:        *    |   +--------+       |          +---+--------+-------+
 546:        *    |   | hole   |       |  |====>  | 2 | hole   |   3   |
 547:        *    |   +--------+       |          |---+--------+-------+
 548:        *    |                    |          |        4           |
 549:        *    +--------------------+          +--------------------+</pre>
 550:        *
 551:        */
 552:       public void paintExcept(Graphics g,
 553:                               int holeX, int holeY, int holeWidth, int holeHeight)
 554:       {
 555:         int stripeHeight;
 556: 
 557:         stripeHeight = holeY - y;
 558:         if (stripeHeight > 0)
 559:           paint(g, x, y, width, stripeHeight);   // patch #1 in the image above
 560: 
 561:         stripeHeight = holeHeight;
 562:         if (stripeHeight > 0)
 563:         {
 564:           paint(g, x, holeY, holeX - x, stripeHeight);  // patches #2 and #3
 565:           paint(g, holeX + holeWidth, holeY, width - (holeX + holeWidth), stripeHeight);
 566:         }
 567: 
 568:         stripeHeight = height - (holeY - y + holeHeight);
 569:         if (stripeHeight > 0)
 570:           paint(g, x, y + height - stripeHeight, width, stripeHeight); // #4
 571:       }
 572:     };
 573: 
 574:     BorderPainter bp;
 575:     int textX, textY, borderWidth, borderHeight;
 576: 
 577:     borderWidth = width - (mes.borderSpacing.left + mes.borderSpacing.right);
 578:     borderHeight = height - (mes.borderSpacing.top + mes.borderSpacing.bottom);
 579:     bp = new BorderPainter(c, getBorder(),
 580:                            x + mes.borderSpacing.left, y + mes.borderSpacing.top,
 581:                            borderWidth, borderHeight);
 582: 
 583:     switch (getRealTitleJustification(c))
 584:     {
 585:     case LEFT:
 586:       textX = x + TEXT_INSET_H;
 587:       break;
 588: 
 589:     case CENTER:
 590:       textX = x + (borderWidth - mes.textWidth) / 2;
 591:       break;
 592: 
 593:     case RIGHT:
 594:       textX = x + borderWidth - (mes.textWidth + TEXT_INSET_H);
 595:       break;
 596: 
 597:     default:
 598:       throw new IllegalStateException();
 599:     }
 600: 
 601:     switch (titlePosition)
 602:     {
 603:     case ABOVE_TOP:
 604:       textY = y;
 605:       break;
 606: 
 607:     case TOP:
 608:     case DEFAULT_POSITION:
 609:     default:
 610:       textY = y + mes.borderSpacing.top + mes.borderInsets.top - mes.textAscent;
 611:       break;
 612: 
 613:     case BELOW_TOP:
 614:       textY = y + mes.borderSpacing.top + mes.borderInsets.top + TEXT_SPACING;
 615:       break;
 616: 
 617:     case ABOVE_BOTTOM:
 618:       textY = y + height - mes.borderSpacing.bottom - mes.borderInsets.bottom
 619:         - TEXT_SPACING - (mes.textAscent + mes.textDescent);
 620:       break;
 621: 
 622:     case BOTTOM:
 623:     case BELOW_BOTTOM:
 624:       textY = y + height - (mes.textAscent + mes.textDescent);
 625:       break;
 626:     }
 627: 
 628:     if (mes.trimmedText == null)
 629:       bp.paint(g);
 630:     else
 631:     {
 632:       try
 633:       {
 634:         g.setFont(mes.font);
 635:         g.setColor(getTitleColor());
 636:         g.drawString(mes.trimmedText, textX, textY + mes.textAscent);
 637:       }
 638:       finally
 639:       {
 640:         g.setFont(oldFont);
 641:         g.setColor(oldColor);
 642:       }
 643:       bp.paintExcept(g, textX - 2, textY,
 644:                      mes.textWidth + 2, mes.textAscent + mes.textDescent);
 645:     }
 646:   }
 647:   
 648:   
 649:   /**
 650:    * Measures the width of this border.
 651:    *
 652:    * @param c the component whose border is to be measured.
 653:    *
 654:    * @return an Insets object whose <code>left</code>, <code>right</code>,
 655:    *         <code>top</code> and <code>bottom</code> fields indicate the
 656:    *         width of the border at the respective edge.
 657:    *
 658:    * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
 659:    */
 660:   public Insets getBorderInsets(Component c)
 661:   {
 662:     return getBorderInsets(c, new Insets(0, 0, 0, 0));
 663:   }
 664:   
 665: 
 666:   /**
 667:    * Measures the width of this border, storing the results into a
 668:    * pre-existing Insets object.
 669:    *
 670:    * @param insets an Insets object for holding the result values.
 671:    *        After invoking this method, the <code>left</code>,
 672:    *        <code>right</code>, <code>top</code> and
 673:    *        <code>bottom</code> fields indicate the width of the
 674:    *        border at the respective edge.
 675:    *
 676:    * @return the same object that was passed for <code>insets</code>.
 677:    *
 678:    * @see #getBorderInsets()
 679:    */
 680:   public Insets getBorderInsets(Component c, Insets insets)
 681:   {
 682:     return getMeasurements(c).getContentInsets(insets);
 683:   }
 684:   
 685:   
 686:   /**
 687:    * Returns <code>false</code>, indicating that there are pixels inside
 688:    * the area of this border where the background shines through.
 689:    *
 690:    * @return <code>false</code>.
 691:    */
 692:   public boolean isBorderOpaque()
 693:   {
 694:     /* Note that the AbstractBorder.isBorderOpaque would also return
 695:      * false, so there is actually no need to override the inherited
 696:      * implementation. However, GNU Classpath strives for exact
 697:      * compatibility with the Sun reference implementation, which
 698:      * overrides isBorderOpaque for unknown reasons.
 699:      */
 700:     return false;
 701:   }
 702: 
 703: 
 704:   /**
 705:    * Returns the text of the title.
 706:    *
 707:    * @return the title text, or <code>null</code> if no title is
 708:    *         displayed.
 709:    */
 710:   public String getTitle()
 711:   {
 712:     return title;
 713:   }
 714: 
 715: 
 716:   /**
 717:    * Retrieves the border underneath the title. If no border has been
 718:    * set, or if it has been set to<code>null</code>, the current
 719:    * {@link javax.swing.LookAndFeel} will be asked for a border
 720:    * using the key <code>TitledBorder.border</code>.
 721:    *
 722:    * @return a border, or <code>null</code> if the current LookAndFeel
 723:    *         does not provide a border for the key
 724:    *         <code>TitledBorder.border</code>.
 725:    *
 726:    * @see javax.swing.UIManager#getBorder(Object)
 727:    */
 728:   public Border getBorder()
 729:   {
 730:     if (border != null)
 731:       return border;
 732: 
 733:     return UIManager.getBorder("TitledBorder.border");
 734:   }
 735: 
 736: 
 737:   /**
 738:    * Returns the vertical position of the title text in relation
 739:    * to the border.
 740:    *
 741:    * @return one of the values {@link #ABOVE_TOP}, {@link #TOP},
 742:    *         {@link #BELOW_TOP}, {@link #ABOVE_BOTTOM}, {@link #BOTTOM},
 743:    *         {@link #BELOW_BOTTOM}, or {@link #DEFAULT_POSITION}.
 744:    */
 745:   public int getTitlePosition()
 746:   {
 747:     return titlePosition;
 748:   }
 749: 
 750: 
 751:   /**
 752:    * Returns the horizontal alignment of the title text in relation to
 753:    * the border.
 754:    *
 755:    * @return one of the values {@link #LEFT}, {@link #CENTER}, {@link
 756:    *         #RIGHT}, {@link #LEADING}, {@link #TRAILING}, or {@link
 757:    *         #DEFAULT_JUSTIFICATION}.
 758:    */
 759:   public int getTitleJustification()
 760:   {
 761:     return titleJustification;
 762:   }
 763: 
 764: 
 765:   /**
 766:    * Retrieves the font for displaying the title text. If no font has
 767:    * been set, or if it has been set to<code>null</code>, the current
 768:    * {@link javax.swing.LookAndFeel} will be asked for a font
 769:    * using the key <code>TitledBorder.font</code>.
 770:    *
 771:    * @return a font, or <code>null</code> if the current LookAndFeel
 772:    *         does not provide a font for the key
 773:    *         <code>TitledBorder.font</code>.
 774:    *
 775:    * @see javax.swing.UIManager#getFont(Object)
 776:    */
 777:   public Font getTitleFont()
 778:   {
 779:     if (titleFont != null)
 780:       return titleFont;
 781: 
 782:     return UIManager.getFont("TitledBorder.font");
 783:   }
 784: 
 785: 
 786:   /**
 787:    * Retrieves the color for displaying the title text. If no color has
 788:    * been set, or if it has been set to<code>null</code>, the current
 789:    * {@link javax.swing.LookAndFeel} will be asked for a color
 790:    * using the key <code>TitledBorder.titleColor</code>.
 791:    *
 792:    * @return a color, or <code>null</code> if the current LookAndFeel
 793:    *         does not provide a color for the key
 794:    *         <code>TitledBorder.titleColor</code>.
 795:    *
 796:    * @see javax.swing.UIManager#getColor(Object)
 797:    */
 798:   public Color getTitleColor()
 799:   {
 800:     if (titleColor != null)
 801:       return titleColor;
 802: 
 803:     return UIManager.getColor("TitledBorder.titleColor");
 804:   }
 805: 
 806: 
 807:   /**
 808:    * Sets the text of the title.
 809:    *
 810:    * @param title the new title text, or <code>null</code> for displaying
 811:    *        no text at all.
 812:    */
 813:   public void setTitle(String title)
 814:   {
 815:     // Swing borders are not JavaBeans, thus no need to fire an event.
 816:     this.title = title;
 817:   }
 818: 
 819: 
 820:   /**
 821:    * Sets the border underneath the title.
 822:    *
 823:    * @param border a border, or <code>null</code> to use the
 824:    *        border that is supplied by the current LookAndFeel.
 825:    *
 826:    * @see #getBorder()
 827:    */
 828:   public void setBorder(Border border)
 829:   {
 830:     // Swing borders are not JavaBeans, thus no need to fire an event.
 831:     this.border = border;
 832:   }
 833: 
 834: 
 835:   /**
 836:    * Sets the vertical position of the title text in relation
 837:    * to the border.
 838:    *
 839:    * @param titlePosition one of the values {@link #ABOVE_TOP},
 840:    *        {@link #TOP}, {@link #BELOW_TOP}, {@link #ABOVE_BOTTOM},
 841:    *        {@link #BOTTOM}, {@link #BELOW_BOTTOM},
 842:    *        or {@link #DEFAULT_POSITION}.
 843:    *
 844:    * @throws IllegalArgumentException if an unsupported value is passed
 845:    *         for <code>titlePosition</code>.
 846:    */
 847:   public void setTitlePosition(int titlePosition)
 848:   {
 849:     if ((titlePosition < DEFAULT_POSITION) || (titlePosition > BELOW_BOTTOM))
 850:       throw new IllegalArgumentException();
 851: 
 852:     // Swing borders are not JavaBeans, thus no need to fire an event.
 853:     this.titlePosition = titlePosition;
 854:   }
 855: 
 856: 
 857:   /**
 858:    * Sets the horizontal alignment of the title text in relation to the border.
 859:    *
 860:    * @param titleJustification the new alignment, which must be one of
 861:    *        {@link #LEFT}, {@link #CENTER}, {@link #RIGHT}, {@link #LEADING},
 862:    *        {@link #TRAILING}, or {@link #DEFAULT_JUSTIFICATION}.
 863:    *
 864:    * @throws IllegalArgumentException if an unsupported value is passed
 865:    *         for <code>titleJustification</code>.
 866:    */
 867:   public void setTitleJustification(int titleJustification)
 868:   {
 869:     if ((titleJustification < DEFAULT_JUSTIFICATION)
 870:         || (titleJustification > TRAILING))
 871:       throw new IllegalArgumentException();
 872: 
 873:     // Swing borders are not JavaBeans, thus no need to fire an event.
 874:     this.titleJustification = titleJustification;
 875:   }
 876: 
 877: 
 878:   /**
 879:    * Sets the font for displaying the title text.
 880:    *
 881:    * @param titleFont the font, or <code>null</code> to use the font
 882:    *        provided by the current {@link javax.swing.LookAndFeel}.
 883:    *
 884:    * @see #getTitleFont()
 885:    */
 886:   public void setTitleFont(Font titleFont)
 887:   {
 888:     // Swing borders are not JavaBeans, thus no need to fire an event.
 889:     this.titleFont = titleFont;
 890:   }
 891: 
 892: 
 893:   /**
 894:    * Sets the color for displaying the title text.
 895:    *
 896:    * @param titleColor the color, or <code>null</code> to use the color
 897:    *        provided by the current {@link javax.swing.LookAndFeel}.
 898:    *
 899:    * @see #getTitleColor()
 900:    */
 901:   public void setTitleColor(Color titleColor)
 902:   {
 903:     // Swing borders are not JavaBeans, thus no need to fire an event.
 904:     this.titleColor = titleColor;
 905:   }
 906: 
 907: 
 908:   /**
 909:    * Calculates the minimum size needed for displaying the border
 910:    * and its title.
 911:    *
 912:    * @param c the Component for which this TitledBorder consitutes
 913:    *        a border.
 914:    */
 915:   public Dimension getMinimumSize(Component c)
 916:   {
 917:     return getMeasurements(c).getMinimumSize();
 918:   }
 919: 
 920: 
 921:   /**
 922:    * Returns the font that is used for displaying the title text for
 923:    * a given Component.
 924:    *
 925:    * @param c the Component for which this TitledBorder is the border.
 926:    *
 927:    * @return The font returned by {@link #getTitleFont()}, or a fallback
 928:    *         if {@link #getTitleFont()} returned <code>null</code>.
 929:    */
 930:   protected Font getFont(Component c)
 931:   {
 932:     Font f;
 933: 
 934:     f = getTitleFont();
 935:     if (f != null)
 936:       return f;
 937: 
 938:     return new Font("Dialog", Font.PLAIN, 12);
 939:   }
 940: 
 941: 
 942:   /**
 943:    * Returns the horizontal alignment of the title text in relation to
 944:    * the border, mapping the component-dependent alignment constants
 945:    * {@link #LEADING}, {@link #TRAILING} and {@link #DEFAULT_JUSTIFICATION}
 946:    * to the correct value according to the embedded component&#x2019;s
 947:    * orientation.
 948:    *
 949:    * @param c the Component for which this TitledBorder is the border.
 950:    *
 951:    * @return one of the values {@link #LEFT}, {@link #CENTER}, or {@link
 952:    *         #RIGHT}.
 953:    */
 954:   private int getRealTitleJustification(Component c)
 955:   {
 956:     switch (titleJustification)
 957:     {
 958:     case DEFAULT_JUSTIFICATION:
 959:     case LEADING:
 960:       if ((c == null) || c.getComponentOrientation().isLeftToRight())
 961:         return LEFT;
 962:       else
 963:         return RIGHT;
 964: 
 965:     case TRAILING:
 966:       if ((c == null) || c.getComponentOrientation().isLeftToRight())
 967:         return RIGHT;
 968:       else
 969:         return LEFT;
 970: 
 971:     default:
 972:       return titleJustification;
 973:     }
 974:   }
 975: 
 976: 
 977:   /**
 978:    * Performs various measurements for the current state of this TitledBorder
 979:    * and the given Component.
 980:    */
 981:   private Measurements getMeasurements(Component c)
 982:   {
 983:     Measurements m = new Measurements();
 984:     FontMetrics fmet;
 985: 
 986:     m.font = getFont(c);
 987:     fmet = c.getFontMetrics(m.font);
 988:     m.border = getBorder();
 989:     if (m.border != null)
 990:       m.borderInsets = m.border.getBorderInsets(c);
 991:     else
 992:       m.borderInsets = new Insets(0, 0, 0, 0);
 993: 
 994:     if (title != null)
 995:     {
 996:       m.trimmedText = title.trim();
 997:       if (m.trimmedText.length() == 0)
 998:         m.trimmedText = null;
 999:     }
1000:     
1001:     m.textAscent = fmet.getAscent();
1002:     m.textDescent = fmet.getDescent();
1003:     if (m.trimmedText != null)
1004:       m.textWidth = fmet.stringWidth(m.trimmedText) + 3;
1005: 
1006:     m.edgeSpacing = new Insets(EDGE_SPACING, EDGE_SPACING, EDGE_SPACING, EDGE_SPACING);
1007:     m.borderSpacing = new Insets(0, 0, 0, 0);
1008: 
1009:     switch (titlePosition)
1010:     {
1011:     case ABOVE_TOP:
1012:       m.borderSpacing.top += m.textAscent + m.textDescent + TEXT_SPACING;
1013:       break;
1014: 
1015:     case BELOW_TOP:
1016:       m.edgeSpacing.top += m.textAscent + m.textDescent + TEXT_SPACING;
1017:       break;
1018: 
1019:     case ABOVE_BOTTOM:
1020:       m.edgeSpacing.bottom += m.textAscent + m.textDescent + TEXT_SPACING;
1021:       break;
1022: 
1023:     case BOTTOM:
1024:       m.edgeSpacing.bottom += Math.max(m.textAscent - m.borderInsets.bottom, 0);
1025:       m.borderSpacing.bottom += m.textDescent;
1026:       break;
1027: 
1028:     case BELOW_BOTTOM:
1029:       m.borderSpacing.bottom += m.textAscent + m.textDescent + TEXT_SPACING;
1030:       break;
1031: 
1032:     default:
1033:       m.borderSpacing.top += m.textAscent;
1034:     }
1035: 
1036:     return m;
1037:   }
1038: 
1039: 
1040:   /**
1041:    * A private helper class for holding the result of measuring the
1042:    * distances of a TitledBorder.  While it would be possible to cache
1043:    * these objects, it does not seem to be worth the effort. Note that
1044:    * invalidating the cache would be tricky, especially since there is
1045:    * no notification mechanism that would inform the cache when
1046:    * border has changed, so it would return different insets.
1047:    */
1048:   private static class Measurements
1049:   {
1050:     /**
1051:      * The font used for displaying the title text. Note that it can
1052:      * well be that the TitledBorder&#x2019;s font is <code>null</code>,
1053:      * which means that the font is to be retrieved from the current
1054:      * LookAndFeel. In this case, this <code>font</code> field will
1055:      * contain the result of the retrieval. Therefore, it is safe
1056:      * to assume that his <code>font</code> field will never have
1057:      * a <code>null</code> value.
1058:      */
1059:     Font font;
1060: 
1061: 
1062:     /**
1063:      * The number of pixels between the base line and the top of the
1064:      * text box.
1065:      */
1066:     int textAscent;
1067: 
1068: 
1069:     /**
1070:      * The number of pixels between the base line and the bottom of
1071:      * the text box.
1072:      */
1073:     int textDescent;
1074: 
1075: 
1076:     /**
1077:      * The title text after removing leading and trailing white space
1078:      * characters. If the title consists only of white space, the
1079:      * value of <code>trimmedText</code> will be <code>null</code>.
1080:      */
1081:     String trimmedText;
1082: 
1083: 
1084:     /**
1085:      * The width of the trimmed title text in pixels.
1086:      */
1087:     int textWidth;
1088: 
1089: 
1090:     /**
1091:      * The border that constitues the interior border
1092:      * underneath the title text.
1093:      */
1094:     Border border;
1095: 
1096: 
1097:     /**
1098:      * The distance between the TitledBorder and the interior border.
1099:      */
1100:     Insets borderSpacing;
1101: 
1102:     
1103:     /**
1104:      * The width of the interior border, as returned by
1105:      * <code>border.getBorderInsets()</code>.
1106:      */
1107:     Insets borderInsets;
1108: 
1109:     
1110:     /**
1111:      * The distance between the interior border and the nested
1112:      * Component for which this TitledBorder is a border.
1113:      */
1114:     Insets edgeSpacing;
1115: 
1116: 
1117:     /**
1118:      * Determines the insets of the nested component when it has a
1119:      * TitledBorder as its border. Used by {@link
1120:      * TitledBorder#getBorderInsets()}.
1121:      *
1122:      * @param i an Insets object for storing the results into, or
1123:      *        <code>null</code> to cause the creation of a
1124:      *        new instance.
1125:      *
1126:      * @return the <code>i</code> object, or a new Insets object
1127:      *         if <code>null</code> was passed for <code>i</code>.
1128:      */
1129:     public Insets getContentInsets(Insets i)
1130:     {
1131:       if (i == null)
1132:         i = new Insets(0, 0, 0, 0);
1133:       i.left = borderSpacing.left + borderInsets.left + edgeSpacing.left;
1134:       i.right = borderSpacing.right + borderInsets.right + edgeSpacing.right;
1135:       i.top = borderSpacing.top + borderInsets.top + edgeSpacing.top;
1136:       i.bottom = borderSpacing.bottom + borderInsets.bottom + edgeSpacing.bottom;
1137:       return i;
1138:     }
1139: 
1140: 
1141:     /**
1142:      * Calculates the minimum size needed for displaying the border
1143:      * and its title. Used by {@link TitledBorder#getMiminumSize()}.
1144:      */
1145:     public Dimension getMinimumSize()
1146:     {
1147:       int width;
1148:       Insets insets;
1149: 
1150:       insets = getContentInsets(null);
1151:       width = Math.max(insets.left + insets.right, textWidth + 2 * TEXT_INSET_H);
1152:       return new Dimension(width, insets.top + insets.bottom);
1153:     }
1154:   }
1155: }