GNU Classpath (0.17) | ||
Frames | No Frames |
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}’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}’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}’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’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’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’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: }
GNU Classpath (0.17) |