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