/* MetalBorders.java
   Copyright (C) 2005, 2006, 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.metal;

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Insets;

import javax.swing.AbstractButton;
import javax.swing.ButtonModel;
import javax.swing.JButton;
import javax.swing.JInternalFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.border.AbstractBorder;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.plaf.BorderUIResource;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicBorders;
import javax.swing.text.JTextComponent;


/**
 * A factory class that creates borders for the different Swing components.
 *
 * @author Roman Kennke (roman@kennke.org)
 */
public class MetalBorders
{

  /** The shared instance for getButtonBorder(). */
  private static Border buttonBorder;

  /** The shared instance for getToggleButtonBorder(). */
  private static Border toggleButtonBorder;

  /** The shared instance for getDesktopIconBorder(). */
  private static Border desktopIconBorder;

  /** The shared instance for getRolloverButtonBorder(). */
  private static Border toolbarButtonBorder;

  /** The shared instance for getTextFieldBorder(). */
  private static Border textFieldBorder;

  /** The shared instance for getTextBorder(). */
  private static Border textBorder;

  /** The shared instance for getRolloverBorder(). */
  private static Border rolloverBorder;

  /**
   * A MarginBorder that gets shared by multiple components.
   * Created on demand by the private helper function {@link
   * #getMarginBorder()}.
   */
  private static BasicBorders.MarginBorder marginBorder;

  /**
   * <p>A border used for {@link JButton} components.</p>
   * 
   * <p>This {@link Border} implementation can handle only instances of
   * {@link AbstractButton} and their subclasses.</p>
   * 
   * <p>If the Metal Look and Feel's current theme is 'Ocean' the border
   * will be painted with a special highlight when the mouse cursor if
   * over the button (ie. the property <code>rollover</code> of the
   * button's model is <code>true</code>) and is not a <b>direct</b>
   * child of a {@link JToolBar}.</p> 
   */
  public static class ButtonBorder extends AbstractBorder implements UIResource
  {
    /** The borders insets. */
    protected static Insets borderInsets = new Insets(3, 3, 3, 3);

    /**
     * Creates a new instance of <code>ButtonBorder</code>.
     */
    public ButtonBorder()
    {
      // Nothing to do here.
    }

    /**
     * Paints the button border.
     *
     * @param c the component for which we paint the border
     * @param g the Graphics context to use
     * @param x the X coordinate of the upper left corner of c
     * @param y the Y coordinate of the upper left corner of c
     * @param w the width of c
     * @param h the height of c
     */
    public void paintBorder(Component c, Graphics g, int x, int y, int w,
                            int h)
    {
      // With the OceanTheme the button border is painted entirely different.
      // However, I couldn't figure out how this is determined besides checking
      // for instanceof OceanTheme. The button painting is definitely not
      // influenced by a UI default property and it is definitely performed
      // by the same Border class.
      if (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme)
        paintOceanButtonBorder(c, g, x, y, w, h);
      else
        paintDefaultButtonBorder(c, g, x, y, w, h);
    }

