/* BasicBorders.java --
   Copyright (C) 2003, 2004, 2005  Free Software Foundation, Inc.

This file is part of GNU Classpath.

GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA.

Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.

As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module.  An independent module is a module which is not derived from
or based on this library.  If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so.  If you do not wish to do so, delete this
exception statement from your version. */


package javax.swing.plaf.basic;

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import java.io.Serializable;

import javax.swing.AbstractButton;
import javax.swing.ButtonModel;
import javax.swing.JButton;
import javax.swing.JPopupMenu;
import javax.swing.JSplitPane;
import javax.swing.JToolBar;
import javax.swing.UIManager;
import javax.swing.border.AbstractBorder;
import javax.swing.border.BevelBorder;
import javax.swing.border.Border;
import javax.swing.plaf.BorderUIResource;
import javax.swing.plaf.UIResource;
import javax.swing.text.JTextComponent;

/**
 * Provides various borders for the Basic look and feel.
 *
 * @author Sascha Brawer (brawer@dandelis.ch)
 */
public class BasicBorders
{
  /**
   * A MarginBorder that gets shared by multiple components.
   * Created on demand by the private helper function {@link
   * #getMarginBorder()}.
   */
  private static MarginBorder sharedMarginBorder;


  /**
   * Returns a border for drawing push buttons.
   *
   * <p>The colors of the border are retrieved from the
   * <code>UIDefaults</code> of the currently active look and feel
   * using the keys <code>&#x201c;Button.shadow&#x201d;</code>,
   * <code>&#x201c;Button.darkShadow&#x201d;</code>,
   * <code>&#x201c;Button.light&#x201d;</code>, and
   * <code>&#x201c;Button.highlight&#x201d;</code>.
   *
   * <p><img src="doc-files/BasicBorders.ButtonBorder-1.png" width="300"
   * height="170" alt="[A screen shot of the returned border]" />
   *
   * @return a {@link
   *         javax.swing.plaf.BorderUIResource.CompoundBorderUIResource}
   *         whose outer border is a {@link ButtonBorder} and whose
   *         inner border is a {@link MarginBorder}.
   */
  public static Border getButtonBorder()
  {
    Border outer;

    /* The keys for UIDefaults have been determined by writing a
     * test program that dumps the UIDefaults to stdout; that program
     * was run on a JDK 1.4.1_01 for GNU/Linux. Note that in the API,
     * the key "light" is usually called "highlight", and "highlight"
     * is usually called "lightHighlight".
     */
    outer = new ButtonBorder(UIManager.getColor("Button.shadow"),
                             UIManager.getColor("Button.darkShadow"),
                             UIManager.getColor("Button.light"),
                             UIManager.getColor("Button.highlight"));

    /* While the inner border is shared between multiple buttons,
     * we do not share the outer border because ButtonBorders store
     * their border colors. We cannot guarantee that the colors
     * (which come from UIDefaults) are unchanged between invocations
     * of getButtonBorder. We could store the last colors, and share
     * the button border if the colors are the same as in the last
     * invocation, but it probably is not worth the effort.
     */
    return new BorderUIResource.CompoundBorderUIResource(
      outer,
      /* inner */ getMarginBorder());
  }


  /**
   * Returns a border for drawing radio buttons.
   *
   * <p>The colors of the border are retrieved from the
   * <code>UIDefaults</code> of the currently active look and feel
   * using the keys <code>&#x201c;RadioButton.shadow&#x201d;</code>,
   * <code>&#x201c;RadioButton.darkShadow&#x201d;</code>,
   * <code>&#x201c;RadioButton.light&#x201d;</code>, and
   * <code>&#x201c;RadioButton.highlight&#x201d;</code>.
   *
   * <p><img src="doc-files/BasicBorders.RadioButtonBorder-1.png" width="300"
   * height="135" alt="[A screen shot of the returned border]" />
   *
   * @return a {@link
   *         javax.swing.plaf.BorderUIResource.CompoundBorderUIResource}
   *         whose outer border is a {@link RadioButtonBorder} and whose
   *         inner border is a {@link MarginBorder}.
   */
  public static Border getRadioButtonBorder()
  {
    Border outer;

    /* The keys for UIDefaults have been determined by writing a
     * test program that dumps the UIDefaults to stdout; that program
     * was run on a JDK 1.4.1_01 for GNU/Linux. Note that in the API,
     * the key "light" is usually called "highlight", and "highlight"
     * is usually called "lightHighlight".
     */
    outer = new RadioButtonBorder(
      UIManager.getColor("RadioButton.shadow"),
      UIManager.getColor("RadioButton.darkShadow"),
      UIManager.getColor("RadioButton.light"),
      UIManager.getColor("RadioButton.highlight"));

    /* While the inner border is shared between multiple buttons, we
     * do not share the outer border because RadioButtonBorders, being
     * ButtonBorders, store their border colors. We cannot guarantee
     * that the colors (which come from UIDefaults) are unchanged
     * between invocations of getButtonBorder. We could store the last
     * colors, and share the button border if the colors are the same
     * as in the last invocation, but it probably is not worth the
     * effort.
     */
    return new BorderUIResource.CompoundBorderUIResource(
      outer,
      /* inner */ getMarginBorder());
  }


  /**
   * Returns a border for drawing toggle buttons.
   *
   * <p>The colors of the border are retrieved from the
   * <code>UIDefaults</code> of the currently active look and feel
   * using the keys <code>&#x201c;ToggleButton.shadow&#x201d;</code>,
   * <code>&#x201c;ToggleButton.darkShadow&#x201d;</code>,
   * <code>&#x201c;ToggleButton.light&#x201d;</code>, and
   * <code>&#x201c;ToggleButton.highlight&#x201d;</code>.
   *
   * <p><img src="doc-files/BasicBorders.ToggleButtonBorder-1.png" width="270"
   * height="135" alt="[A screen shot of the returned border]" />
   *
   * @return a {@link
   *         javax.swing.plaf.BorderUIResource.CompoundBorderUIResource}
   *         whose outer border is a {@link ToggleButtonBorder} and whose
   *         inner border is a {@link MarginBorder}.
   */
  public static Border getToggleButtonBorder()
  {
    Border outer;

    /* The keys for UIDefaults have been determined by writing a
     * test program that dumps the UIDefaults to stdout; that program
     * was run on a JDK 1.4.1_01 for GNU/Linux. Note that in the API,
     * the key "light" is usually called "highlight", and "highlight"
     * is usually called "lightHighlight".
     */
    outer = new ToggleButtonBorder(
      UIManager.getColor("ToggleButton.shadow"),
      UIManager.getColor("ToggleButton.darkShadow"),
      UIManager.getColor("ToggleButton.light"),
      UIManager.getColor("ToggleButton.highlight"));

    /* While the inner border is shared between multiple buttons, we
     * do not share the outer border because ToggleButtonBorders, being
     * ButtonBorders, store their border colors. We cannot guarantee
     * that the colors (which come from UIDefaults) are unchanged
     * between invocations of getButtonBorder. We could store the last
     * colors, and share the button border if the colors are the same
     * as in the last invocation, but it probably is not worth the
     * effort.
     */
    return new BorderUIResource.CompoundBorderUIResource(
      outer,
      /* inner */ getMarginBorder());
  }


