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