001/* TitledBorder.java -- 002 Copyright (C) 2003, 2004, 2005, 2006, Free Software Foundation, Inc. 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038 039package javax.swing.border; 040 041import java.awt.Color; 042import java.awt.Component; 043import java.awt.Dimension; 044import java.awt.Font; 045import java.awt.FontMetrics; 046import java.awt.Graphics; 047import java.awt.Insets; 048import java.awt.Point; 049import java.awt.Rectangle; 050 051import javax.swing.SwingUtilities; 052import javax.swing.UIManager; 053 054 055/** 056 * A border that paints a title on top of another border. 057 * 058 * @author Sascha Brawer (brawer@dandelis.ch) 059 */ 060public class TitledBorder extends AbstractBorder 061{ 062 /** 063 * A value for the <code>titlePosition</code> property that vertically 064 * positions the title text at the default vertical position, which 065 * is in the middle of the top line of the border. 066 * 067 * @see #getTitlePosition() 068 * @see #setTitlePosition(int) 069 */ 070 public static final int DEFAULT_POSITION = 0; 071 072 073 /** 074 * A value for the <code>titlePosition</code> property that vertically 075 * positions the title text above the top line of the border. 076 * 077 * @see #getTitlePosition() 078 * @see #setTitlePosition(int) 079 */ 080 public static final int ABOVE_TOP = 1; 081 082 083 /** 084 * A value for the <code>titlePosition</code> property that vertically 085 * positions the title text at the middle of the top line 086 * of the border. 087 * 088 * @see #getTitlePosition() 089 * @see #setTitlePosition(int) 090 */ 091 public static final int TOP = 2; 092 093 094 /** 095 * A value for the <code>titlePosition</code> property that vertically 096 * positions the title text below the top line of the border. 097 * 098 * @see #getTitlePosition() 099 * @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}’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}’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}’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}