  /**
   * Returns a border for drawing a two-pixel thick separator line
   * below menu bars.
   *
   * <p>The colors of the border are retrieved from the
   * <code>UIDefaults</code> of the currently active look and feel
   * using the keys <code>&#x201c;MenuBar.shadow&#x201d;</code> and
   * <code>&#x201c;MenuBar.highlight&#x201d;</code>.
   *
   * <p><img src="doc-files/BasicBorders.MenuBarBorder-1.png" width="500"
   * height="140" alt="[A screen shot of a JMenuBar with this border]" />
   *
   * @return a {@link MenuBarBorder}.
   *
   * @see javax.swing.JMenuBar
   */
  public static Border getMenuBarBorder()
  {
    /* See comment in methods above for why this border is not shared. */
    return new MenuBarBorder(UIManager.getColor("MenuBar.shadow"),
                             UIManager.getColor("MenuBar.highlight"));
  }


  /**
   * Returns a border for drawing a one-pixel thick border around
   * split panes that are interrupted where the divider joins the
   * border.
   *
   * <p>The colors of the border are retrieved from the
   * <code>UIDefaults</code> of the currently active look and feel
   * using the keys <code>&#x201c;SplitPane.darkShadow&#x201d;</code> and
   * <code>&#x201c;SplitPane.highlight&#x201d;</code>.
   *   
   * <p><img src="doc-files/BasicBorders.SplitPaneBorder-1.png" width="520"
   * height="200" alt="[A screen shot for JSplitPane.HORIZONTAL_SPLIT]" />
   *
   * <p><img src="doc-files/BasicBorders.SplitPaneBorder-2.png" width="520"
   * height="200" alt="[A screen shot for JSplitPane.VERTICAL_SPLIT]" />
   *
   * @return a {@link SplitPaneBorder}.
   *
   * @see javax.swing.JSplitPane
   * @see #getSplitPaneDividerBorder()
   */
  public static Border getSplitPaneBorder()
  {
    /* See comment in methods above for why this border is not shared. */
    return new SplitPaneBorder(UIManager.getColor("SplitPane.highlight"),
                               UIManager.getColor("SplitPane.darkShadow"));
  }


  /**
   * Returns a border for drawing a one-pixel thick border around
   * the divider of split panes.
   *
   * <p>The colors of the edges that are adjacent to the child components
   * of the <code>JSplitPane</code> are retrieved from the
   * <code>UIDefaults</code> of the currently active look and feel
   * using the keys <code>&#x201c;SplitPane.darkShadow&#x201d;</code> and
   * <code>&#x201c;SplitPane.highlight&#x201d;</code>. The color of the
   * other two edges is the background color of the divider.
   *
   * <p><img src="doc-files/BasicBorders.SplitPaneDividerBorder-1.png"
   * width="520" height="200" alt= 
   * "[A screen shot for JSplitPane.HORIZONTAL_SPLIT]" />
   *
   * @return an instance of <code>SplitPaneDividerBorder</code>, which is
   *         not a public API class of this package.
   *
   * @see javax.swing.JSplitPane
   * @see javax.swing.plaf.basic.BasicSplitPaneDivider
   * @see #getSplitPaneBorder()
   *
   * @since 1.3
   */
  public static Border getSplitPaneDividerBorder()
  {
    /* See comment in methods above for why this border is not shared. */
    return new SplitPaneDividerBorder(
      UIManager.getColor("SplitPane.highlight"),
      UIManager.getColor("SplitPane.darkShadow"));
  }


  /**
   * Returns a border for drawing a border around a text field
   * that makes the field appear as etched into the surface.
   *
   * <p>The colors of the border are retrieved from the
   * <code>UIDefaults</code> of the currently active look and feel
   * using the keys <code>&#x201c;TextField.shadow&#x201d;</code>,
   * <code>&#x201c;TextField.darkShadow&#x201d;</code>,
   * <code>&#x201c;TextField.light&#x201d;</code>, and
   * <code>&#x201c;TextField.highlight&#x201d;</code>.
   *
   * <p><img src="doc-files/BasicBorders.FieldBorder-1.png" width="500"
   * height="200" alt="[A screen shot of a border returned by
   * this method]" />
   *
   * @return an instance of {@link FieldBorder}.
   *
   * @see javax.swing.JTextField
   * @see javax.swing.text.JTextComponent
   */
  public static Border getTextFieldBorder()
  {
    /* See comment in methods above for why this border is not shared. */
    return new FieldBorder(
      UIManager.getColor("TextField.shadow"),
      UIManager.getColor("TextField.darkShadow"),
      UIManager.getColor("TextField.light"),
      UIManager.getColor("TextField.highlight"));
  }
  

  /**
   * Returns a two-pixel thick, green
   * <code>LineBorderUIResource</code>.  This is so ugly that look and
   * feels better use different borders for their progress bars, or
   * they will look really terrible.
   *
   * <p><img src="doc-files/BasicBorders-1.png" width="120" height="80"
   * alt="[A screen shot of a border returned by this method]" />
   */
  public static Border getProgressBarBorder()
  {
    /* There does not seem to exist a way to parametrize the color
     * or thickness of the border through UIDefaults.
     */
    return new BorderUIResource.LineBorderUIResource(Color.green, 2);
  }


  /**
   * Returns a border that is composed of a raised bevel border and a
   * one-pixel thick line border.
   *
   * <p><img src="doc-files/BasicBorders-2.png" width="300" height="200"
   * alt="[A screen shot of a border returned by this method]" />
   *
   * <p>The colors of the border are retrieved from the
   * <code>UIDefaults</code> of the currently active look and feel
   * using the keys <code>&#x201c;InternalFrame.borderShadow&#x201d;</code>,
   * <code>&#x201c;InternalFrame.borderDarkShadow&#x201d;</code>,
   * <code>&#x201c;InternalFrame.borderLight&#x201d;</code>,
   * <code>&#x201c;InternalFrame.borderHighlight&#x201d;</code>, and
   * (for the inner one-pixel thick line)
   * <code>&#x201c;InternalFrame.borderColor&#x201d;</code>.
   */
  public static Border getInternalFrameBorder()
  {
    Color shadow, darkShadow, highlight, lightHighlight, line;

    /* See comment in methods above for why this border is not shared. */
    shadow = UIManager.getColor("InternalFrame.borderShadow");
    darkShadow = UIManager.getColor("InternalFrame.borderDarkShadow");
    highlight = UIManager.getColor("InternalFrame.borderLight");
    lightHighlight = UIManager.getColor("InternalFrame.borderHighlight");
    line = UIManager.getColor("InternalFrame.borderColor");

    return new BorderUIResource.CompoundBorderUIResource(
      /* outer border */
      new BorderUIResource.BevelBorderUIResource(
        BevelBorder.RAISED,
        (highlight != null) ? highlight : Color.lightGray,
        (lightHighlight != null) ? lightHighlight : Color.white,
        (darkShadow != null) ? darkShadow : Color.black,
        (shadow != null) ? shadow : Color.gray),

      /* inner border */
      new BorderUIResource.LineBorderUIResource(
        (line != null) ? line : Color.lightGray));
  }


