Source for javax.swing.plaf.basic.BasicBorders

   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>&#x201c;Button.shadow&#x201d;</code>,
  84:    * <code>&#x201c;Button.darkShadow&#x201d;</code>,
  85:    * <code>&#x201c;Button.light&#x201d;</code>, and
  86:    * <code>&#x201c;Button.highlight&#x201d;</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>&#x201c;RadioButton.shadow&#x201d;</code>,
 134:    * <code>&#x201c;RadioButton.darkShadow&#x201d;</code>,
 135:    * <code>&#x201c;RadioButton.light&#x201d;</code>, and
 136:    * <code>&#x201c;RadioButton.highlight&#x201d;</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>&#x201c;ToggleButton.shadow&#x201d;</code>,
 186:    * <code>&#x201c;ToggleButton.darkShadow&#x201d;</code>,
 187:    * <code>&#x201c;ToggleButton.light&#x201d;</code>, and
 188:    * <code>&#x201c;ToggleButton.highlight&#x201d;</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>&#x201c;MenuBar.shadow&#x201d;</code> and
 239:    * <code>&#x201c;MenuBar.highlight&#x201d;</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>&#x201c;SplitPane.darkShadow&#x201d;</code> and
 267:    * <code>&#x201c;SplitPane.highlight&#x201d;</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>&#x201c;SplitPane.darkShadow&#x201d;</code> and
 299:    * <code>&#x201c;SplitPane.highlight&#x201d;</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>&#x201c;TextField.shadow&#x201d;</code>,
 334:    * <code>&#x201c;TextField.darkShadow&#x201d;</code>,
 335:    * <code>&#x201c;TextField.light&#x201d;</code>, and
 336:    * <code>&#x201c;TextField.highlight&#x201d;</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>&#x201c;InternalFrame.borderShadow&#x201d;</code>,
 389:    * <code>&#x201c;InternalFrame.borderDarkShadow&#x201d;</code>,
 390:    * <code>&#x201c;InternalFrame.borderLight&#x201d;</code>,
 391:    * <code>&#x201c;InternalFrame.borderHighlight&#x201d;</code>, and
 392:    * (for the inner one-pixel thick line)
 393:    * <code>&#x201c;InternalFrame.borderColor&#x201d;</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&#x2019;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: }