GNU Classpath (0.18) | ||
Frames | No Frames |
1: /* BasicBorders.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.plaf.basic; 40: 41: import java.awt.Color; 42: import java.awt.Component; 43: import java.awt.Graphics; 44: import java.awt.Insets; 45: import java.awt.Rectangle; 46: import java.io.Serializable; 47: 48: import javax.swing.AbstractButton; 49: import javax.swing.ButtonModel; 50: import javax.swing.JButton; 51: import javax.swing.JPopupMenu; 52: import javax.swing.JSplitPane; 53: import javax.swing.JToolBar; 54: import javax.swing.UIDefaults; 55: import javax.swing.UIManager; 56: import javax.swing.border.AbstractBorder; 57: import javax.swing.border.BevelBorder; 58: import javax.swing.border.Border; 59: import javax.swing.plaf.BorderUIResource; 60: import javax.swing.plaf.UIResource; 61: import javax.swing.text.JTextComponent; 62: 63: /** 64: * Provides various borders for the Basic look and feel. 65: * 66: * @author Sascha Brawer (brawer@dandelis.ch) 67: */ 68: public class BasicBorders 69: { 70: /** 71: * A MarginBorder that gets shared by multiple components. 72: * Created on demand by the private helper function {@link 73: * #getMarginBorder()}. 74: */ 75: private static MarginBorder sharedMarginBorder; 76: 77: 78: /** 79: * Returns a border for drawing push buttons. 80: * 81: * <p>The colors of the border are retrieved from the 82: * <code>UIDefaults</code> of the currently active look and feel 83: * using the keys <code>“Button.shadow”</code>, 84: * <code>“Button.darkShadow”</code>, 85: * <code>“Button.light”</code>, and 86: * <code>“Button.highlight”</code>. 87: * 88: * <p><img src="doc-files/BasicBorders.ButtonBorder-1.png" width="300" 89: * height="170" alt="[A screen shot of the returned border]" /> 90: * 91: * @return a {@link 92: * javax.swing.plaf.BorderUIResource.CompoundBorderUIResource} 93: * whose outer border is a {@link ButtonBorder} and whose 94: * inner border is a {@link MarginBorder}. 95: */ 96: public static Border getButtonBorder() 97: { 98: UIDefaults defaults; 99: Border outer; 100: 101: defaults = UIManager.getLookAndFeelDefaults(); 102: 103: /* The keys for UIDefaults have been determined by writing a 104: * test program that dumps the UIDefaults to stdout; that program 105: * was run on a JDK 1.4.1_01 for GNU/Linux. Note that in the API, 106: * the key "light" is usually called "highlight", and "highlight" 107: * is usually called "lightHighlight". 108: */ 109: outer = new ButtonBorder(defaults.getColor("Button.shadow"), 110: defaults.getColor("Button.darkShadow"), 111: defaults.getColor("Button.light"), 112: defaults.getColor("Button.highlight")); 113: 114: /* While the inner border is shared between multiple buttons, 115: * we do not share the outer border because ButtonBorders store 116: * their border colors. We cannot guarantee that the colors 117: * (which come from UIDefaults) are unchanged between invocations 118: * of getButtonBorder. We could store the last colors, and share 119: * the button border if the colors are the same as in the last 120: * invocation, but it probably is not worth the effort. 121: */ 122: return new BorderUIResource.CompoundBorderUIResource( 123: outer, 124: /* inner */ getMarginBorder()); 125: } 126: 127: 128: /** 129: * Returns a border for drawing radio buttons. 130: * 131: * <p>The colors of the border are retrieved from the 132: * <code>UIDefaults</code> of the currently active look and feel 133: * using the keys <code>“RadioButton.shadow”</code>, 134: * <code>“RadioButton.darkShadow”</code>, 135: * <code>“RadioButton.light”</code>, and 136: * <code>“RadioButton.highlight”</code>. 137: * 138: * <p><img src="doc-files/BasicBorders.RadioButtonBorder-1.png" width="300" 139: * height="135" alt="[A screen shot of the returned border]" /> 140: * 141: * @return a {@link 142: * javax.swing.plaf.BorderUIResource.CompoundBorderUIResource} 143: * whose outer border is a {@link RadioButtonBorder} and whose 144: * inner border is a {@link MarginBorder}. 145: */ 146: public static Border getRadioButtonBorder() 147: { 148: UIDefaults defaults; 149: Border outer; 150: 151: defaults = UIManager.getLookAndFeelDefaults(); 152: 153: /* The keys for UIDefaults have been determined by writing a 154: * test program that dumps the UIDefaults to stdout; that program 155: * was run on a JDK 1.4.1_01 for GNU/Linux. Note that in the API, 156: * the key "light" is usually called "highlight", and "highlight" 157: * is usually called "lightHighlight". 158: */ 159: outer = new RadioButtonBorder( 160: defaults.getColor("RadioButton.shadow"), 161: defaults.getColor("RadioButton.darkShadow"), 162: defaults.getColor("RadioButton.light"), 163: defaults.getColor("RadioButton.highlight")); 164: 165: /* While the inner border is shared between multiple buttons, we 166: * do not share the outer border because RadioButtonBorders, being 167: * ButtonBorders, store their border colors. We cannot guarantee 168: * that the colors (which come from UIDefaults) are unchanged 169: * between invocations of getButtonBorder. We could store the last 170: * colors, and share the button border if the colors are the same 171: * as in the last invocation, but it probably is not worth the 172: * effort. 173: */ 174: return new BorderUIResource.CompoundBorderUIResource( 175: outer, 176: /* inner */ getMarginBorder()); 177: } 178: 179: 180: /** 181: * Returns a border for drawing toggle buttons. 182: * 183: * <p>The colors of the border are retrieved from the 184: * <code>UIDefaults</code> of the currently active look and feel 185: * using the keys <code>“ToggleButton.shadow”</code>, 186: * <code>“ToggleButton.darkShadow”</code>, 187: * <code>“ToggleButton.light”</code>, and 188: * <code>“ToggleButton.highlight”</code>. 189: * 190: * <p><img src="doc-files/BasicBorders.ToggleButtonBorder-1.png" width="270" 191: * height="135" alt="[A screen shot of the returned border]" /> 192: * 193: * @return a {@link 194: * javax.swing.plaf.BorderUIResource.CompoundBorderUIResource} 195: * whose outer border is a {@link ToggleButtonBorder} and whose 196: * inner border is a {@link MarginBorder}. 197: */ 198: public static Border getToggleButtonBorder() 199: { 200: UIDefaults defaults; 201: Border outer; 202: 203: defaults = UIManager.getLookAndFeelDefaults(); 204: 205: /* The keys for UIDefaults have been determined by writing a 206: * test program that dumps the UIDefaults to stdout; that program 207: * was run on a JDK 1.4.1_01 for GNU/Linux. Note that in the API, 208: * the key "light" is usually called "highlight", and "highlight" 209: * is usually called "lightHighlight". 210: */ 211: outer = new ToggleButtonBorder( 212: defaults.getColor("ToggleButton.shadow"), 213: defaults.getColor("ToggleButton.darkShadow"), 214: defaults.getColor("ToggleButton.light"), 215: defaults.getColor("ToggleButton.highlight")); 216: 217: /* While the inner border is shared between multiple buttons, we 218: * do not share the outer border because ToggleButtonBorders, being 219: * ButtonBorders, store their border colors. We cannot guarantee 220: * that the colors (which come from UIDefaults) are unchanged 221: * between invocations of getButtonBorder. We could store the last 222: * colors, and share the button border if the colors are the same 223: * as in the last invocation, but it probably is not worth the 224: * effort. 225: */ 226: return new BorderUIResource.CompoundBorderUIResource( 227: outer, 228: /* inner */ getMarginBorder()); 229: } 230: 231: 232: /** 233: * Returns a border for drawing a two-pixel thick separator line 234: * below menu bars. 235: * 236: * <p>The colors of the border are retrieved from the 237: * <code>UIDefaults</code> of the currently active look and feel 238: * using the keys <code>“MenuBar.shadow”</code> and 239: * <code>“MenuBar.highlight”</code>. 240: * 241: * <p><img src="doc-files/BasicBorders.MenuBarBorder-1.png" width="500" 242: * height="140" alt="[A screen shot of a JMenuBar with this border]" /> 243: * 244: * @return a {@link MenuBarBorder}. 245: * 246: * @see javax.swing.JMenuBar 247: */ 248: public static Border getMenuBarBorder() 249: { 250: UIDefaults defaults; 251: 252: /* See comment in methods above for why this border is not shared. */ 253: defaults = UIManager.getLookAndFeelDefaults(); 254: return new MenuBarBorder(defaults.getColor("MenuBar.shadow"), 255: defaults.getColor("MenuBar.highlight")); 256: } 257: 258: 259: /** 260: * Returns a border for drawing a one-pixel thick border around 261: * split panes that are interrupted where the divider joins the 262: * border. 263: * 264: * <p>The colors of the border are retrieved from the 265: * <code>UIDefaults</code> of the currently active look and feel 266: * using the keys <code>“SplitPane.darkShadow”</code> and 267: * <code>“SplitPane.highlight”</code>. 268: * 269: * <p><img src="doc-files/BasicBorders.SplitPaneBorder-1.png" width="520" 270: * height="200" alt="[A screen shot for JSplitPane.HORIZONTAL_SPLIT]" /> 271: * 272: * <p><img src="doc-files/BasicBorders.SplitPaneBorder-2.png" width="520" 273: * height="200" alt="[A screen shot for JSplitPane.VERTICAL_SPLIT]" /> 274: * 275: * @return a {@link SplitPaneBorder}. 276: * 277: * @see javax.swing.JSplitPane 278: * @see #getSplitPaneDividerBorder() 279: */ 280: public static Border getSplitPaneBorder() 281: { 282: UIDefaults defaults; 283: 284: /* See comment in methods above for why this border is not shared. */ 285: defaults = UIManager.getLookAndFeelDefaults(); 286: return new SplitPaneBorder(defaults.getColor("SplitPane.highlight"), 287: defaults.getColor("SplitPane.darkShadow")); 288: } 289: 290: 291: /** 292: * Returns a border for drawing a one-pixel thick border around 293: * the divider of split panes. 294: * 295: * <p>The colors of the edges that are adjacent to the child components 296: * of the <code>JSplitPane</code> are retrieved from the 297: * <code>UIDefaults</code> of the currently active look and feel 298: * using the keys <code>“SplitPane.darkShadow”</code> and 299: * <code>“SplitPane.highlight”</code>. The color of the 300: * other two edges is the background color of the divider. 301: * 302: * <p><img src="doc-files/BasicBorders.SplitPaneDividerBorder-1.png" 303: * width="520" height="200" alt= 304: * "[A screen shot for JSplitPane.HORIZONTAL_SPLIT]" /> 305: * 306: * @return an instance of <code>SplitPaneDividerBorder</code>, which is 307: * not a public API class of this package. 308: * 309: * @see javax.swing.JSplitPane 310: * @see javax.swing.plaf.basic.BasicSplitPaneDivider 311: * @see #getSplitPaneBorder() 312: * 313: * @since 1.3 314: */ 315: public static Border getSplitPaneDividerBorder() 316: { 317: UIDefaults defaults; 318: 319: /* See comment in methods above for why this border is not shared. */ 320: defaults = UIManager.getLookAndFeelDefaults(); 321: return new SplitPaneDividerBorder( 322: defaults.getColor("SplitPane.highlight"), 323: defaults.getColor("SplitPane.darkShadow")); 324: } 325: 326: 327: /** 328: * Returns a border for drawing a border around a text field 329: * that makes the field appear as etched into the surface. 330: * 331: * <p>The colors of the border are retrieved from the 332: * <code>UIDefaults</code> of the currently active look and feel 333: * using the keys <code>“TextField.shadow”</code>, 334: * <code>“TextField.darkShadow”</code>, 335: * <code>“TextField.light”</code>, and 336: * <code>“TextField.highlight”</code>. 337: * 338: * <p><img src="doc-files/BasicBorders.FieldBorder-1.png" width="500" 339: * height="200" alt="[A screen shot of a border returned by 340: * this method]" /> 341: * 342: * @return an instance of {@link FieldBorder}. 343: * 344: * @see javax.swing.JTextField 345: * @see javax.swing.text.JTextComponent 346: */ 347: public static Border getTextFieldBorder() 348: { 349: UIDefaults defaults; 350: 351: /* See comment in methods above for why this border is not shared. */ 352: defaults = UIManager.getLookAndFeelDefaults(); 353: return new FieldBorder( 354: defaults.getColor("TextField.shadow"), 355: defaults.getColor("TextField.darkShadow"), 356: defaults.getColor("TextField.light"), 357: defaults.getColor("TextField.highlight")); 358: } 359: 360: 361: /** 362: * Returns a two-pixel thick, green 363: * <code>LineBorderUIResource</code>. This is so ugly that look and 364: * feels better use different borders for their progress bars, or 365: * they will look really terrible. 366: * 367: * <p><img src="doc-files/BasicBorders-1.png" width="120" height="80" 368: * alt="[A screen shot of a border returned by this method]" /> 369: */ 370: public static Border getProgressBarBorder() 371: { 372: /* There does not seem to exist a way to parametrize the color 373: * or thickness of the border through UIDefaults. 374: */ 375: return new BorderUIResource.LineBorderUIResource(Color.green, 2); 376: } 377: 378: 379: /** 380: * Returns a border that is composed of a raised bevel border and a 381: * one-pixel thick line border. 382: * 383: * <p><img src="doc-files/BasicBorders-2.png" width="300" height="200" 384: * alt="[A screen shot of a border returned by this method]" /> 385: * 386: * <p>The colors of the border are retrieved from the 387: * <code>UIDefaults</code> of the currently active look and feel 388: * using the keys <code>“InternalFrame.borderShadow”</code>, 389: * <code>“InternalFrame.borderDarkShadow”</code>, 390: * <code>“InternalFrame.borderLight”</code>, 391: * <code>“InternalFrame.borderHighlight”</code>, and 392: * (for the inner one-pixel thick line) 393: * <code>“InternalFrame.borderColor”</code>. 394: */ 395: public static Border getInternalFrameBorder() 396: { 397: UIDefaults defaults; 398: Color shadow, darkShadow, highlight, lightHighlight, line; 399: 400: /* See comment in methods above for why this border is not shared. */ 401: defaults = UIManager.getLookAndFeelDefaults(); 402: 403: shadow = defaults.getColor("InternalFrame.borderShadow"); 404: darkShadow = defaults.getColor("InternalFrame.borderDarkShadow"); 405: highlight = defaults.getColor("InternalFrame.borderLight"); 406: lightHighlight = defaults.getColor("InternalFrame.borderHighlight"); 407: line = defaults.getColor("InternalFrame.borderColor"); 408: 409: return new BorderUIResource.CompoundBorderUIResource( 410: /* outer border */ 411: new BorderUIResource.BevelBorderUIResource( 412: BevelBorder.RAISED, 413: (highlight != null) ? highlight : Color.lightGray, 414: (lightHighlight != null) ? lightHighlight : Color.white, 415: (darkShadow != null) ? darkShadow : Color.black, 416: (shadow != null) ? shadow : Color.gray), 417: 418: /* inner border */ 419: new BorderUIResource.LineBorderUIResource( 420: (line != null) ? line : Color.lightGray)); 421: } 422: 423: 424: /** 425: * Returns a shared MarginBorder. 426: */ 427: static Border getMarginBorder() // intentionally not public 428: { 429: /* Swing is not designed to be thread-safe, so there is no 430: * need to synchronize the access to the global variable. 431: */ 432: if (sharedMarginBorder == null) 433: sharedMarginBorder = new MarginBorder(); 434: 435: return sharedMarginBorder; 436: } 437: 438: 439: /** 440: * A border whose appearance depends on the state of 441: * the enclosed button. 442: * 443: * <p><img src="doc-files/BasicBorders.ButtonBorder-1.png" width="300" 444: * height="170" alt="[A screen shot of this border]" /> 445: * 446: * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel 447: * 448: * @author Sascha Brawer (brawer@dandelis.ch) 449: */ 450: public static class ButtonBorder 451: extends AbstractBorder 452: implements Serializable, UIResource 453: { 454: /** 455: * Determined using the <code>serialver</code> tool 456: * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5. 457: */ 458: static final long serialVersionUID = -157053874580739687L; 459: 460: 461: /** 462: * The color for drawing the shaded parts of the border. 463: * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel 464: */ 465: protected Color shadow; 466: 467: 468: /** 469: * The color for drawing the dark shaded parts of the border. 470: * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel 471: */ 472: protected Color darkShadow; 473: 474: 475: /** 476: * The color for drawing the highlighted parts of the border. 477: * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel 478: */ 479: protected Color highlight; 480: 481: 482: /** 483: * The color for drawing the bright highlighted parts of the border. 484: * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel 485: */ 486: protected Color lightHighlight; 487: 488: 489: /** 490: * Constructs a new border for drawing a button in the Basic 491: * look and feel. 492: * 493: * @param shadow the shadow color. 494: * @param darkShadow a darker variant of the shadow color. 495: * @param highlight the highlight color. 496: * @param lightHighlight a brighter variant of the highlight color. 497: */ 498: public ButtonBorder(Color shadow, Color darkShadow, 499: Color highlight, Color lightHighlight) 500: { 501: /* These colors usually come from the UIDefaults of the current 502: * look and feel. Use fallback values if the colors are not 503: * supplied. The API specification is silent about what 504: * behavior is expected for null colors, so users should not 505: * rely on this fallback (which is why it is not documented in 506: * the above Javadoc). 507: */ 508: this.shadow = (shadow != null) ? shadow : Color.gray; 509: this.darkShadow = (darkShadow != null) ? darkShadow : Color.black; 510: this.highlight = (highlight != null) ? highlight : Color.lightGray; 511: this.lightHighlight = (lightHighlight != null) 512: ? lightHighlight 513: : Color.white; 514: } 515: 516: 517: /** 518: * Paints the ButtonBorder around a given component. 519: * 520: * @param c the component whose border is to be painted. 521: * @param g the graphics for painting. 522: * @param x the horizontal position for painting the border. 523: * @param y the vertical position for painting the border. 524: * @param width the width of the available area for painting the border. 525: * @param height the height of the available area for painting the border. 526: * 527: * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel 528: */ 529: public void paintBorder(Component c, Graphics g, 530: int x, int y, int width, int height) 531: { 532: ButtonModel bmodel = null; 533: 534: if (c instanceof AbstractButton) 535: bmodel = ((AbstractButton) c).getModel(); 536: 537: BasicGraphicsUtils.drawBezel( 538: g, x, y, width, height, 539: /* pressed */ (bmodel != null) 540: && /* mouse button pressed */ bmodel.isPressed() 541: && /* mouse inside */ bmodel.isArmed(), 542: /* default */ (c instanceof JButton) 543: && ((JButton) c).isDefaultButton(), 544: shadow, darkShadow, highlight, lightHighlight); 545: } 546: 547: 548: /** 549: * Measures the width of this border. 550: * 551: * <p>Although the thickness of the actually painted border 552: * depends on the state of the enclosed component, this 553: * measurement always returns the same amount of pixels. Indeed, 554: * it would be rather confusing if a button was appearing to 555: * change its size depending on whether it is pressed or not. 556: * 557: * @param c the component whose border is to be measured. 558: * 559: * @return an Insets object whose <code>left</code>, 560: * <code>right</code>, <code>top</code> and 561: * <code>bottom</code> fields indicate the width of the 562: * border at the respective edge. 563: * 564: * @see #getBorderInsets(java.awt.Component, java.awt.Insets) 565: */ 566: public Insets getBorderInsets(Component c) 567: { 568: /* There is no obvious reason for overriding this method, but we 569: * try to have exactly the same API as the Sun reference 570: * implementation. 571: */ 572: return getBorderInsets(c, null); 573: } 574: 575: 576: /** 577: * Measures the width of this border, storing the results into a 578: * pre-existing Insets object. 579: * 580: * <p>Although the thickness of the actually painted border 581: * depends on the state of the enclosed component, this 582: * measurement always returns the same amount of pixels. Indeed, 583: * it would be rather confusing if a button was appearing to 584: * change its size depending on whether it is pressed or not. 585: * 586: * @param insets an Insets object for holding the result values. 587: * After invoking this method, the <code>left</code>, 588: * <code>right</code>, <code>top</code> and 589: * <code>bottom</code> fields indicate the width of the 590: * border at the respective edge. 591: * 592: * @return the same object that was passed for <code>insets</code>. 593: * 594: * @see #getBorderInsets(Component) 595: */ 596: public Insets getBorderInsets(Component c, Insets insets) 597: { 598: /* The exact amount has been determined using a test program 599: * that was run on the Sun reference implementation. With 600: * Apple/Sun JDK 1.3.1 on MacOS X 10.1.5, the result is 601: * [3, 3, 3, 3]. With Sun JDK 1.4.1_01 on Linux/x86, the 602: * result is [2, 3, 3, 3]. We use the values from the 1.4.1_01 603: * release. 604: */ 605: if (insets == null) 606: return new Insets(2, 3, 3, 3); 607: 608: insets.top = 2; 609: insets.bottom = insets.left = insets.right = 3; 610: return insets; 611: } 612: } 613: 614: 615: /** 616: * A border that makes its enclosed component appear as lowered 617: * into the surface. Typically used for text fields. 618: * 619: * <p><img src="doc-files/BasicBorders.FieldBorder-1.png" width="500" 620: * height="200" alt="[A screen shot of this border]" /> 621: * 622: * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawEtchedRect 623: * 624: * @author Sascha Brawer (brawer@dandelis.ch) 625: */ 626: public static class FieldBorder 627: extends AbstractBorder 628: implements UIResource 629: { 630: /** 631: * Determined using the <code>serialver</code> tool 632: * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5. 633: */ 634: static final long serialVersionUID = 949220756998454908L; 635: 636: 637: /** 638: * The color for drawing the outer half of the top and left 639: * edges. 640: */ 641: protected Color shadow; 642: 643: 644: /** 645: * The color for drawing the inner half of the top and left 646: * edges. 647: */ 648: protected Color darkShadow; 649: 650: 651: /** 652: * The color for drawing the inner half of the bottom and right 653: * edges. 654: */ 655: protected Color highlight; 656: 657: 658: /** 659: * The color for drawing the outer half of the bottom and right 660: * edges. 661: */ 662: protected Color lightHighlight; 663: 664: 665: /** 666: * Constructs a new border for drawing a text field in the Basic 667: * look and feel. 668: * 669: * @param shadow the color for drawing the outer half 670: * of the top and left edges. 671: * 672: * @param darkShadow the color for drawing the inner half 673: * of the top and left edges. 674: * 675: * @param highlight the color for drawing the inner half 676: * of the bottom and right edges. 677: * 678: * @param lightHighlight the color for drawing the outer half 679: * of the bottom and right edges. 680: */ 681: public FieldBorder(Color shadow, Color darkShadow, 682: Color highlight, Color lightHighlight) 683: { 684: /* These colors usually come from the UIDefaults of the current 685: * look and feel. Use fallback values if the colors are not 686: * supplied. The API specification is silent about what 687: * behavior is expected for null colors, so users should not 688: * rely on this fallback (which is why it is not documented in 689: * the above Javadoc). 690: */ 691: this.shadow = (shadow != null) ? shadow : Color.gray; 692: this.darkShadow = (darkShadow != null) ? darkShadow : Color.black; 693: this.highlight = (highlight != null) ? highlight : Color.lightGray; 694: this.lightHighlight = (lightHighlight != null) 695: ? lightHighlight : Color.white; 696: } 697: 698: 699: /** 700: * Paints the FieldBorder around a given component. 701: * 702: * @param c the component whose border is to be painted. 703: * @param g the graphics for painting. 704: * @param x the horizontal position for painting the border. 705: * @param y the vertical position for painting the border. 706: * @param width the width of the available area for painting the border. 707: * @param height the height of the available area for painting the border. 708: * 709: * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawEtchedRect 710: */ 711: public void paintBorder(Component c, Graphics g, 712: int x, int y, int width, int height) 713: { 714: BasicGraphicsUtils.drawEtchedRect(g, x, y, width, height, 715: shadow, darkShadow, 716: highlight, lightHighlight); 717: } 718: 719: 720: /** 721: * Measures the width of this border. 722: * 723: * @param c the component whose border is to be measured. 724: * If <code>c</code> is an instance of {@link 725: * javax.swing.text.JTextComponent}, its margin is 726: * added to the border size. 727: * 728: * @return an Insets object whose <code>left</code>, 729: * <code>right</code>, <code>top</code> and 730: * <code>bottom</code> fields indicate the width of the 731: * border at the respective edge. 732: * 733: * @see #getBorderInsets(java.awt.Component, java.awt.Insets) 734: */ 735: public Insets getBorderInsets(Component c) 736: { 737: return getBorderInsets(c, null); 738: } 739: 740: 741: /** 742: * Measures the width of this border, storing the results into a 743: * pre-existing Insets object. 744: * 745: * @param c the component whose border is to be measured. 746: * If <code>c</code> is an instance of {@link 747: * javax.swing.text.JTextComponent}, its margin is 748: * added to the border size. 749: * 750: * @param insets an Insets object for holding the result values. 751: * After invoking this method, the <code>left</code>, 752: * <code>right</code>, <code>top</code> and 753: * <code>bottom</code> fields indicate the width of the 754: * border at the respective edge. 755: * 756: * @return the same object that was passed for <code>insets</code>. 757: * 758: * @see #getBorderInsets(Component) 759: */ 760: public Insets getBorderInsets(Component c, Insets insets) 761: { 762: if (insets == null) 763: insets = new Insets(2, 2, 2, 2); 764: else 765: insets.top = insets.left = insets.bottom = insets.right = 2; 766: 767: if (c instanceof JTextComponent) 768: { 769: Insets margin = ((JTextComponent) c).getMargin(); 770: insets.top += margin.top; 771: insets.left += margin.left; 772: insets.bottom += margin.bottom; 773: insets.right += margin.right; 774: } 775: 776: return insets; 777: } 778: } 779: 780: 781: /** 782: * An invisible, but spacing border whose margin is determined 783: * by calling the <code>getMargin()</code> method of the enclosed 784: * component. If the enclosed component has no such method, 785: * this border will not occupy any space. 786: * 787: * <p><img src="doc-files/BasicBorders.MarginBorder-1.png" width="325" 788: * height="200" alt="[An illustration that shows how MarginBorder 789: * determines its borders]" /> 790: * 791: * @author Sascha Brawer (brawer@dandelis.ch) 792: */ 793: public static class MarginBorder 794: extends AbstractBorder 795: implements Serializable, UIResource 796: { 797: /** 798: * Determined using the <code>serialver</code> tool 799: * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5. 800: */ 801: static final long serialVersionUID = -3035848353448896090L; 802: 803: 804: /** 805: * Constructs a new MarginBorder. 806: */ 807: public MarginBorder() 808: { 809: } 810: 811: 812: /** 813: * Measures the width of this border. 814: * 815: * @param c the component whose border is to be measured. 816: * 817: * @return an Insets object whose <code>left</code>, <code>right</code>, 818: * <code>top</code> and <code>bottom</code> fields indicate the 819: * width of the border at the respective edge. 820: * 821: * @see #getBorderInsets(java.awt.Component, java.awt.Insets) 822: */ 823: public Insets getBorderInsets(Component c) 824: { 825: return getBorderInsets(c, new Insets(0, 0, 0, 0)); 826: } 827: 828: 829: /** 830: * Determines the insets of this border by calling the 831: * <code>getMargin()</code> method of the enclosed component. The 832: * resulting margin will be stored into the the <code>left</code>, 833: * <code>right</code>, <code>top</code> and <code>bottom</code> 834: * fields of the passed <code>insets</code> parameter. 835: * 836: * <p>Unfortunately, <code>getMargin()</code> is not a method of 837: * {@link javax.swing.JComponent} or some other common superclass 838: * of things with margins. While reflection could be used to 839: * determine the existence of this method, this would be slow on 840: * many virtual machines. Therefore, the current implementation 841: * knows about {@link javax.swing.AbstractButton#getMargin()}, 842: * {@link javax.swing.JPopupMenu#getMargin()}, {@link 843: * javax.swing.JToolBar#getMargin()}, and {@link 844: * javax.swing.text.JTextComponent}. If <code>c</code> is an 845: * instance of a known class, the respective 846: * <code>getMargin()</code> method is called to determine the 847: * correct margin. Otherwise, a zero-width margin is returned. 848: * 849: * @param c the component whose border is to be measured. 850: * 851: * @return the same object that was passed for <code>insets</code>, 852: * but with changed fields. 853: */ 854: public Insets getBorderInsets(Component c, Insets insets) 855: { 856: Insets margin = null; 857: 858: /* This is terrible object-oriented design. See the above Javadoc 859: * for an excuse. 860: */ 861: if (c instanceof AbstractButton) 862: margin = ((AbstractButton) c).getMargin(); 863: else if (c instanceof JPopupMenu) 864: margin = ((JPopupMenu) c).getMargin(); 865: else if (c instanceof JToolBar) 866: margin = ((JToolBar) c).getMargin(); 867: else if (c instanceof JTextComponent) 868: margin = ((JTextComponent) c).getMargin(); 869: 870: if (margin == null) 871: insets.top = insets.left = insets.bottom = insets.right = 0; 872: else 873: { 874: insets.top = margin.top; 875: insets.left = margin.left; 876: insets.bottom = margin.bottom; 877: insets.right = margin.right; 878: } 879: 880: return insets; 881: } 882: } 883: 884: 885: /** 886: * A border for drawing a separator line below JMenuBar. 887: * 888: * <p><img src="doc-files/BasicBorders.MenuBarBorder-1.png" width="500" 889: * height="140" alt="[A screen shot of a JMenuBar with this border]" /> 890: * 891: * @author Sascha Brawer (brawer@dandelis.ch) 892: */ 893: public static class MenuBarBorder 894: extends AbstractBorder 895: implements UIResource 896: { 897: /** 898: * Determined using the <code>serialver</code> tool 899: * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5. 900: */ 901: static final long serialVersionUID = -6909056571935227506L; 902: 903: 904: /** 905: * The shadow color, which is used for the upper line of the 906: * two-pixel thick bottom edge. 907: */ 908: private Color shadow; 909: 910: 911: /** 912: * The highlight color, which is used for the lower line of the 913: * two-pixel thick bottom edge. 914: */ 915: private Color highlight; 916: 917: 918: /** 919: * Constructs a new MenuBarBorder for drawing a JMenuBar in 920: * the Basic look and feel. 921: * 922: * <p><img src="doc-files/BasicBorders.MenuBarBorder-1.png" width="500" 923: * height="140" alt="[A screen shot of a JMenuBar with this 924: * border]" /> 925: * 926: * @param shadow the shadow color, which is used for the upper 927: * line of the two-pixel thick bottom edge. 928: * 929: * @param highlight the shadow color, which is used for the lower 930: * line of the two-pixel thick bottom edge. 931: */ 932: public MenuBarBorder(Color shadow, Color highlight) 933: { 934: /* These colors usually come from the UIDefaults of the current 935: * look and feel. Use fallback values if the colors are not 936: * supplied. The API specification is silent about what 937: * behavior is expected for null colors, so users should not 938: * rely on this fallback (which is why it is not documented in 939: * the above Javadoc). 940: */ 941: this.shadow = (shadow != null) ? shadow : Color.gray; 942: this.highlight = (highlight != null) ? highlight : Color.white; 943: } 944: 945: 946: /** 947: * Paints the MenuBarBorder around a given component. 948: * 949: * @param c the component whose border is to be painted, usually 950: * an instance of {@link javax.swing.JMenuBar}. 951: * 952: * @param g the graphics for painting. 953: * @param x the horizontal position for painting the border. 954: * @param y the vertical position for painting the border. 955: * @param width the width of the available area for painting the border. 956: * @param height the height of the available area for painting the border. 957: */ 958: public void paintBorder(Component c, Graphics g, 959: int x, int y, int width, int height) 960: { 961: Color oldColor; 962: 963: /* To understand this code, it might be helpful to look at the 964: * image "BasicBorders.MenuBarBorder-1.png" that is included 965: * with the JavaDoc. It is located in the "doc-files" 966: * subdirectory. 967: */ 968: oldColor = g.getColor(); 969: y = y + height - 2; 970: try 971: { 972: g.setColor(shadow); 973: g.drawLine(x, y, x + width - 2, y); 974: g.drawLine(x, y + 1, x, y + 1); 975: g.drawLine(x + width - 2, y + 1, x + width - 2, y + 1); 976: 977: g.setColor(highlight); 978: g.drawLine(x + 1, y + 1, x + width - 3, y + 1); 979: g.drawLine(x + width - 1, y, x + width - 1, y + 1); 980: } 981: finally 982: { 983: g.setColor(oldColor); 984: } 985: } 986: 987: 988: /** 989: * Measures the width of this border. 990: * 991: * @param c the component whose border is to be measured. 992: * 993: * @return an Insets object whose <code>left</code>, 994: * <code>right</code>, <code>top</code> and 995: * <code>bottom</code> fields indicate the width of the 996: * border at the respective edge. 997: * 998: * @see #getBorderInsets(java.awt.Component, java.awt.Insets) 999: */ 1000: public Insets getBorderInsets(Component c) 1001: { 1002: /* There is no obvious reason for overriding this method, but we 1003: * try to have exactly the same API as the Sun reference 1004: * implementation. 1005: */ 1006: return getBorderInsets(c, null); 1007: } 1008: 1009: 1010: /** 1011: * Measures the width of this border, storing the results into a 1012: * pre-existing Insets object. 1013: * 1014: * @param insets an Insets object for holding the result values. 1015: * After invoking this method, the <code>left</code>, 1016: * <code>right</code>, <code>top</code> and 1017: * <code>bottom</code> fields indicate the width of the 1018: * border at the respective edge. 1019: * 1020: * @return the same object that was passed for <code>insets</code>. 1021: * 1022: * @see #getBorderInsets(Component) 1023: */ 1024: public Insets getBorderInsets(Component c, Insets insets) 1025: { 1026: /* The exact amount has been determined using a test program 1027: * that was run on the Apple/Sun JDK 1.3.1 on MacOS X, and the 1028: * Sun JDK 1.4.1_01 on GNU/Linux for x86. Both gave [0,0,2,0], 1029: * which was expected from looking at the screen shot. 1030: */ 1031: if (insets == null) 1032: return new Insets(0, 0, 2, 0); 1033: 1034: insets.left = insets.right = insets.top = 0; 1035: insets.bottom = 2; 1036: return insets; 1037: } 1038: } 1039: 1040: 1041: /** 1042: * A border for drawing radio buttons in the Basic look and feel. 1043: * 1044: * <p><img src="doc-files/BasicBorders.RadioButtonBorder-1.png" width="300" 1045: * height="135" alt="[A screen shot of this border]" /> 1046: * 1047: * <p>Note about the screen shot: Normally, the 1048: * <code>borderPainted</code> property is <code>false</code> for 1049: * JRadioButtons. For this screen shot, it has been set to 1050: * <code>true</code> so the borders get drawn. Also, a 1051: * concretization of the Basic look and would typically provide 1052: * icons for the various states of radio buttons. 1053: * 1054: * <p>Note that the focus rectangle is invisible If the radio button 1055: * is currently selected. While it might be debatable whether this 1056: * makes a lot of sense, this behavior can be observed in the Sun 1057: * reference implementation (in JDK 1.3.1 and 1.4.1). The Classpath 1058: * implementation tries to exactly replicate the JDK appearance. 1059: * 1060: * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel 1061: * 1062: * @author Sascha Brawer (brawer@dandelis.ch) 1063: */ 1064: public static class RadioButtonBorder 1065: extends ButtonBorder 1066: { 1067: /** 1068: * Determined using the <code>serialver</code> tool 1069: * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5. 1070: */ 1071: static final long serialVersionUID = 1596945751743747369L; 1072: 1073: 1074: /** 1075: * Constructs a new border for drawing a JRadioButton in 1076: * the Basic look and feel. 1077: * 1078: * @param shadow the shadow color. 1079: * @param darkShadow a darker variant of the shadow color. 1080: * @param highlight the highlight color. 1081: * @param lightHighlight a brighter variant of the highlight color. 1082: */ 1083: public RadioButtonBorder(Color shadow, Color darkShadow, 1084: Color highlight, Color lightHighlight) 1085: { 1086: /* The superclass ButtonBorder substitutes null arguments 1087: * with fallback colors. 1088: */ 1089: super(shadow, darkShadow, highlight, lightHighlight); 1090: } 1091: 1092: 1093: /** 1094: * Paints the RadioButtonBorder around a given component. 1095: * 1096: * <p>The Sun implementation always seems to draw exactly 1097: * the same border, irrespective of the state of the button. 1098: * This is rather surprising, but GNU Classpath emulates the 1099: * observable behavior. 1100: * 1101: * @param c the component whose border is to be painted. 1102: * @param g the graphics for painting. 1103: * @param x the horizontal position for painting the border. 1104: * @param y the vertical position for painting the border. 1105: * @param width the width of the available area for painting the border. 1106: * @param height the height of the available area for painting the border. 1107: * 1108: * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel 1109: */ 1110: public void paintBorder(Component c, Graphics g, 1111: int x, int y, int width, int height) 1112: { 1113: AbstractButton button = null; 1114: ButtonModel bmodel = null; 1115: boolean lowered = false; 1116: boolean focused = false; 1117: 1118: if (c instanceof AbstractButton) 1119: { 1120: button = (AbstractButton) c; 1121: bmodel = button.getModel(); 1122: } 1123: 1124: if (bmodel != null) 1125: { 1126: lowered = button.isSelected() 1127: || (/* mouse inside */ bmodel.isArmed() && bmodel.isPressed()); 1128: focused = button.hasFocus() && button.isFocusPainted(); 1129: } 1130: 1131: if (lowered) 1132: BasicGraphicsUtils.drawLoweredBezel(g, x, y, width, height, 1133: shadow, darkShadow, 1134: highlight, lightHighlight); 1135: else 1136: BasicGraphicsUtils.drawBezel(g, x, y, width, height, 1137: /* isPressed */ false, 1138: /* isPefault */ focused, 1139: shadow, darkShadow, 1140: highlight, lightHighlight); 1141: } 1142: 1143: 1144: /** 1145: * Measures the width of this border. 1146: * 1147: * @param c the component whose border is to be measured. 1148: * 1149: * @return an Insets object whose <code>left</code>, 1150: * <code>right</code>, <code>top</code> and 1151: * <code>bottom</code> fields indicate the width of the 1152: * border at the respective edge. 1153: * 1154: * @see #getBorderInsets(java.awt.Component, java.awt.Insets) 1155: */ 1156: public Insets getBorderInsets(Component c) 1157: { 1158: /* There is no obvious reason for overriding this method, but we 1159: * try to have exactly the same API as the Sun reference 1160: * implementation. 1161: */ 1162: return getBorderInsets(c, null); 1163: } 1164: 1165: 1166: /** 1167: * Measures the width of this border, storing the results into a 1168: * pre-existing Insets object. 1169: * 1170: * @param insets an Insets object for holding the result values. 1171: * After invoking this method, the <code>left</code>, 1172: * <code>right</code>, <code>top</code> and 1173: * <code>bottom</code> fields indicate the width of the 1174: * border at the respective edge. 1175: * 1176: * @return the same object that was passed for <code>insets</code>. 1177: * 1178: * @see #getBorderInsets(Component) 1179: */ 1180: public Insets getBorderInsets(Component c, Insets insets) 1181: { 1182: /* The exact amount has been determined using a test program 1183: * that was run on the Apple/Sun JDK 1.3.1 on MacOS X, and the 1184: * Sun JDK 1.4.1_01 on GNU/Linux for x86. Both gave [2,2,2,2]. 1185: */ 1186: if (insets == null) 1187: return new Insets(2, 2, 2, 2); 1188: 1189: insets.left = insets.right = insets.top = insets.bottom = 2; 1190: return insets; 1191: } 1192: } 1193: 1194: 1195: /** 1196: * A one-pixel thick border for rollover buttons, for example in 1197: * tool bars. 1198: * 1199: * @since 1.4 1200: * @author Sascha Brawer (brawer@dandelis.ch) 1201: */ 1202: public static class RolloverButtonBorder 1203: extends ButtonBorder 1204: { 1205: /** 1206: * Determined using the <code>serialver</code> tool 1207: * of Sun JDK 1.4.1_01 on GNU/Linux 2.4.20 for x86. 1208: */ 1209: static final long serialVersionUID = 1976364864896996846L; 1210: 1211: 1212: /** 1213: * Constructs a new border for drawing a roll-over button 1214: * in the Basic look and feel. 1215: * 1216: * @param shadow the shadow color. 1217: * @param darkShadow a darker variant of the shadow color. 1218: * @param highlight the highlight color. 1219: * @param lightHighlight a brighter variant of the highlight color. 1220: */ 1221: public RolloverButtonBorder(Color shadow, Color darkShadow, 1222: Color highlight, Color lightHighlight) 1223: { 1224: super(shadow, darkShadow, highlight, lightHighlight); 1225: } 1226: 1227: 1228: /** 1229: * Paints the border around a rollover button. If <code>c</code> 1230: * is not an {@link javax.swing.AbstractButton} whose model 1231: * returns <code>true</code> for {@link 1232: * javax.swing.ButtonModel#isRollover}, nothing gets painted at 1233: * all. 1234: * 1235: * @param c the button whose border is to be painted. 1236: * @param g the graphics for painting. 1237: * @param x the horizontal position for painting the border. 1238: * @param y the vertical position for painting the border. 1239: * @param width the width of the available area for painting the border. 1240: * @param height the height of the available area for painting the border. 1241: */ 1242: public void paintBorder(Component c, Graphics g, 1243: int x, int y, int width, int height) 1244: { 1245: ButtonModel bmodel = null; 1246: boolean drawPressed; 1247: Color oldColor = g.getColor(); 1248: int x2, y2; 1249: 1250: if (c instanceof AbstractButton) 1251: bmodel = ((AbstractButton) c).getModel(); 1252: 1253: /* Draw nothing if c is not a rollover button. */ 1254: if ((bmodel == null) || !bmodel.isRollover()) 1255: return; 1256: 1257: /* Draw nothing if the mouse is pressed, but outside the button. */ 1258: if (bmodel.isPressed() && !bmodel.isArmed()) 1259: return; 1260: 1261: drawPressed = bmodel.isSelected() || bmodel.isPressed(); 1262: x2 = x + width - 1; 1263: y2 = y + height - 1; 1264: 1265: try 1266: { 1267: g.setColor(drawPressed ? shadow : lightHighlight); 1268: g.drawLine(x, y, x2 - 1, y); // top edge 1269: g.drawLine(x, y + 1, x, y2 - 1); // left edge 1270: 1271: g.setColor(drawPressed ? lightHighlight : shadow); 1272: g.drawLine(x, y2, x2, y2); // bottom edge 1273: g.drawLine(x2, y, x2, y2 - 1); // right edge 1274: } 1275: finally 1276: { 1277: g.setColor(oldColor); 1278: } 1279: } 1280: } 1281: 1282: 1283: /** 1284: * A border for JSplitPanes in the Basic look and feel. The divider 1285: * in the middle of the JSplitPane has its own border class, of which 1286: * an instance can be obtained with {@link #getSplitPaneDividerBorder()}. 1287: * 1288: * <p><img src="doc-files/BasicBorders.SplitPaneBorder-1.png" width="520" 1289: * height="200" alt="[A screen shot for JSplitPane.HORIZONTAL_SPLIT]" /> 1290: * 1291: * <p><img src="doc-files/BasicBorders.SplitPaneBorder-2.png" width="520" 1292: * height="200" alt="[A screen shot for JSplitPane.VERTICAL_SPLIT]" /> 1293: * 1294: * <p>In contrast to the other borders of the Basic look and feel, 1295: * this class is not serializable. While this might be unintended, 1296: * GNU Classpath follows the specification in order to be fully 1297: * compatible with the Sun reference implementation. 1298: * 1299: * <p>In the Sun JDK, the bottom edge of the divider also gets 1300: * painted if the orientation of the enclosed JSplitPane is 1301: * <code>JSplitPane.VERTICAL_SPLIT</code> (at least in versions 1302: * 1.3.1 and 1.4.1). GNU Classpath does not replicate this bug. A 1303: * report has been filed with Sun (bug ID 4885629). 1304: * 1305: * <p>Note that the bottom left pixel of the border has a different 1306: * color depending on the orientation of the enclosed JSplitPane. 1307: * Although this is visually inconsistent, Classpath replicates the 1308: * appearance of the Sun reference implementation. A bug report has 1309: * been filed with Sun (review ID 188774). 1310: * 1311: * @see #getSplitPaneBorder() 1312: * @see #getSplitPaneDividerBorder() 1313: * 1314: * @author Sascha Brawer (brawer@dandelis.ch) 1315: */ 1316: public static class SplitPaneBorder 1317: implements Border, UIResource 1318: { 1319: /** 1320: * Indicates that the top edge shall be not be painted 1321: * by {@link #paintRect(java.awt.Graphics, int, int, int, int, int)}. 1322: */ 1323: private static final int SUPPRESS_TOP = 1; 1324: 1325: 1326: /** 1327: * Indicates that the left edge shall be not be painted 1328: * by {@link #paintRect(java.awt.Graphics, int, int, int, int, int)}. 1329: */ 1330: private static final int SUPPRESS_LEFT = 2; 1331: 1332: 1333: /** 1334: * Indicates that the bottom edge shall be not be painted 1335: * by {@link #paintRect(java.awt.Graphics, int, int, int, int, int)}. 1336: */ 1337: private static final int SUPPRESS_BOTTOM = 4; 1338: 1339: 1340: /** 1341: * Indicates that the right edge shall be not be painted 1342: * by {@link #paintRect(java.awt.Graphics, int, int, int, int, int)}. 1343: */ 1344: private static final int SUPPRESS_RIGHT = 8; 1345: 1346: 1347: /** 1348: * The color for drawing the bottom and right edges of the border. 1349: */ 1350: protected Color highlight; 1351: 1352: 1353: /** 1354: * The color for drawing the top and left edges of the border. 1355: */ 1356: protected Color shadow; 1357: 1358: 1359: /** 1360: * Constructs a new border for drawing a JSplitPane in the Basic 1361: * look and feel. The divider in the middle of the JSplitPane has 1362: * its own border class, <code>SplitPaneDividerBorder</code>. 1363: * 1364: * @param shadow the shadow color. 1365: * @param highlight the highlight color. 1366: */ 1367: public SplitPaneBorder(Color highlight, Color shadow) 1368: { 1369: /* These colors usually come from the UIDefaults of the current 1370: * look and feel. Use fallback values if the colors are not 1371: * supplied. The API specification is silent about what 1372: * behavior is expected for null colors, so users should not 1373: * rely on this fallback (which is why it is not documented in 1374: * the above Javadoc). 1375: */ 1376: this.shadow = (shadow != null) ? shadow : Color.black; 1377: this.highlight = (highlight != null) ? highlight : Color.white; 1378: } 1379: 1380: 1381: /** 1382: * Paints the border around a <code>JSplitPane</code>. 1383: * 1384: * <p><img src="doc-files/BasicBorders.SplitPaneBorder-1.png" width="520" 1385: * height="200" alt="[A screen shot for JSplitPane.HORIZONTAL_SPLIT]" /> 1386: * 1387: * <p><img src="doc-files/BasicBorders.SplitPaneBorder-2.png" width="520" 1388: * height="200" alt="[A screen shot for JSplitPane.VERTICAL_SPLIT]" /> 1389: * 1390: * @param c the <code>JSplitPane</code> whose border is to be painted. 1391: * @param g the graphics for painting. 1392: * @param x the horizontal position for painting the border. 1393: * @param y the vertical position for painting the border. 1394: * @param width the width of the available area for painting the border. 1395: * @param height the height of the available area for painting the border. 1396: */ 1397: public void paintBorder(Component c, Graphics g, 1398: int x, int y, int width, int height) 1399: { 1400: JSplitPane splitPane; 1401: Component content; 1402: 1403: if (!(c instanceof JSplitPane)) 1404: return; 1405: 1406: splitPane = (JSplitPane) c; 1407: switch (splitPane.getOrientation()) 1408: { 1409: case JSplitPane.HORIZONTAL_SPLIT: 1410: if ((content = splitPane.getLeftComponent()) != null) 1411: paintRect(g, SUPPRESS_RIGHT, true, x, y, content.getBounds()); 1412: if ((content = splitPane.getRightComponent()) != null) 1413: paintRect(g, SUPPRESS_LEFT, true, x, y, content.getBounds()); 1414: break; 1415: 1416: case JSplitPane.VERTICAL_SPLIT: 1417: if ((content = splitPane.getTopComponent()) != null) 1418: paintRect(g, SUPPRESS_BOTTOM, false, x, y, content.getBounds()); 1419: if ((content = splitPane.getBottomComponent()) != null) 1420: paintRect(g, SUPPRESS_TOP, false, x, y, content.getBounds()); 1421: break; 1422: } 1423: } 1424: 1425: 1426: /** 1427: * Paints a border around a child of a <code>JSplitPane</code>, 1428: * omitting some of the edges. 1429: * 1430: * @param g the graphics for painting. 1431: * 1432: * @param suppress a bit mask indicating the set of suppressed 1433: * edges, for example <code>SUPPRESS_TOP | SUPPRESS_RIGHT</code>. 1434: * 1435: * @param x the x coordinate of the SplitPaneBorder. 1436: * 1437: * @param y the y coordinate of the SplitPaneBorder. 1438: * 1439: * @param shadeBottomLeftPixel <code>true</code> to paint the 1440: * bottom left pixel in the shadow color, 1441: * <code>false</code> for the highlight color. The Basic 1442: * look and feel uses the highlight color for the bottom 1443: * left pixel of the border of a JSplitPane whose 1444: * orientation is VERTICAL_SPLIT, and the shadow color 1445: * otherwise. While this might be a strange distinction, 1446: * Classpath tries to look identical to the reference 1447: * implementation. A bug report has been filed with Sun; 1448: * its review ID is 188774. We currently replicate the 1449: * Sun behavior. 1450: * 1451: * @param rect the bounds of the child of JSplitPane whose 1452: * border is to be painted. 1453: */ 1454: private void paintRect(Graphics g, int suppress, 1455: boolean shadeBottomLeftPixel, 1456: int x, int y, 1457: Rectangle rect) 1458: { 1459: if (rect == null) 1460: return; 1461: 1462: /* On each edge, the border exceeds the enclosed child by one 1463: * pixel. See the image "BasicBorders.SplitPaneBorder-1.png" in 1464: * the directory "doc-files". 1465: */ 1466: x += rect.x - 1; 1467: y += rect.y - 1; 1468: int right = x + rect.width + 1; 1469: int bottom = y + rect.height + 1; 1470: 1471: Color oldColor = g.getColor(); 1472: try 1473: { 1474: g.setColor(shadow); 1475: if ((suppress & SUPPRESS_TOP) == 0) 1476: g.drawLine(x, y, right, y); 1477: if ((suppress & SUPPRESS_LEFT) == 0) 1478: g.drawLine(x, y, x, bottom); 1479: else 1480: g.drawLine(x, bottom, x, bottom); // one pixel 1481: 1482: g.setColor(highlight); 1483: if ((suppress & SUPPRESS_BOTTOM) == 0) 1484: g.drawLine(x + (shadeBottomLeftPixel ? 1 : 0), bottom, right, bottom); 1485: else if (!shadeBottomLeftPixel) 1486: g.drawLine(x, bottom, x, bottom); // one pixel 1487: 1488: if ((suppress & SUPPRESS_RIGHT) == 0) 1489: g.drawLine(right, y, right, bottom); 1490: } 1491: finally 1492: { 1493: g.setColor(oldColor); 1494: } 1495: } 1496: 1497: 1498: /** 1499: * Measures the width of this border. 1500: * 1501: * @param c the component whose border is to be measured, usually 1502: * an instance of {@link javax.swing.JSplitPane}. 1503: * 1504: * @return an Insets object whose <code>left</code>, 1505: * <code>right</code>, <code>top</code> and 1506: * <code>bottom</code> fields indicate the width of the 1507: * border at the respective edge. 1508: */ 1509: public Insets getBorderInsets(Component c) 1510: { 1511: return new Insets(1, 1, 1, 1); 1512: } 1513: 1514: 1515: /** 1516: * Determines whether this border fills every pixel in its area 1517: * when painting. 1518: * 1519: * @return <code>false</code> because this border does not 1520: * paint over the pixels where the divider joins 1521: * the border. 1522: */ 1523: public boolean isBorderOpaque() 1524: { 1525: /* Strangely, the Sun implementation (tested with JDK 1.3.1 and 1526: * 1.4.1_01) seems to always return true. It could be a bug, 1527: * but without knowing the details of their implementation, it is 1528: * hard to decide. 1529: */ 1530: return false; 1531: } 1532: } 1533: 1534: 1535: /** 1536: * A border for the divider inside a JSplitPane. 1537: * 1538: * <p><img src="doc-files/BasicBorders.SplitPaneDividerBorder-1.png" 1539: * width="520" height="200" alt="[A screen shot of this border]" /> 1540: * 1541: * @author Sascha Brawer (brawer@dandelis.ch) 1542: */ 1543: private static class SplitPaneDividerBorder 1544: implements Border, UIResource, Serializable 1545: { 1546: /** 1547: * The highlight color, which is drawn on the left or top edge 1548: * depending on the orientation of the JSplitPanel. 1549: */ 1550: protected Color highlight; 1551: 1552: 1553: /** 1554: * The highlight color, which is drawn on the right or bottom edge 1555: * depending on the orientation of the JSplitPanel. 1556: */ 1557: protected Color shadow; 1558: 1559: 1560: /** 1561: * Constructs a new border for drawing the divider of a JSplitPane 1562: * in the Basic look and feel. The outer parts of the JSplitPane have 1563: * their own border class, <code>SplitPaneBorder</code>. 1564: * 1565: * @param shadow the shadow color. 1566: * @param highlight the highlight color. 1567: */ 1568: public SplitPaneDividerBorder(Color highlight, Color shadow) 1569: { 1570: this.highlight = (highlight != null) ? highlight : Color.white; 1571: this.shadow = (shadow != null) ? shadow : Color.black; 1572: } 1573: 1574: 1575: /** 1576: * Paints the border around the divider of a <code>JSplitPane</code>. 1577: * 1578: * <p><img src="doc-files/BasicBorders.SplitPaneDividerBorder-1.png" 1579: * width="520" height="200" alt="[A picture that shows which pixels 1580: * get painted in what color]" /> 1581: * 1582: * @param c the <code>JSplitPane</code> whose divider’s border 1583: * is to be painted. 1584: * @param g the graphics for painting. 1585: * @param x the horizontal position for painting the border. 1586: * @param y the vertical position for painting the border. 1587: * @param width the width of the available area for painting the border. 1588: * @param height the height of the available area for painting the border. 1589: */ 1590: public void paintBorder(Component c, Graphics g, 1591: int x, int y, int width, int height) 1592: { 1593: Color oldColor, dcol; 1594: int x2, y2; 1595: JSplitPane sp; 1596: 1597: sp = getSplitPane(c); 1598: if (sp == null) 1599: return; 1600: 1601: x2 = x + width - 1; 1602: y2 = y + height - 1; 1603: oldColor = g.getColor(); 1604: dcol = c.getBackground(); 1605: try 1606: { 1607: switch (sp.getOrientation()) 1608: { 1609: case JSplitPane.HORIZONTAL_SPLIT: 1610: g.setColor(dcol); 1611: g.drawLine(x + 1, y, x2 - 1, y); 1612: g.drawLine(x + 1, y2, x2 - 1, y2); 1613: g.setColor(sp.getLeftComponent() != null ? highlight : dcol); 1614: g.drawLine(x, y, x, y2); 1615: g.setColor(sp.getRightComponent() != null ? shadow : dcol); 1616: g.drawLine(x2, y, x2, y2); 1617: break; 1618: 1619: case JSplitPane.VERTICAL_SPLIT: 1620: g.setColor(dcol); 1621: g.drawLine(x, y + 1, x, y2 - 1); 1622: g.drawLine(x2, y + 1, x2, y2 - 1); 1623: g.setColor(sp.getTopComponent() != null ? highlight : dcol); 1624: g.drawLine(x, y, x2, y); 1625: g.setColor(sp.getBottomComponent() != null ? shadow : dcol); 1626: g.drawLine(x, y2, x2, y2); 1627: break; 1628: } 1629: } 1630: finally 1631: { 1632: g.setColor(oldColor); 1633: } 1634: } 1635: 1636: 1637: /** 1638: * Measures the width of this border. 1639: * 1640: * @param c the component whose border is to be measured, usually 1641: * an instance of {@link javax.swing.JSplitPane}. 1642: * 1643: * @return an Insets object whose <code>left</code>, 1644: * <code>right</code>, <code>top</code> and 1645: * <code>bottom</code> fields indicate the width of the 1646: * border at the respective edge. 1647: */ 1648: public Insets getBorderInsets(Component c) 1649: { 1650: return new Insets(1, 1, 1, 1); 1651: } 1652: 1653: 1654: /** 1655: * Determines whether this border fills every pixel in its area 1656: * when painting. 1657: * 1658: * @return <code>true</code> if both highlight and shadow 1659: * color are fully opaque. 1660: */ 1661: public boolean isBorderOpaque() 1662: { 1663: return (highlight.getAlpha() == 255) && (shadow.getAlpha() == 255); 1664: } 1665: 1666: 1667: /** 1668: * Determines the JSplitPane whose divider is being painted. 1669: * 1670: * @param c an instance of BasicSplitPaneDivider. 1671: * 1672: * @return a <code>JSplitPane</code>, or <code>null</code> if 1673: * <code>c</code> is not an instance of {@link 1674: * javax.swing.plaf.basic.BasicSplitPaneDivider}. 1675: */ 1676: private JSplitPane getSplitPane(Component c) 1677: { 1678: if (c instanceof BasicSplitPaneDivider) 1679: return (((BasicSplitPaneDivider) c).getBasicSplitPaneUI()) 1680: .getSplitPane(); 1681: else 1682: return null; 1683: } 1684: } 1685: 1686: 1687: /** 1688: * A border for toggle buttons in the Basic look and feel. 1689: * 1690: * <p><img src="doc-files/BasicBorders.ToggleButtonBorder-1.png" 1691: * width="270" height="135" alt="[A screen shot of this border]" /> 1692: * 1693: * <p>The Sun implementation always seems to draw exactly 1694: * the same border, irrespective of the state of the button. 1695: * This is rather surprising, but GNU Classpath emulates the 1696: * observable behavior. 1697: * 1698: * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel 1699: * 1700: * @author Sascha Brawer (brawer@dandelis.ch) 1701: */ 1702: public static class ToggleButtonBorder 1703: extends ButtonBorder 1704: { 1705: /** 1706: * Determined using the <code>serialver</code> tool 1707: * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5. 1708: */ 1709: static final long serialVersionUID = -3528666548001058394L; 1710: 1711: 1712: /** 1713: * Constructs a new border for drawing a JToggleButton in 1714: * the Basic look and feel. 1715: * 1716: * @param shadow the shadow color. 1717: * @param darkShadow a darker variant of the shadow color. 1718: * @param highlight the highlight color. 1719: * @param lightHighlight a brighter variant of the highlight color. 1720: */ 1721: public ToggleButtonBorder(Color shadow, Color darkShadow, 1722: Color highlight, Color lightHighlight) 1723: { 1724: /* The superclass ButtonBorder substitutes null arguments 1725: * with fallback colors. 1726: */ 1727: super(shadow, darkShadow, highlight, lightHighlight); 1728: } 1729: 1730: 1731: /** 1732: * Paints the ToggleButtonBorder around a given component. 1733: * 1734: * <p>The Sun implementation always seems to draw exactly 1735: * the same border, irrespective of the state of the button. 1736: * This is rather surprising, but GNU Classpath emulates the 1737: * observable behavior. 1738: * 1739: * @param c the component whose border is to be painted. 1740: * @param g the graphics for painting. 1741: * @param x the horizontal position for painting the border. 1742: * @param y the vertical position for painting the border. 1743: * @param width the width of the available area for painting the border. 1744: * @param height the height of the available area for painting the border. 1745: * 1746: * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel 1747: */ 1748: public void paintBorder(Component c, Graphics g, 1749: int x, int y, int width, int height) 1750: { 1751: /* The author of this code tried various variants for setting 1752: * the state of the enclosed JToggleButton, but it seems that 1753: * the drawn border is always identical. Weird, because this 1754: * means that the user does not see whether the JToggleButton 1755: * is selected or not. 1756: */ 1757: BasicGraphicsUtils.drawBezel(g, x, y, width, height, 1758: /* pressed */ false, 1759: /* default */ false, 1760: shadow, darkShadow, 1761: highlight, lightHighlight); 1762: } 1763: 1764: 1765: /** 1766: * Measures the width of this border. 1767: * 1768: * @param c the component whose border is to be measured. 1769: * 1770: * @return an Insets object whose <code>left</code>, 1771: * <code>right</code>, <code>top</code> and 1772: * <code>bottom</code> fields indicate the width of the 1773: * border at the respective edge. 1774: * 1775: * @see #getBorderInsets(java.awt.Component, java.awt.Insets) 1776: */ 1777: public Insets getBorderInsets(Component c) 1778: { 1779: /* There is no obvious reason for overriding this method, but we 1780: * try to have exactly the same API as the Sun reference 1781: * implementation. 1782: */ 1783: return getBorderInsets(c, null); 1784: } 1785: 1786: 1787: /** 1788: * Measures the width of this border, storing the results into a 1789: * pre-existing Insets object. 1790: * 1791: * @param insets an Insets object for holding the result values. 1792: * After invoking this method, the <code>left</code>, 1793: * <code>right</code>, <code>top</code> and 1794: * <code>bottom</code> fields indicate the width of the 1795: * border at the respective edge. 1796: * 1797: * @return the same object that was passed for <code>insets</code>. 1798: * 1799: * @see #getBorderInsets(Component) 1800: */ 1801: public Insets getBorderInsets(Component c, Insets insets) 1802: { 1803: /* The exact amount has been determined using a test program 1804: * that was run on the Apple/Sun JDK 1.3.1 on MacOS X, and the 1805: * Sun JDK 1.4.1_01 on GNU/Linux for x86. Both gave [2,2,2,2]. 1806: */ 1807: if (insets == null) 1808: return new Insets(2, 2, 2, 2); 1809: 1810: insets.left = insets.right = insets.top = insets.bottom = 2; 1811: return insets; 1812: } 1813: } 1814: }
GNU Classpath (0.18) |