  /**
   * Returns a shared MarginBorder.
   */
  static Border getMarginBorder()  // intentionally not public
  {
    /* Swing is not designed to be thread-safe, so there is no
     * need to synchronize the access to the global variable.
     */
    if (sharedMarginBorder == null)
      sharedMarginBorder = new MarginBorder();

    return sharedMarginBorder;
  }
  
  
  /**
   * A border whose appearance depends on the state of
   * the enclosed button.
   *
   * <p><img src="doc-files/BasicBorders.ButtonBorder-1.png" width="300"
   * height="170" alt="[A screen shot of this border]" />
   *
   * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
   *
   * @author Sascha Brawer (brawer@dandelis.ch)
   */
  public static class ButtonBorder
    extends AbstractBorder
    implements Serializable, UIResource
  {
    /**
     * Determined using the <code>serialver</code> tool
     * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
     */
    static final long serialVersionUID = -157053874580739687L;
    
    
    /**
     * The color for drawing the shaded parts of the border.
     * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
     */
    protected Color shadow;
    
    
    /**
     * The color for drawing the dark shaded parts of the border.
     * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
     */
    protected Color darkShadow;
    
    
    /**
     * The color for drawing the highlighted parts of the border.
     * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
     */
    protected Color highlight;
    
    
    /**
     * The color for drawing the bright highlighted parts of the border.
     * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
     */
    protected Color lightHighlight;
    
    
    /**
     * Constructs a new border for drawing a button in the Basic
     * look and feel.
     *
     * @param shadow the shadow color.
     * @param darkShadow a darker variant of the shadow color.
     * @param highlight the highlight color.
     * @param lightHighlight a brighter variant of the highlight  color.
     */
    public ButtonBorder(Color shadow, Color darkShadow,
                        Color highlight, Color lightHighlight)
    {
      /* These colors usually come from the UIDefaults of the current
       * look and feel. Use fallback values if the colors are not
       * supplied.  The API specification is silent about what
       * behavior is expected for null colors, so users should not
       * rely on this fallback (which is why it is not documented in
       * the above Javadoc).
       */
      this.shadow = (shadow != null) ? shadow : Color.gray;
      this.darkShadow = (darkShadow != null) ? darkShadow : Color.black;
      this.highlight = (highlight != null) ? highlight : Color.lightGray;
      this.lightHighlight = (lightHighlight != null)
        ? lightHighlight
        : Color.white;
    }
    

    /**
     * Paints the ButtonBorder around a given component.
     *
     * @param c the component whose border is to be painted.
     * @param g the graphics for painting.
     * @param x the horizontal position for painting the border.
     * @param y the vertical position for painting the border.
     * @param width the width of the available area for painting the border.
     * @param height the height of the available area for painting the border.
     *
     * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
     */
    public void paintBorder(Component c, Graphics  g,
                            int x, int y, int width, int height)
    {
      ButtonModel bmodel = null;
      
      if (c instanceof AbstractButton)
        bmodel = ((AbstractButton) c).getModel();
      
      BasicGraphicsUtils.drawBezel(
        g, x, y, width, height,
        /* pressed */ (bmodel != null)
                        && /* mouse button pressed */ bmodel.isPressed()
                        && /* mouse inside */ bmodel.isArmed(),
        /* default */ (c instanceof JButton)
                        && ((JButton) c).isDefaultButton(),
        shadow, darkShadow, highlight, lightHighlight);
    }
    
    
    /**
     * Measures the width of this border.
     *
     * <p>Although the thickness of the actually painted border
     * depends on the state of the enclosed component, this
     * measurement always returns the same amount of pixels.  Indeed,
     * it would be rather confusing if a button was appearing to
     * change its size depending on whether it is pressed or not.
     *
     * @param c the component whose border is to be measured.
     *
     * @return an Insets object whose <code>left</code>,
     *         <code>right</code>, <code>top</code> and
     *         <code>bottom</code> fields indicate the width of the
     *         border at the respective edge.
     *
     * @see #getBorderInsets(java.awt.Component, java.awt.Insets) 
     */
    public Insets getBorderInsets(Component c)
    {
      /* There is no obvious reason for overriding this method, but we
       * try to have exactly the same API as the Sun reference
       * implementation.
       */
      return getBorderInsets(c, null);
    }

    
    /**
     * Measures the width of this border, storing the results into a
     * pre-existing Insets object.
     *
     * <p>Although the thickness of the actually painted border
     * depends on the state of the enclosed component, this
     * measurement always returns the same amount of pixels.  Indeed,
     * it would be rather confusing if a button was appearing to
     * change its size depending on whether it is pressed or not.
     *
     * @param insets an Insets object for holding the result values.
     *        After invoking this method, the <code>left</code>,
     *        <code>right</code>, <code>top</code> and
     *        <code>bottom</code> fields indicate the width of the
     *        border at the respective edge.
     *
     * @return the same object that was passed for <code>insets</code>.
     *
     * @see #getBorderInsets(Component)
     */
    public Insets getBorderInsets(Component c, Insets insets)
    {
      /* The exact amount has been determined using a test program
       * that was run on the Sun reference implementation. With
       * Apple/Sun JDK 1.3.1 on MacOS X 10.1.5, the result is
       * [3, 3, 3, 3]. With Sun JDK 1.4.1_01 on Linux/x86, the
       * result is [2, 3, 3, 3]. We use the values from the 1.4.1_01
       * release.
       */
      if (insets == null)
        return new Insets(2, 3, 3, 3);

      insets.top = 2;
      insets.bottom = insets.left = insets.right = 3;
      return insets;
    }
  }
  
  
  /**
   * A border that makes its enclosed component appear as lowered
   * into the surface. Typically used for text fields.
   *
   * <p><img src="doc-files/BasicBorders.FieldBorder-1.png" width="500"
   * height="200" alt="[A screen shot of this border]" />
   *
   * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawEtchedRect
   *
   * @author Sascha Brawer (brawer@dandelis.ch)
   */
  public static class FieldBorder
    extends AbstractBorder
    implements UIResource
  {
    /**
     * Determined using the <code>serialver</code> tool
     * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
     */
    static final long serialVersionUID = 949220756998454908L;


    /**
     * The color for drawing the outer half of the top and left
     * edges.
     */
    protected Color shadow;


    /**
     * The color for drawing the inner half of the top and left
     * edges.
     */
    protected Color darkShadow;


    /**
     * The color for drawing the inner half of the bottom and right
     * edges.
     */
    protected Color highlight;


    /**
     * The color for drawing the outer half of the bottom and right
     * edges.
     */
    protected Color lightHighlight;


    /**
     * Constructs a new border for drawing a text field in the Basic
     * look and feel.
     *
     * @param shadow the color for drawing the outer half
     *        of the top and left edges.
     *
     * @param darkShadow the color for drawing the inner half
     *        of the top and left edges.
     *
     * @param highlight the color for drawing the inner half
     *        of the bottom and right edges.
     *
     * @param lightHighlight the color for drawing the outer half
     *        of the bottom and right edges.
     */
    public FieldBorder(Color shadow, Color darkShadow,
                       Color highlight, Color lightHighlight)
    {
      /* These colors usually come from the UIDefaults of the current
       * look and feel. Use fallback values if the colors are not
       * supplied.  The API specification is silent about what
       * behavior is expected for null colors, so users should not
       * rely on this fallback (which is why it is not documented in
       * the above Javadoc).
       */
      this.shadow = (shadow != null) ? shadow : Color.gray;
      this.darkShadow = (darkShadow != null) ? darkShadow : Color.black;
      this.highlight = (highlight != null) ? highlight : Color.lightGray;
      this.lightHighlight = (lightHighlight != null)
        ? lightHighlight : Color.white;
    }

    
    /**
     * Paints the FieldBorder around a given component.
     *
     * @param c the component whose border is to be painted.
     * @param g the graphics for painting.
     * @param x the horizontal position for painting the border.
     * @param y the vertical position for painting the border.
     * @param width the width of the available area for painting the border.
     * @param height the height of the available area for painting the border.
     *
     * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawEtchedRect
     */
    public void paintBorder(Component c, Graphics  g,
                            int x, int y, int width, int height)
    {
      BasicGraphicsUtils.drawEtchedRect(g, x, y, width, height,
                                        shadow, darkShadow,
                                        highlight, lightHighlight);
    }
    
    
    /**
     * Measures the width of this border.
     *
     * @param c the component whose border is to be measured.
     *        If <code>c</code> is an instance of {@link
     *        javax.swing.text.JTextComponent}, its margin is
     *        added to the border size.
     *
     * @return an Insets object whose <code>left</code>,
     *         <code>right</code>, <code>top</code> and
     *         <code>bottom</code> fields indicate the width of the
     *         border at the respective edge.
     *
     * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
     */
    public Insets getBorderInsets(Component c)
    {
      return getBorderInsets(c, null);
    }


    /**
     * Measures the width of this border, storing the results into a
     * pre-existing Insets object.
     *
     * @param c the component whose border is to be measured.
     *        If <code>c</code> is an instance of {@link
     *        javax.swing.text.JTextComponent}, its margin is
     *        added to the border size.
     *
     * @param insets an Insets object for holding the result values.
     *        After invoking this method, the <code>left</code>,
     *        <code>right</code>, <code>top</code> and
     *        <code>bottom</code> fields indicate the width of the
     *        border at the respective edge.
     *
     * @return the same object that was passed for <code>insets</code>.
     *
     * @see #getBorderInsets(Component)
     */
    public Insets getBorderInsets(Component c, Insets insets)
    {
      if (insets == null)
        insets = new Insets(2, 2, 2, 2);
      else
        insets.top = insets.left = insets.bottom = insets.right = 2;

      if (c instanceof JTextComponent)
      {
        Insets margin = ((JTextComponent) c).getMargin();
        insets.top += margin.top;
        insets.left += margin.left;
        insets.bottom += margin.bottom;
        insets.right += margin.right;
      }

      return insets;
    }
  }
  
  
  /**
   * An invisible, but spacing border whose margin is determined
   * by calling the <code>getMargin()</code> method of the enclosed
   * component.  If the enclosed component has no such method,
   * this border will not occupy any space.
   *
   * <p><img src="doc-files/BasicBorders.MarginBorder-1.png" width="325"
   * height="200" alt="[An illustration that shows how MarginBorder
   * determines its borders]" />
   *
   * @author Sascha Brawer (brawer@dandelis.ch)
   */
  public static class MarginBorder
    extends AbstractBorder
    implements Serializable, UIResource
  {
    /**
     * Determined using the <code>serialver</code> tool
     * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
     */
    static final long serialVersionUID = -3035848353448896090L;
    
    
    /**
     * Constructs a new MarginBorder.
     */
    public MarginBorder()
    {
      // Nothing to do here.
    }
    
    /**
     * Measures the width of this border.
     *
     * @param c the component whose border is to be measured.
     *
     * @return an Insets object whose <code>left</code>, <code>right</code>,
     *         <code>top</code> and <code>bottom</code> fields indicate the
     *         width of the border at the respective edge.
     *
     * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
     */
    public Insets getBorderInsets(Component c)
    {
      return getBorderInsets(c, new Insets(0, 0, 0, 0));
    }
    
    
    /**
     * Determines the insets of this border by calling the
     * <code>getMargin()</code> method of the enclosed component.  The
     * resulting margin will be stored into the the <code>left</code>,
     * <code>right</code>, <code>top</code> and <code>bottom</code>
     * fields of the passed <code>insets</code> parameter.
     *
     * <p>Unfortunately, <code>getMargin()</code> is not a method of
     * {@link javax.swing.JComponent} or some other common superclass
     * of things with margins. While reflection could be used to
     * determine the existence of this method, this would be slow on
     * many virtual machines. Therefore, the current implementation
     * knows about {@link javax.swing.AbstractButton#getMargin()},
     * {@link javax.swing.JPopupMenu#getMargin()}, {@link
     * javax.swing.JToolBar#getMargin()}, and {@link
     * javax.swing.text.JTextComponent}. If <code>c</code> is an
     * instance of a known class, the respective
     * <code>getMargin()</code> method is called to determine the
     * correct margin. Otherwise, a zero-width margin is returned.
     *
     * @param c the component whose border is to be measured.
     *
     * @return the same object that was passed for <code>insets</code>,
     *         but with changed fields.
     */
    public Insets getBorderInsets(Component c, Insets insets)
    {
      Insets margin = null;

      /* This is terrible object-oriented design. See the above Javadoc
       * for an excuse.
       */
      if (c instanceof AbstractButton)
        margin = ((AbstractButton) c).getMargin();
      else if (c instanceof JPopupMenu)
        margin = ((JPopupMenu) c).getMargin();
      else if (c instanceof JToolBar)
        margin = ((JToolBar) c).getMargin();
      else if (c instanceof JTextComponent)
        margin = ((JTextComponent) c).getMargin();
      
      if (margin == null)
        insets.top = insets.left = insets.bottom = insets.right = 0;
      else
      {
        insets.top = margin.top;
        insets.left = margin.left;
        insets.bottom = margin.bottom;
        insets.right = margin.right;
      }

      return insets;
    }
  }
  