    /**
     * Paints the button border for the DefaultMetalTheme.
     *
     * @param c the component (button)
     * @param g the graphics object to use
     * @param x the upper left corner of the component, X coordinate
     * @param y the upper left corner of the component, Y coordinate
     * @param w the width of the component
     * @param h the height of the component
     */
    private void paintDefaultButtonBorder(Component c, Graphics g, int x,
                                          int y, int w, int h)
    {
      ButtonModel bmodel = null;

      // The RI will fail with a ClassCastException in such a situation.
      // This code tries to be more helpful.
      if (c instanceof AbstractButton)
        bmodel = ((AbstractButton) c).getModel();
      else
        throw new IllegalStateException("A ButtonBorder is supposed to work "
                                        + "only with AbstractButton and"
                                        + "subclasses.");

      Color darkShadow = MetalLookAndFeel.getControlDarkShadow();
      Color shadow = MetalLookAndFeel.getControlShadow();
      Color light = MetalLookAndFeel.getControlHighlight();
      Color middle = MetalLookAndFeel.getControl();

      if (c.isEnabled())
        {
          // draw dark border
          g.setColor(darkShadow);
          g.drawRect(x, y, w - 2, h - 2);

          // If the button is the default button, we paint a special border,
          // regardless of the pressed state.
          if (c instanceof JButton && ((JButton) c).isDefaultButton())
            {
              g.drawRect(x + 1, y + 1, w - 4, h - 4);
              // Draw white highlight.
              g.setColor(light);
              g.drawLine(x + 2, y + 2, x + w - 4, y + 2);
              g.drawLine(x + 2, y + 2, x + 2, y + h - 4);
              g.drawLine(x + 2, y + h - 1, x + w - 1, y + h - 1);
              g.drawLine(x + w - 1, y + 2, x + w - 1, y + h - 1);
              // Draw crossing pixels.
              g.setColor(middle);
              g.fillRect(x + w - 2, y + 2, 1, 1);
              g.fillRect(x + 2, y + h - 2, 1, 1);
            }
          else
            {
              // The normal border. This is used when the button is not
              // pressed or the button is not armed.
              if (! (bmodel.isPressed() && bmodel.isArmed()))
                {
                  // draw light border
                  g.setColor(light);
                  g.drawRect(x + 1, y + 1, w - 2, h - 2);

                  // draw crossing pixels of both borders
                  g.setColor(middle);
                  g.drawLine(x + 1, y + h - 2, x + 1, y + h - 2);
                  g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1);
                }
              // The pressed border. This border is painted only when
              // the button is both pressed and armed.
              else
                {
                  // draw light border
                  g.setColor(light);
                  g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1);
                  g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1);

                  // draw shadow border
                  g.setColor(middle);
                  g.drawLine(x + 1, y + 1, x + w - 2, y + 1);
                  g.drawLine(x + 1, y + 1, x + 1, y + h - 2);
 
                  // draw crossing pixels of both borders
                  g.setColor(shadow);
                  g.drawRect(x + 1, y + h - 2, 0, 0);
                  g.drawRect(x + w - 2, y + 1, 0, 0);
                }
            }
        }
      else 
        {
          // draw disabled border
          g.setColor(MetalLookAndFeel.getInactiveControlTextColor());
          g.drawRect(x, y, w - 2, h - 2);          
        }
    }

    /**
     * Paints the button border for the OceanTheme.
     *
     * @param c the button
     * @param g the graphics context
     * @param x the X coordinate of the upper left corner of the painting rect
     * @param y the Y coordinate of the upper left corner of the painting rect
     * @param w the width of the painting rect
     * @param h the height of the painting rect
     */
    private void paintOceanButtonBorder(Component c, Graphics g, int x,
                                        int y, int w, int h)
    {
      ButtonModel bmodel = null;
      
      // The RI will fail with a ClassCastException in such a situation.
      // This code tries to be more helpful.
      if (c instanceof AbstractButton)
        bmodel = ((AbstractButton) c).getModel();
      else
        throw new IllegalStateException("A ButtonBorder is supposed to work "
                                        + "only with AbstractButton and"
                                        + "subclasses.");

      Color darkShadow = MetalLookAndFeel.getControlDarkShadow();
      Color shadow = MetalLookAndFeel.getControlShadow();
      Color light = MetalLookAndFeel.getControlHighlight();
      Color middle = MetalLookAndFeel.getControl();

      if (c.isEnabled())
        {
          // Paint the pressed border if the button is pressed, or if
          // the button is the default button. In the OceanTheme, the default
          // button has the same border as a pressed button.
          if (bmodel.isPressed() || ((c instanceof JButton)
                                     && ((JButton) c).isDefaultButton()))
            {
              // Draw fat border.
              g.setColor(darkShadow);
              g.drawRect(x, y, w - 1, h - 1);
              g.drawRect(x + 1, y + 1, w - 3, h - 3);
            }
          else if (bmodel.isRollover() && !(c.getParent() instanceof JToolBar))
            {
              // Paint a bigger border when the mouse is over the button but
              // only if it is *not* part of a JToolBar.
              g.setColor(shadow);
              g.drawRect(x, y, w - 1, h - 1);
              g.drawRect(x + 2, y + 2, w - 5, h - 5);
              g.setColor(darkShadow);
              g.drawRect(x + 1, y + 1, w - 3, h - 3);
            }
          else
            {
              g.setColor(darkShadow);
              g.drawRect(x, y, w - 1, h - 1);
            }
        }
      else 
        {
          // draw disabled border
          g.setColor(MetalLookAndFeel.getInactiveControlTextColor());
          g.drawRect(x, y, w - 2, h - 2);          
        }
    }

    /**
     * Returns the insets of the <code>ButtonBorder</code>.
     *
     * @param c the component for which the border is used (ignored).
     *
     * @return The insets of the <code>ButtonBorder</code>.
     */
    public Insets getBorderInsets(Component c)
    {
      return borderInsets;
    }

    /**
     * Returns the insets of the <code>ButtonBorder</code> in the specified 
     * <code>newInsets</code> object.
     *
     * @param c the component for which the border is used (ignored).
     * @param newInsets the insets object where to put the values (
     *                  <code>null</code> not permitted).
     *
     * @return The <code>newInsets</code> reference.
     */
    public Insets getBorderInsets(Component c, Insets newInsets)
    {
      newInsets.bottom = borderInsets.bottom;
      newInsets.left = borderInsets.left;
      newInsets.right = borderInsets.right;
      newInsets.top = borderInsets.top;
      return newInsets;
    }
  }

  /**
   * A border used when painting {@link JInternalFrame} instances.
   */
  static class DesktopIconBorder extends AbstractBorder
    implements UIResource
  {
    /**
     * Creates a new border instance.
     */
    public DesktopIconBorder()
    {
      // Nothing to do here.
    }
    
    /**
     * Returns the border insets.
     * 
     * @param c  the component (ignored).
     * 
     * @return The border insets.
     */
    public Insets getBorderInsets(Component c)
    {
      return getBorderInsets(c, null);
    }
    
    /**
     * Returns the border insets.
     * 
     * @param c  the component (ignored).
     * @return The border insets.
     */
    public Insets getBorderInsets(Component c, Insets newInsets)
    {
      if (newInsets == null)
        newInsets = new Insets(3, 3, 2, 3);
      else
        {
          newInsets.top = 3;
          newInsets.left = 3;
          newInsets.bottom = 2;
          newInsets.right = 3;
        }
      return newInsets;  
    }
    
    /**
     * Paints the border for the specified component.
     * 
     * @param c  the component.
     * @param g  the graphics device.
     * @param x  the x-coordinate.
     * @param y  the y-coordinate.
     * @param w  the width.
     * @param h  the height.
     */
    public void paintBorder(Component c, Graphics g, int x, int y, int w, 
        int h)
    {
      g.setColor(MetalLookAndFeel.getControlDarkShadow());      
      g.drawRect(x, y, w - 1, h - 1); 
    }
    
  }

  /**
   * A simple 3D border.
   */
  public static class Flush3DBorder extends AbstractBorder
    implements UIResource
  {
    private static final Insets borderInsets = new Insets(2, 2, 2, 2);
    
    /**
     * Creates a new border instance.
     */
    public Flush3DBorder()
    {
      // Nothing to do here.
    }
    
    /**
     * Returns the border insets.
     * 
     * @param c  the component (ignored).
     * 
     * @return The border insets.
     */
    public Insets getBorderInsets(Component c)
    {
      return borderInsets;
    }
    
    /**
     * Returns the border insets.
     * 
     * @param c  the component (ignored).
     * @param newInsets  an existing insets instance, that will be populated
     *                   with the border insets and returned as the result
     *                   (<code>null</code> not permitted).
     *                   
     * @return The <code>newInsets</code> reference.
     */
    public Insets getBorderInsets(Component c, Insets newInsets)
    {
      newInsets.top = borderInsets.top;
      newInsets.left = borderInsets.left;
      newInsets.bottom = borderInsets.bottom;
      newInsets.right = borderInsets.right;
      return newInsets;  
    }
    
    /**
     * Paints the border for the specified component.
     * 
     * @param c  the component (ignored).
     * @param g  the graphics device.
     * @param x  the x-coordinate.
     * @param y  the y-coordinate.
     * @param w  the width.
     * @param h  the height.
     */
    public void paintBorder(Component c, Graphics g, int x, int y, int w, 
        int h)
    {              
      Color savedColor = g.getColor();
      g.setColor(MetalLookAndFeel.getControlDarkShadow());
      g.drawRect(x, y, w - 2, h - 2);
      g.setColor(MetalLookAndFeel.getControlHighlight());
      g.drawRect(x + 1, y + 1, w - 2, h - 2);
      g.setColor(MetalLookAndFeel.getControl());
      g.drawLine(x + 1, y + h - 2, x + 1, y + h - 2);
      g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1);
      g.setColor(savedColor);
    }
    
  }
    
  /**
   * A border used for a {@link JInternalFrame} when it is being used as a 
   * palette.
   * 
   * @since 1.3
   */
  public static class PaletteBorder extends AbstractBorder
    implements UIResource
  {
    private static final Insets borderInsets = new Insets(1, 1, 1, 1);

    /**
     * Creates a new <code>PaletteBorder</code>.
     */
    public PaletteBorder()
    {
      // Nothing to do here.
    }
    
    /**
     * Returns the border insets.
     * 
     * @param c  the component (ignored).
     * 
     * @return The border insets.
     */
    public Insets getBorderInsets(Component c)
    {
      return borderInsets;
    }

    /**
     * Returns the border insets.
     * 
     * @param c  the component (ignored).
     * @param newInsets  an existing insets instance, that will be populated
     *                   with the border insets and returned as the result
     *                   (<code>null</code> not permitted).
     *                   
     * @return The <code>newInsets</code> reference.
     */
    public Insets getBorderInsets(Component c, Insets newInsets)
    {        
      newInsets.top = borderInsets.top;
      newInsets.left = borderInsets.left;
      newInsets.bottom = borderInsets.bottom;
      newInsets.right = borderInsets.right;
      return newInsets;  
    }
    
    /**
     * Paints the border for the specified component.
     * 
     * @param c  the component (ignored).
     * @param g  the graphics device.
     * @param x  the x-coordinate.
     * @param y  the y-coordinate.
     * @param w  the width.
     * @param h  the height.
     */
    public void paintBorder(Component c, Graphics g, int x, int y, int w, 
            int h)
    {
      Color savedColor = g.getColor();
      
      // draw the outline
      g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
      g.drawRect(x, y, w - 1, h - 1);
      
      // put a dot in each corner
      g.setColor(MetalLookAndFeel.getControl());
      g.fillRect(x, y, 1, 1);
      g.fillRect(x + w - 1, y, 1, 1);
      g.fillRect(x + w - 1, y + h - 1, 1, 1);
      g.fillRect(x, y + h - 1, 1, 1);      
      g.setColor(savedColor);
    }

  }
    
  /**
   * A border used for the {@link JTextField} component.
   */
  public static class TextFieldBorder extends Flush3DBorder
    implements UIResource
  {
    /**
     * Creates a new border instance.
     */
    public TextFieldBorder()
    {
      // Nothing to do here.
    }
    
    /**
     * Paints the border for the specified component.
     * 
     * @param c  the component (ignored).
     * @param g  the graphics device.
     * @param x  the x-coordinate.
     * @param y  the y-coordinate.
     * @param w  the width.
     * @param h  the height.
     */
    public void paintBorder(Component c, Graphics g, int x, int y, int w, 
        int h)
    {
      boolean enabledTextBorder;
      if (c instanceof JTextComponent)
        {
          JTextComponent tc = (JTextComponent) c;
          enabledTextBorder = tc.isEnabled() && tc.isEditable();
        }
      else
        enabledTextBorder = false;

      if (enabledTextBorder)
        super.paintBorder(c, g, x, y, w, h);
      else
        {
          Color savedColor = g.getColor();
          g.setColor(MetalLookAndFeel.getControlShadow());
          g.drawRect(x, y, w - 1, h - 1);
          g.setColor(savedColor);
        }
    }
    
  }

  /**
   * A border used for the {@link JInternalFrame} component.
   */
  public static class InternalFrameBorder extends AbstractBorder
    implements UIResource
  {
    private static final Insets borderInsets = new Insets(5, 5, 5, 5);

    /**
     * Creates a new border instance.
     */
    public InternalFrameBorder()
    {
      // Nothing to do here.
    }
    
    /**
     * Returns the border insets.
     * 
     * @param c  the component (ignored).
     * 
     * @return The border insets.
     */
    public Insets getBorderInsets(Component c)
    {
      return borderInsets;
    }
    
    /**
     * Returns the border insets.
     * 
     * @param c  the component (ignored).
     * @param newInsets  an existing insets instance, that will be populated
     *                   with the border insets and returned as the result
     *                   (<code>null</code> not permitted).
     *                   
     * @return The <code>newInsets</code> reference.
     */
    public Insets getBorderInsets(Component c, Insets newInsets)
    {
      newInsets.top = borderInsets.top;
      newInsets.left = borderInsets.left;
      newInsets.bottom = borderInsets.bottom;
      newInsets.right = borderInsets.right;
      return newInsets;  
    }
    
    /**
     * Paints the border for the specified component.
     * 
     * @param c  the component.
     * @param g  the graphics device.
     * @param x  the x-coordinate.
     * @param y  the y-coordinate.
     * @param w  the width.
     * @param h  the height.
     */
    public void paintBorder(Component c, Graphics g, int x, int y, int w, 
        int h)
    {
        
      JInternalFrame f = (JInternalFrame) c;
      if (f.isSelected())
        g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
      else
        g.setColor(MetalLookAndFeel.getControlDarkShadow());
      
      // fill the border background
      g.fillRect(x, y, w, 5);
      g.fillRect(x, y, 5, h);
      g.fillRect(x + w - 5, y, 5, h);
      g.fillRect(x, y + h - 5, w, 5);
      
      // draw a dot in each corner
      g.setColor(MetalLookAndFeel.getControl());
      g.fillRect(x, y, 1, 1);
      g.fillRect(x + w - 1, y, 1, 1);
      g.fillRect(x + w - 1, y + h - 1, 1, 1);
      g.fillRect(x, y + h - 1, 1, 1);
      
      // draw the lines
      g.setColor(MetalLookAndFeel.getBlack());
      g.drawLine(x + 14, y + 2, x + w - 15, y + 2);
      g.drawLine(x + 14, y + h - 3, x + w - 15, y + h - 3);
      g.drawLine(x + 2, y + 14, x + 2, y + h - 15);
      g.drawLine(x + w - 3, y + 14, x + w - 3, y + h - 15);
      
      // draw the line highlights
      if (f.isSelected())
        g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
      else 
        g.setColor(MetalLookAndFeel.getControlShadow());
      g.drawLine(x + 15, y + 3, x + w - 14, y + 3);
      g.drawLine(x + 15, y + h - 2, x + w - 14, y + h - 2);
      g.drawLine(x + 3, y + 15, x + 3, y + h - 14);
      g.drawLine(x + w - 2, y + 15, x + w - 2, y + h - 14);
    }
    
  }

  /**
   * A border used for {@link JInternalFrame} components that are
   * presented as dialogs (by the {@link JOptionPane} class).
   */
  public static class OptionDialogBorder extends AbstractBorder
    implements UIResource
  {
      
    /**
     * Creates a new border instance.
     */
    public OptionDialogBorder()
    {
      // Nothing to do here.
    }
    
    /**
     * Returns the border insets.
     * 
     * @param c  the component (ignored).
     * 
     * @return The border insets.
     */
    public Insets getBorderInsets(Component c)
    {
      return getBorderInsets(c, null);
    }
    
    /**
     * Returns the border insets.
     * 
     * @param c  the component (ignored).
     * @return The border insets.
     */
    public Insets getBorderInsets(Component c, Insets newInsets)
    {
      if (newInsets == null)
        newInsets = new Insets(3, 3, 3, 3);
      else
        {
          newInsets.top = 3;
          newInsets.left = 3;
          newInsets.bottom = 3;
          newInsets.right = 3;
        }
      return newInsets;  
    }
        
    /**
     * Paints the border for the specified component.
     * 
     * @param c  the component.
     * @param g  the graphics device.
     * @param x  the x-coordinate.
     * @param y  the y-coordinate.
     * @param w  the width.
     * @param h  the height.
     */
    public void paintBorder(Component c, Graphics g, int x, int y, int w, 
        int h)
    {
        
      JInternalFrame f = (JInternalFrame) c;
      g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
      if (f.getContentPane() instanceof JOptionPane)
        {
          JOptionPane pane = (JOptionPane) f.getContentPane();
          int type = pane.getMessageType();
          if (type == JOptionPane.QUESTION_MESSAGE)
            {
              Color bc = UIManager.getColor(
                  "OptionPane.questionDialog.border.background");
              if (bc != null)
                g.setColor(bc);
            }
          if (type == JOptionPane.WARNING_MESSAGE)
            {
              Color bc = UIManager.getColor(
                  "OptionPane.warningDialog.border.background");
              if (bc != null)
                g.setColor(bc);              
            }
          else if (type == JOptionPane.ERROR_MESSAGE)
            {
              Color bc = UIManager.getColor(
                  "OptionPane.errorDialog.border.background");
              if (bc != null)
                g.setColor(bc);              
            }
        }
      
      // fill the border background
      g.fillRect(x, y, w, 3);
      g.fillRect(x, y, 3, h);
      g.fillRect(x + w - 3, y, 3, h);
      g.fillRect(x, y + h - 3, w, 3);
      
      // draw a dot in each corner
      g.setColor(MetalLookAndFeel.getControl());
      g.fillRect(x, y, 1, 1);
      g.fillRect(x + w - 1, y, 1, 1);
      g.fillRect(x + w - 1, y + h - 1, 1, 1);
      g.fillRect(x, y + h - 1, 1, 1);
      
    }
    
  }

  /**
   * A border used for {@link JMenu} and {@link JMenuItem} components.
   */
  public static class MenuItemBorder extends AbstractBorder
    implements UIResource
  {
    /** The border insets. */
    protected static Insets borderInsets = new Insets(2, 2, 2, 2);
    
    /**
     * Creates a new border instance.
     */
    public MenuItemBorder()
    {
      // Nothing to do here.
    }
    
    /**
     * Paints the border for the component.  A border is painted only if the
     * component is a selected {@link JMenu} or an armed {@link JMenuItem}.
     * 
     * @param c  the component.
     * @param g  the graphics device.
     * @param x  the x-coordinate of the border area.
     * @param y  the y-coordinate of the border area.
     * @param w  the width of the border area.
     * @param h  the height of the border area.
     */
    public void paintBorder(Component c, Graphics g, int x, int y, int w,
        int h)
    {
      Color dark = MetalLookAndFeel.getPrimaryControlDarkShadow();
      Color light = MetalLookAndFeel.getPrimaryControlHighlight();
      if (c instanceof JMenu) 
        {
          JMenu menu = (JMenu) c;
          if (menu.isSelected())
            {
              g.setColor(dark);
              g.drawLine(x, y, x, y + h);
              g.drawLine(x, y, x + w, y);
              g.drawLine(x + w - 2, y + 1, x + w - 2, y + h);
              g.setColor(light);
              g.drawLine(x + w - 1, y + 1, x + w - 1, y + h);
            }
        }
      else if (c instanceof JMenuItem)
        {
          JMenuItem item = (JMenuItem) c;
          if (item.isArmed()) 
            {
              g.setColor(dark);
              g.drawLine(x, y, x + w, y);
              g.setColor(light);
              g.drawLine(x, y + h - 1, x + w, y + h - 1);
            }
          else
            {
              // Normally we draw a light line on the left.
              g.setColor(light);
              g.drawLine(x, y, x, y + h);
            }
        }
    }
    
    /**
     * Returns the border insets.
     * 
     * @param c  the component (ignored).
     * 
     * @return The border insets.
     */
    public Insets getBorderInsets(Component c)
    {
      return borderInsets;
    }
    
    /**
     * Populates <code>insets</code> with the border insets, then returns it.
     * 
     * @param c  the component (ignored).
     * @param insets  the object to populate with the border insets.
     * 
     * @return The border insets.
     * 
     * @throws NullPointerException if <code>insets</code> is <code>null</code>.
     */
    public Insets getBorderInsets(Component c, Insets insets)
    {
      insets.left = borderInsets.left;
      insets.top = borderInsets.top;
      insets.bottom = borderInsets.bottom;
      insets.right = borderInsets.right;
      return insets;
    }
  }

  /**
   * A border used for {@link JMenuBar} components.
   */
  public static class MenuBarBorder
      extends AbstractBorder
      implements UIResource
  {
    /** The border insets. */
    protected static Insets borderInsets = new Insets(1, 0, 1, 0);
    
    /**
     * Creates a new border instance.
     */
    public MenuBarBorder()
    {
    }
    
    /**
     * Paints the border for the component.  A border is painted only if the
     * component is a selected {@link JMenu} or an armed {@link JMenuItem}.
     * 
     * @param c  the component.
     * @param g  the graphics device.
     * @param x  the x-coordinate of the border area.
     * @param y  the y-coordinate of the border area.
     * @param w  the width of the border area.
     * @param h  the height of the border area.
     */
    public void paintBorder(Component c, Graphics g, int x, int y, int w,
        int h)
    {
      // Although it is not correct to decide on the static property
      // currentTheme which color to use the RI does it like that.
      // The trouble is that by simply changing the current theme to
      // e.g. DefaultMetalLookAndFeel this method will use another color
      // although a change in painting behavior should be expected only
      // after setting a new look and feel and updating all components.
      if(MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme)
        g.setColor(UIManager.getColor("MenuBar.borderColor"));
      else
        g.setColor(MetalLookAndFeel.getControlShadow());
      
      g.drawLine(x, y + h - 1, x + w, y + h - 1);
    }
    
    /**
     * Returns the border insets.
     * 
     * @param c  the component (ignored).
     * 
     * @return The border insets.
     */
    public Insets getBorderInsets(Component c)
    {
      return borderInsets;
    }
    
    /**
     * Populates <code>insets</code> with the border insets, then returns it.
     * 
     * @param c  the component (ignored).
     * @param insets  the object to populate with the border insets.
     * 
     * @return The border insets.
     * 
     * @throws NullPointerException if <code>insets</code> is <code>null</code>.
     */
    public Insets getBorderInsets(Component c, Insets insets)
    {
      insets.left = borderInsets.left;
      insets.top = borderInsets.top;
      insets.bottom = borderInsets.bottom;
      insets.right = borderInsets.right;
      return insets;
    }
  }

  /**
   * A border for {@link JScrollPane} components.
   */
  public static class ScrollPaneBorder
    extends AbstractBorder
    implements UIResource
  {
    /** The border insets. */
    private static Insets insets = new Insets(1, 1, 2, 2);
    
    /**
     * Constructs a new ScrollPaneBorder.
     */
    public ScrollPaneBorder()
    {
      // Nothing to do here.
    }
    
    /**
     * Returns the insets of the border for the Component <code>c</code>.
     *
     * @param c the Component for which we return the border insets
     */
    public Insets getBorderInsets(Component c)
    {
      return insets;
    }

    /**
     * Paints the border.
     *
     * @param c the Component for which the border is painted
     * @param g the Graphics context
     * @param x the X coordinate of the upper left corner of the border
     * @param y the Y coordinate of the upper left corner of the border
     * @param w the width of the border
     * @param h the height of the border
     */
    public void paintBorder(Component c, Graphics g, int x, int y,
                            int w, int h)
    {
      Color darkShadow = MetalLookAndFeel.getControlDarkShadow();
      Color shadow = MetalLookAndFeel.getControlShadow();
      Color light = MetalLookAndFeel.getWhite();
      Color middle = MetalLookAndFeel.getControl();

      // paint top border line
      g.setColor(darkShadow);
      g.drawLine(x, y, x + w - 2, y);

      // paint left border line
      g.drawLine(x, y, x, y + h - 2);
 
      // paint right inner border line
      g.drawLine(x + w - 2, y, x + w - 2, y + h + 1);

      // paint bottom inner border line
      g.drawLine(x + 2, y + h - 2, x + w - 2, y + h - 2);

      // draw right outer border line
      g.setColor(light);
      g.drawLine(x + w - 1, y, x + w - 1, y + h - 1);

      // draw bottom outer border line
      g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);

      // paint the lighter points
      g.setColor(middle);
      g.drawLine(x + w - 1, y, x + w - 1, y);
      g.drawLine(x + w - 2, y + 2, x + w - 2, y + 2);
      g.drawLine(x, y + h - 1, x, y + h - 1);
      g.drawLine(x + 1, y + h - 2, x + 1, y + h - 2);

    }
    
  }
  
  /**
   * A button border that is only visible when the mouse pointer is within 
   * the button's bounds.
   */
  public static class RolloverButtonBorder
    extends MetalBorders.ButtonBorder
  {
    /**
     * Creates a new border instance.
     */
    public RolloverButtonBorder()
    {
      // Nothing to do here.
    }
    
    /**
     * Paints the border.
     * 
     * @param c  the component.
     * @param g  the graphics device.
     * @param x  the x-coordinate.
     * @param y  the y-coordinate.
     * @param w  the width.
     * @param h  the height.
     */
    public void paintBorder(Component c, Graphics g, int x, int y, int w, 
            int h)
    {
      // TODO: What should be done here? Obviously the ButtonBorder already
      // handles the rollover state in Sun's impl. Maybe this is only there
      // for backwards compatibility.
      super.paintBorder(c, g, x, y, w, h);
    }
  }
  
  /**
   * This border is used in Toolbar buttons as inner border.
   */
  static class RolloverMarginBorder extends AbstractBorder
  {
    /** The borders insets. */
    protected static Insets borderInsets = new Insets(3, 3, 3, 3);

    /**
     * Creates a new instance of RolloverBorder.
     */
    public RolloverMarginBorder()
    {
      // Nothing to do here.
    }
    
    /**
     * Returns the insets of the RolloverBorder.
     *
     * @param c the component for which the border is used
     *
     * @return the insets of the RolloverBorder
     */
    public Insets getBorderInsets(Component c)
    {
      return getBorderInsets(c, null);
    }

    /**
     * Returns the insets of the RolloverMarginBorder in the specified
     * Insets object.
     *
     * @param c the component for which the border is used
     * @param newInsets the insets object where to put the values
     *
     * @return the insets of the RolloverMarginBorder
     */
    public Insets getBorderInsets(Component c, Insets newInsets)
    {
      if (newInsets == null)
        newInsets = new Insets(0, 0, 0, 0);

      AbstractButton b = (AbstractButton) c;
      Insets margin = b.getMargin();
      newInsets.bottom = borderInsets.bottom;
      newInsets.left = borderInsets.left;
      newInsets.right = borderInsets.right;
      newInsets.top = borderInsets.top;
      return newInsets;
    }
  }

  /**
   * A border implementation for popup menus.
   */
  public static class PopupMenuBorder
    extends AbstractBorder
    implements UIResource
  {

    /** The border's insets. */
    protected static Insets borderInsets = new Insets(3, 1, 2, 1);

    /**
     * Constructs a new PopupMenuBorder.
     */
    public PopupMenuBorder()
    {
      // Nothing to do here.
    }
    
    /**
     * Returns the insets of the border, creating a new Insets instance
     * with each call.
     *
     * @param c the component for which we return the border insets
     *          (not used here)
     */
    public Insets getBorderInsets(Component c)
    {
      return getBorderInsets(c, null);
    }
    
    /**
     * Returns the insets of the border, using the supplied Insets instance.
     *
     * @param c the component for which we return the border insets
     *          (not used here)
     * @param i the Insets instance to fill with the Insets values
     */
    public Insets getBorderInsets(Component c, Insets i)
    {
      Insets insets;
      if (i == null)
        insets = new Insets(borderInsets.top, borderInsets.left,
                            borderInsets.bottom, borderInsets.right);
      else
        {
          insets = i;
          insets.top = borderInsets.top;
          insets.left = borderInsets.left;
          insets.bottom = borderInsets.bottom;
          insets.right = borderInsets.right;
        }
      
      return insets;
    }

    /**
     * Paints the border for component <code>c</code> using the
     * Graphics context <code>g</code> with the dimension
     * <code>x, y, w, h</code>.
     *
     * @param c the component for which we paint the border
     * @param g the Graphics context to use
     * @param x the X coordinate of the upper left corner of c
     * @param y the Y coordinate of the upper left corner of c
     * @param w the width of c
     * @param h the height of c
     */
    public void paintBorder(Component c, Graphics g, int x, int y, int w,
                            int h)
    {
      Color darkShadow = MetalLookAndFeel.getPrimaryControlDarkShadow();
      Color light = MetalLookAndFeel.getPrimaryControlHighlight();

      // draw dark outer border
      g.setColor(darkShadow);
      g.drawRect(x, y, w - 1, h - 1);
      
      // draw highlighted inner border (only top and left)
      g.setColor(light);
      g.drawLine(x + 1, y + 1, x + w - 2, y + 1);
    }
    
  }

  /**
   * A border used for the {@link JToggleButton} component.
   * 
   * @since 1.3
   */
  public static class ToggleButtonBorder
    extends ButtonBorder 
  {
    /**
     * Creates a new border instance.
     */
    public ToggleButtonBorder()
    {
      // Nothing to do here.
    }
    
    /**
     * Paints the toggle button border.
     *
     * @param c the component for which we paint the border
     * @param g the Graphics context to use
     * @param x the X coordinate of the upper left corner of c
     * @param y the Y coordinate of the upper left corner of c
     * @param w the width of c
     * @param h the height of c
     */
    public void paintBorder(Component c, Graphics g, int x, int y, int w,
                            int h)
    {
      ButtonModel bmodel = null;
      
      if (c instanceof AbstractButton)
        bmodel = ((AbstractButton) c).getModel();

      Color darkShadow = MetalLookAndFeel.getControlDarkShadow();
      Color shadow = MetalLookAndFeel.getControlShadow();
      Color light = MetalLookAndFeel.getWhite();
      Color middle = MetalLookAndFeel.getControl();

      if (c.isEnabled())
        {
          // draw dark border
          g.setColor(darkShadow);
          g.drawRect(x, y, w - 2, h - 2);

          if (!bmodel.isArmed())
            {
              // draw light border
              g.setColor(light);
              g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1);
              g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1);
              if (bmodel.isSelected())
                g.setColor(middle);
              g.drawLine(x + 1, y + 1, x + w - 3, y + 1);
              g.drawLine(x + 1, y + 1, x + 1, y + h - 3);

              // draw crossing pixels of both borders
              g.setColor(shadow);
              g.drawLine(x + 1, y + h - 2, x + 1, y + h - 2);
              g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1);
            }
          else
            {
              // draw light border
              g.setColor(light);
              g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1);
              g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1);

              // draw shadow border
              g.setColor(shadow);
              g.drawLine(x + 1, y + 1, x + w - 2, y + 1);
              g.drawLine(x + 1, y + 1, x + 1, y + h - 2);
 
              // draw crossing pixels of both borders
              g.setColor(shadow);
              g.drawLine(x + 1, y + h - 2, x + 1, y + h - 2);
              g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1);
              
            }
          // draw corners
          g.setColor(middle);
          g.drawLine(x, y + h - 1, x, y + h - 1);
          g.drawLine(x + w - 1, y, x + w - 1, y);
        }
      else 
        {
          // draw disabled border
          g.setColor(MetalLookAndFeel.getControlDisabled());
          g.drawRect(x, y, w - 2, h - 2);          
        }
    }
  }

  /**
   * A border used for the {@link JToolBar} component.
   */
  public static class ToolBarBorder extends AbstractBorder
    implements UIResource, SwingConstants
  {
    /**
     * Creates a new border instance.
     */
    public ToolBarBorder()
    {
      // Nothing to do here.
    }
    
    /**
     * Returns the border insets.
     * 
     * @param c  the component (ignored).
     * 
     * @return The border insets.
     */
    public Insets getBorderInsets(Component c)
    {
      return getBorderInsets(c, null);
    }
    
    /**
     * Returns the border insets.
     * 
     * @param c  the component (ignored).
     * @return The border insets.
     */
    public Insets getBorderInsets(Component c, Insets newInsets)
    {
      JToolBar tb = (JToolBar) c;
      if (tb.getOrientation() == JToolBar.HORIZONTAL)
        {   
          if (newInsets == null)
            newInsets = new Insets(2, 16, 2, 2);
          else
            {
              newInsets.top = 2;
              newInsets.left = 16;
              newInsets.bottom = 2;
              newInsets.right = 2;
            }
          return newInsets;  
        }
      else // assume JToolBar.VERTICAL
        { 
          if (newInsets == null)
            newInsets = new Insets(16, 2, 2, 2);
          else
            {
              newInsets.top = 16;
              newInsets.left = 2;
              newInsets.bottom = 2;
              newInsets.right = 2;
            }
          return newInsets;  
        }

    }
    
    /**
     * Paints the border for the specified component.
     * 
     * @param c  the component.
     * @param g  the graphics device.
     * @param x  the x-coordinate.
     * @param y  the y-coordinate.
     * @param w  the width.
     * @param h  the height.
     */
    public void paintBorder(Component c, Graphics g, int x, int y, int w, 
        int h)
    {
        
      JToolBar tb = (JToolBar) c;
      if (tb.getOrientation() == JToolBar.HORIZONTAL)
        {
           MetalUtils.fillMetalPattern(tb, g, x + 2, y + 2, x + 11, y + h - 5, 
                  MetalLookAndFeel.getControlHighlight(), 
                  MetalLookAndFeel.getControlDarkShadow());
        }
      else
        { 
          MetalUtils.fillMetalPattern(tb, g, x + 2, y + 2, x + w - 5, y + 11, 
                  MetalLookAndFeel.getControlHighlight(), 
                  MetalLookAndFeel.getControlDarkShadow());
        }
    }
    
  }
  
  /**
   * A border for table header cells.
   *
   * @since 1.3
   */
  public static class TableHeaderBorder extends AbstractBorder
  {
    /**
     * The insets of this border.
     */
    // TODO: According to tests that I have done, this is really the border
    // that should be returned by getBorderInsets(). However, the name
    // is very distracting. Is there any deeper meaning in it?
    protected Insets editorBorderInsets;

    /**
     * Creates a new instance of <code>TableHeaderBorder</code>.
     */
    public TableHeaderBorder()
    {
      editorBorderInsets = new Insets(1, 1, 1, 1);
    }

    /**
     * Return the insets of this border.
     *
     * @return the insets of this border
     */
    public Insets getBorderInsets(Component c)
    {
      return editorBorderInsets;
    }

    /**
     * Paints the border.
     *
     * @param c the component for which to paint the border
     * @param g the graphics context to use
     * @param x the x cooridinate of the border rectangle
     * @param y the y cooridinate of the border rectangle
     * @param w the width of the border rectangle
     * @param h the height of the border rectangle
     */
    public void paintBorder(Component c, Graphics g, int x, int y, int w, int h)
    {
      Color dark = MetalLookAndFeel.getControlDarkShadow();
      Color light = MetalLookAndFeel.getWhite();
      Color old = g.getColor();
      g.setColor(light);
      g.drawLine(x, y, x + w - 2, y);
      g.drawLine(x, y, x, y + h - 2);
      g.setColor(dark);
      g.drawLine(x + w - 1, y, x + w - 1, y + h - 1);
      g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1);
      g.setColor(old);
    }
  }

  /**
   * Returns a border for Swing buttons in the Metal Look &amp; Feel.
   *
   * @return a border for Swing buttons in the Metal Look &amp; Feel
   */
  public static Border getButtonBorder()
  {
    if (buttonBorder == null)
      {
        Border outer = new ButtonBorder();
        Border inner = getMarginBorder();
        buttonBorder = new BorderUIResource.CompoundBorderUIResource(outer, 
            inner);
      }
    return buttonBorder;
  }
  
  /**
   * Returns a border for use with {@link JToggleButton} components.
   *
   * @return A border.
   * 
   * @since 1.3
   */
  public static Border getToggleButtonBorder()
  {
    if (toggleButtonBorder == null)
      {
        Border outer = new ToggleButtonBorder();
        Border inner = getMarginBorder();
        toggleButtonBorder = new BorderUIResource.CompoundBorderUIResource(
            outer, inner);
      }
    return toggleButtonBorder;
  }

  /**
   * Returns a border instance that is used with a {@link JInternalFrame} when
   * it is in the iconified state.
   * 
   * @return A border.
   * 
   * @since 1.3
   */
  public static Border getDesktopIconBorder()
  {
    if (desktopIconBorder == null)
      desktopIconBorder = new DesktopIconBorder();
    return desktopIconBorder;      
  }

  /**
   * Returns a border for use by the {@link JTextField} component.
   * 
   * @return A border.
   * 
   * @since 1.3
   */
  public static Border getTextFieldBorder()
  {
    if (textFieldBorder == null)
      {
        Border inner = getMarginBorder();
        Border outer = new TextFieldBorder();
        textFieldBorder =
          new BorderUIResource.CompoundBorderUIResource(outer, inner);
      }
    return textFieldBorder;
  }

  /**
   * Returns the border that is used for text components (except text fields,
   * which use {@link #getTextFieldBorder}.
   *
   * @return the border that is used for text components
   *
   * @since 1.3
   */
  public static Border getTextBorder()
  {
    if (textBorder == null)
      {
        Border inner = getMarginBorder();
        Border outer = new Flush3DBorder();
        textBorder =
          new BorderUIResource.CompoundBorderUIResource(outer, inner);
      }
    return textBorder;
  }

  /**
   * Returns a border for Toolbar buttons in the Metal Look &amp; Feel.
   *
   * @return a border for Toolbar buttons in the Metal Look &amp; Feel
   */
  static Border getToolbarButtonBorder()
  {
    if (toolbarButtonBorder == null)
      {
        Border outer = new ButtonBorder();
        Border inner = new RolloverMarginBorder();
        toolbarButtonBorder = new CompoundBorder(outer, inner);
      }
    return toolbarButtonBorder;
  }

  /**
   * Returns a shared instance of {@link BasicBorders.MarginBorder}.
   *
   * @return a shared instance of {@link BasicBorders.MarginBorder}
   */
  static Border getMarginBorder()
  {
    if (marginBorder == null)
      marginBorder = new BasicBorders.MarginBorder();
    return marginBorder;
  }

  /**
   * Returns a shared instance of a compound border for rollover buttons.
   * 
   * @return A shared border instance.
   */
  static Border getRolloverBorder()
  {
    if (rolloverBorder == null)
      {
        Border outer = new MetalBorders.RolloverButtonBorder();
        Border inner = MetalBorders.getMarginBorder();
        rolloverBorder = new BorderUIResource.CompoundBorderUIResource(outer, 
            inner);
      }
    return rolloverBorder;
  }

}