  /**
   * A border for drawing a separator line below JMenuBar.
   *
   * <p><img src="doc-files/BasicBorders.MenuBarBorder-1.png" width="500"
   * height="140" alt="[A screen shot of a JMenuBar with this border]" />
   *
   * @author Sascha Brawer (brawer@dandelis.ch)
   */
  public static class MenuBarBorder
    extends AbstractBorder
    implements UIResource
  {
    /**
     * Determined using the <code>serialver</code> tool
     * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
     */
    static final long serialVersionUID = -6909056571935227506L;
    
    
    /**
     * The shadow color, which is used for the upper line of the
     * two-pixel thick bottom edge.
     */
    private Color shadow;


    /**
     * The highlight color, which is used for the lower line of the
     * two-pixel thick bottom edge.
     */
    private Color highlight;


    /**
     * Constructs a new MenuBarBorder for drawing a JMenuBar in
     * the Basic look and feel.
     *
     * <p><img src="doc-files/BasicBorders.MenuBarBorder-1.png" width="500"
     * height="140" alt="[A screen shot of a JMenuBar with this
     * border]" />
     *
     * @param shadow the shadow color, which is used for the upper
     *        line of the two-pixel thick bottom edge.
     *
     * @param highlight the shadow color, which is used for the lower
     *        line of the two-pixel thick bottom edge.
     */
    public MenuBarBorder(Color shadow, Color highlight)
    {
      /* These colors usually come from the UIDefaults of the current
       * look and feel. Use fallback values if the colors are not
       * supplied.  The API specification is silent about what
       * behavior is expected for null colors, so users should not
       * rely on this fallback (which is why it is not documented in
       * the above Javadoc).
       */
      this.shadow = (shadow != null) ? shadow : Color.gray;
      this.highlight = (highlight != null) ? highlight : Color.white;
    }


    /**
     * Paints the MenuBarBorder around a given component.
     *
     * @param c the component whose border is to be painted, usually
     *        an instance of {@link javax.swing.JMenuBar}.
     *
     * @param g the graphics for painting.
     * @param x the horizontal position for painting the border.
     * @param y the vertical position for painting the border.
     * @param width the width of the available area for painting the border.
     * @param height the height of the available area for painting the border.
     */
    public void paintBorder(Component c, Graphics  g,
                            int x, int y, int width, int height)
    {
      Color oldColor;

      /* To understand this code, it might be helpful to look at the
       * image "BasicBorders.MenuBarBorder-1.png" that is included
       * with the JavaDoc. It is located in the "doc-files"
       * subdirectory.
       */
      oldColor = g.getColor();
      y = y + height - 2;
      try
      {
        g.setColor(shadow);
        g.drawLine(x, y, x + width - 2, y);
        g.drawLine(x, y + 1, x, y + 1);
        g.drawLine(x + width - 2, y + 1, x + width - 2, y + 1);

        g.setColor(highlight);
        g.drawLine(x + 1, y + 1, x + width - 3, y + 1);
        g.drawLine(x + width - 1, y, x + width - 1, y + 1);        
      }
      finally
      {
        g.setColor(oldColor);
      }
    }


    /**
     * Measures the width of this border.
     *
     * @param c the component whose border is to be measured.
     *
     * @return an Insets object whose <code>left</code>,
     *         <code>right</code>, <code>top</code> and
     *         <code>bottom</code> fields indicate the width of the
     *         border at the respective edge.
     *
     * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
     */
    public Insets getBorderInsets(Component c)
    {
      /* There is no obvious reason for overriding this method, but we
       * try to have exactly the same API as the Sun reference
       * implementation.
       */
      return getBorderInsets(c, null);
    }


    /**
     * Measures the width of this border, storing the results into a
     * pre-existing Insets object.
     *
     * @param insets an Insets object for holding the result values.
     *        After invoking this method, the <code>left</code>,
     *        <code>right</code>, <code>top</code> and
     *        <code>bottom</code> fields indicate the width of the
     *        border at the respective edge.
     *
     * @return the same object that was passed for <code>insets</code>.
     *
     * @see #getBorderInsets(Component)
     */
    public Insets getBorderInsets(Component c, Insets insets)
    {
      /* The exact amount has been determined using a test program
       * that was run on the Apple/Sun JDK 1.3.1 on MacOS X, and the
       * Sun JDK 1.4.1_01 on GNU/Linux for x86. Both gave [0,0,2,0],
       * which was expected from looking at the screen shot.
       */
      if (insets == null)
        return new Insets(0, 0, 2, 0);

      insets.left = insets.right = insets.top = 0;
      insets.bottom = 2;
      return insets;
    }
  }


  /**
   * A border for drawing radio buttons in the Basic look and feel.
   *
   * <p><img src="doc-files/BasicBorders.RadioButtonBorder-1.png" width="300"
   * height="135" alt="[A screen shot of this border]" />
   *
   * <p>Note about the screen shot: Normally, the
   * <code>borderPainted</code> property is <code>false</code> for
   * JRadioButtons. For this screen shot, it has been set to
   * <code>true</code> so the borders get drawn. Also, a
   * concretization of the Basic look and would typically provide
   * icons for the various states of radio buttons.
   *
   * <p>Note that the focus rectangle is invisible If the radio button
   * is currently selected. While it might be debatable whether this
   * makes a lot of sense, this behavior can be observed in the Sun
   * reference implementation (in JDK 1.3.1 and 1.4.1). The Classpath
   * implementation tries to exactly replicate the JDK appearance.
   *
   * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
   *
   * @author Sascha Brawer (brawer@dandelis.ch)
   */
  public static class RadioButtonBorder
    extends ButtonBorder
  {
    /**
     * Determined using the <code>serialver</code> tool
     * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
     */
    static final long serialVersionUID = 1596945751743747369L;


    /**
     * Constructs a new border for drawing a JRadioButton in
     * the Basic look and feel.
     *
     * @param shadow the shadow color.
     * @param darkShadow a darker variant of the shadow color.
     * @param highlight the highlight color.
     * @param lightHighlight a brighter variant of the highlight  color.
     */
    public RadioButtonBorder(Color shadow, Color darkShadow,
                             Color highlight, Color lightHighlight)
    {
      /* The superclass ButtonBorder substitutes null arguments
       * with fallback colors.
       */
      super(shadow, darkShadow, highlight, lightHighlight);
    }


    /**
     * Paints the RadioButtonBorder around a given component.
     *
     * <p>The Sun implementation always seems to draw exactly
     * the same border, irrespective of the state of the button.
     * This is rather surprising, but GNU Classpath emulates the
     * observable behavior.
     *
     * @param c the component whose border is to be painted.
     * @param g the graphics for painting.
     * @param x the horizontal position for painting the border.
     * @param y the vertical position for painting the border.
     * @param width the width of the available area for painting the border.
     * @param height the height of the available area for painting the border.
     *
     * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
     */
    public void paintBorder(Component c, Graphics  g,
                            int x, int y, int width, int height)
    {
      AbstractButton button = null;
      ButtonModel bmodel = null;
      boolean lowered = false;
      boolean focused = false;

      if (c instanceof AbstractButton)
      {
        button = (AbstractButton) c;
        bmodel = button.getModel();
      }

      if (bmodel != null)
      {
        lowered = button.isSelected()
          || (/* mouse inside */ bmodel.isArmed() && bmodel.isPressed());
        focused = button.hasFocus() && button.isFocusPainted();        
      }

      if (lowered)
        BasicGraphicsUtils.drawLoweredBezel(g, x, y, width, height,
                                            shadow, darkShadow,
                                            highlight, lightHighlight);
      else
        BasicGraphicsUtils.drawBezel(g, x, y, width, height,
                                     /* isPressed */ false,
                                     /* isPefault */ focused,
                                     shadow, darkShadow,
                                     highlight, lightHighlight);
    }
    
    
    /**
     * Measures the width of this border.
     *
     * @param c the component whose border is to be measured.
     *
     * @return an Insets object whose <code>left</code>,
     *         <code>right</code>, <code>top</code> and
     *         <code>bottom</code> fields indicate the width of the
     *         border at the respective edge.
     *
     * @see #getBorderInsets(java.awt.Component, java.awt.Insets) 
     */
    public Insets getBorderInsets(Component c)
    {
      /* There is no obvious reason for overriding this method, but we
       * try to have exactly the same API as the Sun reference
       * implementation.
       */
      return getBorderInsets(c, null);
    }

    
    /**
     * Measures the width of this border, storing the results into a
     * pre-existing Insets object.
     *
     * @param insets an Insets object for holding the result values.
     *        After invoking this method, the <code>left</code>,
     *        <code>right</code>, <code>top</code> and
     *        <code>bottom</code> fields indicate the width of the
     *        border at the respective edge.
     *
     * @return the same object that was passed for <code>insets</code>.
     *
     * @see #getBorderInsets(Component)
     */
    public Insets getBorderInsets(Component c, Insets insets)
    {
      /* The exact amount has been determined using a test program
       * that was run on the Apple/Sun JDK 1.3.1 on MacOS X, and the
       * Sun JDK 1.4.1_01 on GNU/Linux for x86. Both gave [2,2,2,2].
       */
      if (insets == null)
        return new Insets(2, 2, 2, 2);

      insets.left = insets.right = insets.top = insets.bottom = 2;
      return insets;
    }
  }


  /**
   * A one-pixel thick border for rollover buttons, for example in
   * tool bars.
   *
   * @since 1.4
   * @author Sascha Brawer (brawer@dandelis.ch)
   */
  public static class RolloverButtonBorder
    extends ButtonBorder
  {
    /**
     * Determined using the <code>serialver</code> tool
     * of Sun JDK 1.4.1_01 on GNU/Linux 2.4.20 for x86.
     */
    static final long serialVersionUID = 1976364864896996846L;


    /**
     * Constructs a new border for drawing a roll-over button
     * in the Basic look and feel.
     *
     * @param shadow the shadow color.
     * @param darkShadow a darker variant of the shadow color.
     * @param highlight the highlight color.
     * @param lightHighlight a brighter variant of the highlight  color.
     */
    public RolloverButtonBorder(Color shadow, Color darkShadow,
                                Color highlight, Color lightHighlight)
    {
      super(shadow, darkShadow, highlight, lightHighlight);
    }


    /**
     * Paints the border around a rollover button.  If <code>c</code>
     * is not an {@link javax.swing.AbstractButton} whose model
     * returns <code>true</code> for {@link
     * javax.swing.ButtonModel#isRollover}, nothing gets painted at
     * all.
     *
     * @param c the button whose border is to be painted.
     * @param g the graphics for painting.
     * @param x the horizontal position for painting the border.
     * @param y the vertical position for painting the border.
     * @param width the width of the available area for painting the border.
     * @param height the height of the available area for painting the border.
     */
    public void paintBorder(Component c, Graphics  g,
                            int x, int y, int width, int height)
    {
      ButtonModel bmodel = null;
      boolean drawPressed;
      Color oldColor = g.getColor();
      int x2, y2;

      if (c instanceof AbstractButton)
        bmodel = ((AbstractButton) c).getModel();

      /* Draw nothing if c is not a rollover button. */
      if ((bmodel == null) || !bmodel.isRollover())
        return;

      /* Draw nothing if the mouse is pressed, but outside the button. */
      if (bmodel.isPressed() && !bmodel.isArmed())
        return;

      drawPressed = bmodel.isSelected() || bmodel.isPressed();
      x2 = x + width - 1;
      y2 = y + height - 1;

      try
      {
        g.setColor(drawPressed ? shadow : lightHighlight);
        g.drawLine(x, y, x2 - 1, y);     // top edge
        g.drawLine(x, y + 1, x, y2 - 1); // left edge

        g.setColor(drawPressed ? lightHighlight : shadow);
        g.drawLine(x, y2, x2, y2);       // bottom edge
        g.drawLine(x2, y, x2, y2 - 1);   // right edge
      }
      finally
      {
        g.setColor(oldColor);
      }
    }
  }


  /**
   * A border for JSplitPanes in the Basic look and feel. The divider
   * in the middle of the JSplitPane has its own border class, of which
   * an instance can be obtained with {@link #getSplitPaneDividerBorder()}.
   *
   * <p><img src="doc-files/BasicBorders.SplitPaneBorder-1.png" width="520"
   * height="200" alt="[A screen shot for JSplitPane.HORIZONTAL_SPLIT]" />
   *
   * <p><img src="doc-files/BasicBorders.SplitPaneBorder-2.png" width="520"
   * height="200" alt="[A screen shot for JSplitPane.VERTICAL_SPLIT]" />
   *
   * <p>In contrast to the other borders of the Basic look and feel,
   * this class is not serializable. While this might be unintended,
   * GNU Classpath follows the specification in order to be fully
   * compatible with the Sun reference implementation.
   *
   * <p>In the Sun JDK, the bottom edge of the divider also gets
   * painted if the orientation of the enclosed JSplitPane is
   * <code>JSplitPane.VERTICAL_SPLIT</code> (at least in versions
   * 1.3.1 and 1.4.1).  GNU Classpath does not replicate this bug. A
   * report has been filed with Sun (bug ID 4885629).
   *
   * <p>Note that the bottom left pixel of the border has a different
   * color depending on the orientation of the enclosed JSplitPane.
   * Although this is visually inconsistent, Classpath replicates the
   * appearance of the Sun reference implementation. A bug report has
   * been filed with Sun (review ID 188774).
   *
   * @see #getSplitPaneBorder()
   * @see #getSplitPaneDividerBorder()
   *
   * @author Sascha Brawer (brawer@dandelis.ch)
   */
  public static class SplitPaneBorder implements Border, UIResource
  {
    /**
     * Indicates that the top edge shall be not be painted
     * by {@link #paintRect}.
     */
    private static final int SUPPRESS_TOP = 1;


    /**
     * Indicates that the left edge shall be not be painted
     * by {@link #paintRect}.
     */
    private static final int SUPPRESS_LEFT = 2;


    /**
     * Indicates that the bottom edge shall be not be painted
     * by {@link #paintRect}.
     */
    private static final int SUPPRESS_BOTTOM = 4;


    /**
     * Indicates that the right edge shall be not be painted
     * by {@link #paintRect}.
     */
    private static final int SUPPRESS_RIGHT = 8;


    /**
     * The color for drawing the bottom and right edges of the border.
     */
    protected Color highlight;


    /**
     * The color for drawing the top and left edges of the border.
     */
    protected Color shadow;


    /**
     * Constructs a new border for drawing a JSplitPane in the Basic
     * look and feel.  The divider in the middle of the JSplitPane has
     * its own border class, <code>SplitPaneDividerBorder</code>.
     *
     * @param shadow the shadow color.
     * @param highlight the highlight color.
     */
    public SplitPaneBorder(Color highlight, Color shadow)
    {
      /* These colors usually come from the UIDefaults of the current
       * look and feel. Use fallback values if the colors are not
       * supplied.  The API specification is silent about what
       * behavior is expected for null colors, so users should not
       * rely on this fallback (which is why it is not documented in
       * the above Javadoc).
       */
      this.shadow = (shadow != null) ? shadow : Color.black;
      this.highlight = (highlight != null) ? highlight : Color.white;
    }


    /**
     * Paints the border around a <code>JSplitPane</code>.
     *
     * <p><img src="doc-files/BasicBorders.SplitPaneBorder-1.png" width="520"
     * height="200" alt="[A screen shot for JSplitPane.HORIZONTAL_SPLIT]" />
     *
     * <p><img src="doc-files/BasicBorders.SplitPaneBorder-2.png" width="520"
     * height="200" alt="[A screen shot for JSplitPane.VERTICAL_SPLIT]" />
     *
     * @param c the <code>JSplitPane</code> whose border is to be painted.
     * @param g the graphics for painting.
     * @param x the horizontal position for painting the border.
     * @param y the vertical position for painting the border.
     * @param width the width of the available area for painting the border.
     * @param height the height of the available area for painting the border.
     */
    public void paintBorder(Component c, Graphics  g,
                            int x, int y, int width, int height)
    {
      JSplitPane splitPane;
      Component content;

      if (!(c instanceof JSplitPane))
        return;

      splitPane = (JSplitPane) c;
      switch (splitPane.getOrientation())
      {
      case JSplitPane.HORIZONTAL_SPLIT:
        if ((content = splitPane.getLeftComponent()) != null)
          paintRect(g, SUPPRESS_RIGHT, true, x, y, content.getBounds());
        if ((content = splitPane.getRightComponent()) != null)
          paintRect(g, SUPPRESS_LEFT, true, x, y, content.getBounds());
        break;

      case JSplitPane.VERTICAL_SPLIT:
        if ((content = splitPane.getTopComponent()) != null)
          paintRect(g, SUPPRESS_BOTTOM, false, x, y, content.getBounds());
        if ((content = splitPane.getBottomComponent()) != null)
          paintRect(g, SUPPRESS_TOP, false, x, y, content.getBounds());
        break;
      }
    }


    /**
     * Paints a border around a child of a <code>JSplitPane</code>,
     * omitting some of the edges.
     *
     * @param g the graphics for painting.
     *
     * @param suppress a bit mask indicating the set of suppressed
     *        edges, for example <code>SUPPRESS_TOP | SUPPRESS_RIGHT</code>.
     *
     * @param x the x coordinate of the SplitPaneBorder.
     *
     * @param y the y coordinate of the SplitPaneBorder.
     *
     * @param shadeBottomLeftPixel <code>true</code> to paint the
     *        bottom left pixel in the shadow color,
     *        <code>false</code> for the highlight color. The Basic
     *        look and feel uses the highlight color for the bottom
     *        left pixel of the border of a JSplitPane whose
     *        orientation is VERTICAL_SPLIT, and the shadow color
     *        otherwise. While this might be a strange distinction,
     *        Classpath tries to look identical to the reference
     *        implementation. A bug report has been filed with Sun;
     *        its review ID is 188774. We currently replicate the
     *        Sun behavior.
     *
     * @param rect the bounds of the child of JSplitPane whose
     *        border is to be painted.
     */
    private void paintRect(Graphics g, int suppress,
                           boolean shadeBottomLeftPixel,
                           int x, int y,
                           Rectangle rect)
    {
      if (rect == null)
        return;

      /* On each edge, the border exceeds the enclosed child by one
       * pixel. See the image "BasicBorders.SplitPaneBorder-1.png" in
       * the directory "doc-files".
       */
      x += rect.x - 1;
      y += rect.y - 1;
      int right = x + rect.width + 1;
      int bottom = y + rect.height + 1;
      
      Color oldColor = g.getColor();
      try
      {
        g.setColor(shadow);
        if ((suppress & SUPPRESS_TOP) == 0)
          g.drawLine(x, y, right, y);
        if ((suppress & SUPPRESS_LEFT) == 0)
          g.drawLine(x, y, x, bottom);
        else
          g.drawLine(x, bottom, x, bottom); // one pixel

        g.setColor(highlight);
        if ((suppress & SUPPRESS_BOTTOM) == 0)
          g.drawLine(x + (shadeBottomLeftPixel ? 1 : 0), bottom, right, bottom);
        else if (!shadeBottomLeftPixel)
          g.drawLine(x, bottom, x, bottom); // one pixel

        if ((suppress & SUPPRESS_RIGHT) == 0)
          g.drawLine(right, y, right, bottom);
      }
      finally
      {
        g.setColor(oldColor);
      }
    }

    
    /**
     * Measures the width of this border.
     *
     * @param c the component whose border is to be measured, usually
     *        an instance of {@link javax.swing.JSplitPane}.
     *
     * @return an Insets object whose <code>left</code>,
     *         <code>right</code>, <code>top</code> and
     *         <code>bottom</code> fields indicate the width of the
     *         border at the respective edge.
     */
    public Insets getBorderInsets(Component c)
    {
      return new Insets(1, 1, 1, 1);
    }


    /**
     * Determines whether this border fills every pixel in its area
     * when painting.
     *
     * @return <code>false</code> because this border does not
     *         paint over the pixels where the divider joins
     *         the border.
     */
    public boolean isBorderOpaque()
    {
      /* Strangely, the Sun implementation (tested with JDK 1.3.1 and
       * 1.4.1_01) seems to always return true. It could be a bug,
       * but without knowing the details of their implementation, it is
       * hard to decide.
       */
      return false;
    }
  }


  /**
   * A border for the divider inside a JSplitPane.
   *
   * <p><img src="doc-files/BasicBorders.SplitPaneDividerBorder-1.png"
   * width="520" height="200" alt="[A screen shot of this border]" />
   *
   * @author Sascha Brawer (brawer@dandelis.ch)
   */
  private static class SplitPaneDividerBorder
    implements Border, UIResource, Serializable
  {
    /**
     * The highlight color, which is drawn on the left or top edge
     * depending on the orientation of the JSplitPanel.
     */
    protected Color highlight;


    /**
     * The highlight color, which is drawn on the right or bottom edge
     * depending on the orientation of the JSplitPanel.
     */
    protected Color shadow;


    /**
     * Constructs a new border for drawing the divider of a JSplitPane
     * in the Basic look and feel.  The outer parts of the JSplitPane have
     * their own border class, <code>SplitPaneBorder</code>.
     *
     * @param shadow the shadow color.
     * @param highlight the highlight color.
     */
    public SplitPaneDividerBorder(Color highlight, Color shadow)
    {
      this.highlight = (highlight != null) ? highlight : Color.white;
      this.shadow = (shadow != null) ? shadow : Color.black;
    }


    /**
     * Paints the border around the divider of a <code>JSplitPane</code>.
     *
     * <p><img src="doc-files/BasicBorders.SplitPaneDividerBorder-1.png"
     * width="520" height="200" alt="[A picture that shows which pixels
     * get painted in what color]" />
     *
     * @param c the <code>JSplitPane</code> whose divider&#x2019;s border
     *        is to be painted.
     * @param g the graphics for painting.
     * @param x the horizontal position for painting the border.
     * @param y the vertical position for painting the border.
     * @param width the width of the available area for painting the border.
     * @param height the height of the available area for painting the border.
     */
    public void paintBorder(Component c, Graphics  g,
                            int x, int y, int width, int height)
    {
      Color oldColor, dcol;
      int x2, y2;
      JSplitPane sp;

      sp = getSplitPane(c);
      if (sp == null)
        return;

      x2 = x + width - 1;
      y2 = y + height - 1;
      oldColor = g.getColor();
      dcol = c.getBackground();
      try
      {
        switch (sp.getOrientation())
        {
        case JSplitPane.HORIZONTAL_SPLIT:
          g.setColor(dcol);
          g.drawLine(x + 1, y, x2 - 1, y);
          g.drawLine(x + 1, y2, x2 - 1, y2);
          g.setColor(sp.getLeftComponent() != null ? highlight : dcol);
          g.drawLine(x, y, x, y2);
          g.setColor(sp.getRightComponent() != null ? shadow : dcol);
          g.drawLine(x2, y, x2, y2);
          break;

        case JSplitPane.VERTICAL_SPLIT:
          g.setColor(dcol);
          g.drawLine(x, y + 1, x, y2 - 1);
          g.drawLine(x2, y + 1, x2, y2 - 1);
          g.setColor(sp.getTopComponent() != null ? highlight : dcol);
          g.drawLine(x, y, x2, y);
          g.setColor(sp.getBottomComponent() != null ? shadow : dcol);
          g.drawLine(x, y2, x2, y2);
          break;
        }
      }
      finally
      {
        g.setColor(oldColor);
      }
    }


    /**
     * Measures the width of this border.
     *
     * @param c the component whose border is to be measured, usually
     *        an instance of {@link javax.swing.JSplitPane}.
     *
     * @return an Insets object whose <code>left</code>,
     *         <code>right</code>, <code>top</code> and
     *         <code>bottom</code> fields indicate the width of the
     *         border at the respective edge.
     */
    public Insets getBorderInsets(Component c)
    {
      return new Insets(1, 1, 1, 1);
    }


    /**
     * Determines whether this border fills every pixel in its area
     * when painting.
     *
     * @return <code>true</code> if both highlight and shadow
     *         color are fully opaque.
     */
    public boolean isBorderOpaque()
    {
      return (highlight.getAlpha() == 255) && (shadow.getAlpha() == 255);
    }

    
    /**
     * Determines the JSplitPane whose divider is being painted.
     *
     * @param c an instance of BasicSplitPaneDivider.
     *
     * @return a <code>JSplitPane</code>, or <code>null</code> if
     *         <code>c</code> is not an instance of {@link
     *         javax.swing.plaf.basic.BasicSplitPaneDivider}.
     */
    private JSplitPane getSplitPane(Component c)
    {
      if (c instanceof BasicSplitPaneDivider)
        return (((BasicSplitPaneDivider) c).getBasicSplitPaneUI())
          .getSplitPane();
      else
        return null;
    }
  }


  /**
   * A border for toggle buttons in the Basic look and feel.
   *
   * <p><img src="doc-files/BasicBorders.ToggleButtonBorder-1.png"
   * width="270" height="135" alt="[A screen shot of this border]" />
   *
   * <p>The Sun implementation always seems to draw exactly
   * the same border, irrespective of the state of the button.
   * This is rather surprising, but GNU Classpath emulates the
   * observable behavior.
   *
   * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
   *
   * @author Sascha Brawer (brawer@dandelis.ch)
   */
  public static class ToggleButtonBorder
    extends ButtonBorder
  {
    /**
     * Determined using the <code>serialver</code> tool
     * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
     */
    static final long serialVersionUID = -3528666548001058394L;

    
    /**
     * Constructs a new border for drawing a JToggleButton in
     * the Basic look and feel.
     *
     * @param shadow the shadow color.
     * @param darkShadow a darker variant of the shadow color.
     * @param highlight the highlight color.
     * @param lightHighlight a brighter variant of the highlight  color.
     */
    public ToggleButtonBorder(Color shadow, Color darkShadow,
                              Color highlight, Color lightHighlight)
    {
      /* The superclass ButtonBorder substitutes null arguments
       * with fallback colors.
       */
      super(shadow, darkShadow, highlight, lightHighlight);
    }


    /**
     * Paints the ToggleButtonBorder around a given component.
     *
     * <p>The Sun implementation always seems to draw exactly
     * the same border, irrespective of the state of the button.
     * This is rather surprising, but GNU Classpath emulates the
     * observable behavior.
     *
     * @param c the component whose border is to be painted.
     * @param g the graphics for painting.
     * @param x the horizontal position for painting the border.
     * @param y the vertical position for painting the border.
     * @param width the width of the available area for painting the border.
     * @param height the height of the available area for painting the border.
     *
     * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
     */
    public void paintBorder(Component c, Graphics  g,
                            int x, int y, int width, int height)
    {
      /* The author of this code tried various variants for setting
       * the state of the enclosed JToggleButton, but it seems that
       * the drawn border is always identical. Weird, because this
       * means that the user does not see whether the JToggleButton
       * is selected or not.
       */
      BasicGraphicsUtils.drawBezel(g, x, y, width, height,
                                   /* pressed */ false, 
                                   /* default */ false,
                                   shadow, darkShadow,
                                   highlight, lightHighlight);
    }


    /**
     * Measures the width of this border.
     *
     * @param c the component whose border is to be measured.
     *
     * @return an Insets object whose <code>left</code>,
     *         <code>right</code>, <code>top</code> and
     *         <code>bottom</code> fields indicate the width of the
     *         border at the respective edge.
     *
     * @see #getBorderInsets(java.awt.Component, java.awt.Insets) 
     */
    public Insets getBorderInsets(Component c)
    {
      /* There is no obvious reason for overriding this method, but we
       * try to have exactly the same API as the Sun reference
       * implementation.
       */
      return getBorderInsets(c, null);
    }

    
    /**
     * Measures the width of this border, storing the results into a
     * pre-existing Insets object.
     *
     * @param insets an Insets object for holding the result values.
     *        After invoking this method, the <code>left</code>,
     *        <code>right</code>, <code>top</code> and
     *        <code>bottom</code> fields indicate the width of the
     *        border at the respective edge.
     *
     * @return the same object that was passed for <code>insets</code>.
     *
     * @see #getBorderInsets(Component)
     */
    public Insets getBorderInsets(Component c, Insets insets)
    {
      /* The exact amount has been determined using a test program
       * that was run on the Apple/Sun JDK 1.3.1 on MacOS X, and the
       * Sun JDK 1.4.1_01 on GNU/Linux for x86. Both gave [2,2,2,2].
       */
      if (insets == null)
        return new Insets(2, 2, 2, 2);

      insets.left = insets.right = insets.top = insets.bottom = 2;
      return insets;
    }
  }
}
