/*
 * @(#)Borders.java    2.5.0   5 September 2006
 *
 * Copyright 2006
 * College of Computer and Information Science
 * Northeastern University
 * Boston, MA  02115
 *
 * The Java Power Tools software may be used for educational
 * purposes as long as this copyright notice is retained intact
 * at the top of all source files.
 *
 * To discuss possible commercial use of this software, 
 * contact Richard Rasala at Northeastern University, 
 * College of Computer and Information Science,
 * 617-373-2462 or rasala@ccs.neu.edu.
 *
 * The Java Power Tools software has been designed and built
 * in collaboration with Viera Proulx and Jeff Raab.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either
 * in whole or in part without explicit permission.
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

package edu.neu.ccs.gui;

import java.awt.*;

import javax.swing.*;
import javax.swing.border.*;


/**
 * <p>Class <code>Borders</code> redesigns the Java class
 * <code>BorderFactory</code> by providing brief names for
 * many methods in that class and by providing additional
 * convenience methods that do not exist in that class.</p>
 * 
 * <p>In particular, this class provides new methods to
 * create <i>etch</i> and <i>bevel</i> style borders
 * with a <code>levels</code> parameter that controls
 * the border thickness.</p>
 * 
 * <p>Technical Note: Many of the complex borders are made
 * using compounds of <code>MatteBorder</code> objects and
 * so the return types will not match the classic Java
 * border classes.</p>
 * 
 * <p>This class cannot be instantiated.</p>
 * 
 * @author Richard Rasala
 * @version 2.5.0
 * @since   2.5.0
 */
public class Borders {
    
    /** Private constructor to prevent instantiation. */
    private Borders() {}
    
    
    /** The int <code>TitledBorder.LEFT</code>. */
    public static final int LEFT =
        TitledBorder.LEFT;
    
    /** The int <code>TitledBorder.RIGHT</code>. */
    public static final int RIGHT =
        TitledBorder.RIGHT;
    
    /** The int <code>TitledBorder.CENTER</code>. */
    public static final int CENTER =
        TitledBorder.CENTER;
    
    /** The int <code>TitledBorder.LEADING</code>. */
    public static final int LEADING =
        TitledBorder.LEADING;
    
    /** The int <code>TitledBorder.TRAILING</code>. */
    public static final int TRAILING =
        TitledBorder.TRAILING;
    
    /**
     * The horizontal default for a titled border that
     * we take to be (<code>CENTER</code>) rather than
     * the Java default (<code>LEADING</code>). */
    public static final int H_DEFAULT = CENTER;
   
    /** The int <code>TitledBorder.TOP</code>. */
    public static final int TOP =
        TitledBorder.TOP;
    
    /** The int <code>TitledBorder.ABOVE_TOP</code>. */
    public static final int ABOVE_TOP =
        TitledBorder.ABOVE_TOP;
    
    /** The int <code>TitledBorder.BELOW_TOP</code>. */
    public static final int BELOW_TOP =
        TitledBorder.BELOW_TOP;
    
    /** The int <code>TitledBorder.BOTTOM</code>. */
    public static final int BOTTOM =
        TitledBorder.BOTTOM;
    
    /** The int <code>TitledBorder.ABOVE_BOTTOM</code>. */
    public static final int ABOVE_BOTTOM =
        TitledBorder.ABOVE_BOTTOM;
    
    /** The int <code>TitledBorder.BELOW_BOTTOM</code>. */
    public static final int BELOW_BOTTOM =
        TitledBorder.BELOW_BOTTOM;
    
    /**
     * The vertical default for a titled border that is
     * (<code><i>TOP</i></code>) as in Java. */
    public static final int V_DEFAULT = TOP;
    
    
    /**
     * The default line border for a titled border,
     * namely, <code>line(Color.black,2)</code>.
     */
    public static final Border DEFAULT_LINE_BORDER =
        line(Color.black, 2);
    
    
    /**
     * <p>Creates an empty border
     * that takes up no space.</p>
     */
    public static Border empty()
    {
        return empty(0, 0, 0, 0);
    }
    
    
    /**
     * <p>Creates an empty border
     * with the given thickness.</p>
     * 
     * @param thickness the border thickness
     */
    public static Border empty(int thickness)
    {
        return empty(thickness, thickness, thickness, thickness);
    }
    
    
    /**
     * <p>Creates an empty border
     * with the given gaps.</p>
     * 
     * @param vgap the border vertical   gap: top and bottom
     * @param hgap the border horizontal gap: left and right
     */
    public static Border empty(int vgap, int hgap)
    {
        return empty(vgap, hgap, vgap, hgap);
    }
    
    
    /**
     * <p>Creates an empty border
     * with the given parameters.</p>
     * 
     * @param top    the border top
     * @param left   the border left
     * @param bottom the border bottom
     * @param right  the border right
     */
    public static Border empty
        (int top, int left, int bottom, int right)
    {
        return BorderFactory.createEmptyBorder
            (top, left, bottom, right);
    }
    
    
    /**
     * <p>Creates a matte border
     * with the given thickness and color.</p>
     * 
     * <p>If the color is <code>null</code>, it is set to
     * the color <code>Color.black</code>.</p>
     * 
     * @param thickness the border thickness
     * @param color     the border color
     */
    public static Border matte
        (int thickness, Color color)
    {
        return matte(thickness, thickness, thickness, thickness, color);
    }
    
    
    /**
     * <p>Creates a matte border
     * with the given gaps and color.</p>
     * 
     * <p>If the color is <code>null</code>, it is set to
     * the color <code>Color.black</code>.</p>
     * 
     * @param vgap  the border vertical   gap: top and bottom
     * @param hgap  the border horizontal gap: left and right
     * @param color the border color
     */
    public static Border matte(int vgap, int hgap, Color color)
    {
        return matte(vgap, hgap, vgap, hgap, color);
    }
    
    
    /**
     * <p>Creates a matte border
     * with the given parameters.</p>
     * 
     * <p>If the color is <code>null</code>, it is set to
     * the color <code>Color.black</code>.</p>
     * 
     * @param top    the border top
     * @param left   the border left
     * @param bottom the border bottom
     * @param right  the border right
     * @param color  the border color
     */
    public static Border matte
        (int top, int left, int bottom, int right, Color color)
    {
        if (color == null)
            color = Color.black;
        
        return BorderFactory.createMatteBorder
            (top, left, bottom, right, color);
    }
    
    
    /**
     * <p>Creates a matte border
     * with the given thickness and tileicon.</p>
     * 
     * <p>If the tileicon is <code>null</code>, it is replaced by
     * the color <code>Color.black</code>.</p>
     * 
     * @param thickness the border thickness
     * @param tileicon  the border tileicon
     */
    public static Border matte
        (int thickness, Icon tileicon)
    {
        return matte(thickness, thickness, thickness, thickness, tileicon);
    }
    
    
    /**
     * <p>Creates a matte border
     * with the given gaps and tileicon.</p>
     * 
     * <p>If the tileicon is <code>null</code>, it is replaced by
     * the color <code>Color.black</code>.</p>
     * 
     * @param vgap     the border vertical   gap: top and bottom
     * @param hgap     the border horizontal gap: left and right
     * @param tileicon the border tileicon
     */
    public static Border matte(int vgap, int hgap, Icon tileicon)
    {
        return matte(vgap, hgap, vgap, hgap, tileicon);
    }
    
    
    /**
     * <p>Creates a matte border
     * with the given parameters.</p>
     * 
     * <p>If the tileicon is <code>null</code>, it is replaced by
     * the color <code>Color.black</code>.</p>
     * 
     * @param top      the border top
     * @param left     the border left
     * @param bottom   the border bottom
     * @param right    the border right
     * @param tileicon the border tileicon
     */
    public static Border matte
        (int top, int left, int bottom, int right, Icon tileicon)
    {
        if (tileicon != null)
            return BorderFactory.createMatteBorder
                (top, left, bottom, right, tileicon);
        else
            return BorderFactory.createMatteBorder
                (top, left, bottom, right, Color.black);
    }
    
    
    /**
     * <p>Creates a line border
     * with color black
     * and thickness 1.</p>
     */
    public static Border line()
    {
        return line(Color.black, 1);
    }
    
    
    /**
     * <p>Creates a line border
     * with given color
     * and thickness 1.</p>
     * 
     * <p>If the color is <code>null</code>, it is set to
     * the color <code>Color.black</code>.</p>
     * 
     * @param color the border color
     */
    public static Border line(Color color)
    {
        return line(color, 1);
    }
    
    
    /**
     * <p>Creates a line border
     * with color black
     * and the given thickness.</p>
     * 
     * @param thickness the border thickness
     */
    public static Border line(int thickness)
    {
        return line(Color.black, thickness);
    }
    
    
    /**
     * <p>Creates a line border
     * with the given color and thickness.</p>
     * 
     * <p>If the color is <code>null</code>, it is set to
     * the color <code>Color.black</code>.</p>
     * 
     * <p>Note that the effect of this method is the same
     * as:</p>
     * 
     * <pre>   matte(thickness, color)</pre>
     * 
     * <p>but the parameters are in the opposite order.</p>
     * 
     * @param color     the border color
     * @param thickness the border thickness
     */
    public static Border line(Color color, int thickness)
    {
        if (color == null)
            color = Color.black;
        
        return BorderFactory.createLineBorder(color, thickness);
    }
    
    
    /**
     * <p>Creates a default lowered etch border.</p>
     */
    public static Border etchLowered()
    {
        return BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);
    }
    
    
    /**
     * <p>Creates a lowered etch border
     * with the given highlight and shadow colors.</p>
     * 
     * <p>If either color is <code>null</code>,
     * returns a default lowered etch border.</p>
     * 
     * @param highlight the highlight color
     * @param shadow    the shadow color
     */
    public static Border etchLowered
        (Color highlight, Color shadow)
    {
        return etchLowered(1, highlight, shadow);
    }
    
    
    /**
     * <p>Creates a lowered etch border in which the thickness
     * of each color band is equal to the number of levels.</p>
     * 
     * <p>Since each etch edge has 2 color bands, the thickness
     * of each edge is 2*levels.</p>
     * 
     * <p>The north and west edges are set up as follows:</p>
     * 
     * <ul>
     *   <li>outer color: shadow</li>
     *   <li>inner color: highlight</li>
     * </ul>
     * 
     * <p>The south and east edges are set up as follows:</p>
     * 
     * <ul>
     *   <li>outer color: highlight</li>
     *   <li>inner color: shadow</li>
     * </ul>
     * 
     * <p>Note that these color settings are the opposite of
     * those for a raised border.</p>
     * 
     * <p>It is an error if levels is not at least 1 or if either
     * color is <code>null</code>.  In that case, a default
     * lowered etch border is returned.</p>
     * 
     * @param levels    the number of levels in each color band
     * @param highlight the highlight color
     * @param shadow    the shadow color
     */
    public static Border etchLowered
        (int levels, Color highlight, Color shadow)
    {
        boolean error =
            (levels <= 0)
            || (highlight == null)
            || (shadow == null);
        
        if (error)
            return etchLowered();
        
        Border border = null;
        
        Border highNW =
            BorderFactory.createMatteBorder(1, 1, 0, 0, highlight);
        
        Border shadNW =
            BorderFactory.createMatteBorder(1, 1, 0, 0, shadow);
        
        Border highSE =
            BorderFactory.createMatteBorder(0, 0, 1, 1, highlight);
        
        Border shadSE =
            BorderFactory.createMatteBorder(0, 0, 1, 1, shadow);
        
        for (int i = 0; i < levels; i++) {
            border = BorderFactory.createCompoundBorder(highNW, border);
            
            border = BorderFactory.createCompoundBorder(shadSE, border);
        }
        
        for (int i = 0; i < levels; i++) {
            border = BorderFactory.createCompoundBorder(shadNW, border);
            
            border = BorderFactory.createCompoundBorder(highSE, border);
        }
        
        return border;
    }
    
    
    /**
     * <p>Creates a default raised etch border.</p>
     */
    public static Border etchRaised()
    {
        return BorderFactory.createEtchedBorder(EtchedBorder.RAISED);
    }
    
    
    /**
     * <p>Creates a raised etch border
     * with the given highlight and shadow colors.</p>
     * 
     * <p>If either color is <code>null</code>,
     * returns a default raised etch border.</p>
     * 
     * @param highlight the highlight color
     * @param shadow    the shadow color
     */
    public static Border etchRaised
        (Color highlight, Color shadow)
    {
        return etchRaised(1, highlight, shadow);
    }
    
    
    /**
     * <p>Creates a raised etch border in which the thickness
     * of each color band is equal to the number of levels.</p>
     * 
     * <p>Since each etch edge has 2 color bands, the thickness
     * of each edge is 2*levels.</p>
     * 
     * <p>The north and west edges are set up as follows:</p>
     * 
     * <ul>
     *   <li>outer color: highlight</li>
     *   <li>inner color: shadow</li>
     * </ul>
     * 
     * <p>The south and east edges are set up as follows:</p>
     * 
     * <ul>
     *   <li>outer color: shadow</li>
     *   <li>inner color: highlight</li>
     * </ul>
     * 
     * <p>Note that these color settings are the opposite of
     * those for a lowered border.</p>
     * 
     * <p>It is an error if levels is not at least 1 or if either
     * color is <code>null</code>.  In that case, a default
     * raised etch border is returned.</p>
     * 
     * @param levels    the number of levels in each color band
     * @param highlight the highlight color
     * @param shadow    the shadow color
     */
    public static Border etchRaised
        (int levels, Color highlight, Color shadow)
    {
        boolean error =
            (levels <= 0)
            || (highlight == null)
            || (shadow == null);
        
        if (error)
            return etchRaised();
        
        return etchLowered(levels, shadow, highlight);
    }
    
    
    /**
     * <p>Creates a default lowered bevel border.</p>
     */
    public static Border bevelLowered()
    {
        return BorderFactory.createBevelBorder(BevelBorder.LOWERED);
    }
    
    
    /**
     * <p>Creates a lowered bevel border
     * with the given highlight and shadow colors.</p>
     * 
     * <p>If either color is <code>null</code>,
     * returns a default lowered bevel border.</p>
     * 
     * @param highlight the highlight color
     * @param shadow    the shadow color
     */
    public static Border bevelLowered
        (Color highlight, Color shadow)
    {
        return bevelLowered(2, highlight, shadow);
    }
    
    
    /**
     * <p>Creates a lowered bevel border
     * with the given outer and inner highlight colors
     * and the given outer and inner shadow colors.</p>
     * 
     * <p>If any color is <code>null</code>,
     * returns a default lowered bevel border.</p>
     * 
     * <p>The parameter order outer-inner-inner-outer
     * is different from that used in the Java class
     * <code>BorderFactory</code> for similar methods.</p>
     * 
     * @param highlightOuter the outer highlight color
     * @param highlightInner the inner highlight color
     * @param shadowInner    the inner shadow color
     * @param shadowOuter    the outer shadow color
     */
    public static Border bevelLowered
        (Color highlightOuter, Color highlightInner,
         Color shadowInner,    Color shadowOuter)
    {
        return bevelLowered
            (2, highlightOuter, highlightInner,
                shadowInner,    shadowOuter);
    }
    
    
    /**
     * <p>Creates a lowered bevel border in which the thickness
     * of each edge is equal to the number of levels.</p>
     * 
     * <p>The north and west edges are set up as follows:</p>
     * 
     * <ul>
     *   <li>color: shadow</li>
     * </ul>
     * 
     * <p>The south and east edges are set up as follows:</p>
     * 
     * <ul>
     *   <li>color: highlight</li>
     * </ul>
     * 
     * <p>Note that these color settings are the opposite of
     * those for a raised border.</p>
     * 
     * <p>It is an error if levels is not at least 1 or if either
     * color is <code>null</code>.  In that case, a default
     * bevel border is returned.</p>
     * 
     * @param levels    the thickness of each edge
     * @param highlight the highlight color
     * @param shadow    the shadow color
     */
    public static Border bevelLowered
        (int levels, Color highlight, Color shadow)
    {
        boolean error =
            (levels <= 0)
            || (highlight == null)
            || (shadow == null);
        
        if (error)
            return bevelLowered();
        
        Border border = null;
        
        Border shadNW =
            BorderFactory.createMatteBorder(1, 1, 0, 0, shadow);
        
        Border highSE =
            BorderFactory.createMatteBorder(0, 0, 1, 1, highlight);
        
        for (int i = 0; i < levels; i++) {
            border = BorderFactory.createCompoundBorder(shadNW, border);
            
            border = BorderFactory.createCompoundBorder(highSE, border);
        }
        
        return border;
    }
    
    
    /**
     * <p>Creates a lowered bevel border in which the thickness
     * of each edge is equal to the number of levels.</p>
     * 
     * <p>The north and west edges are set up as follows:</p>
     * 
     * <p>The color smoothly interpolates
     * from shadowOuter on the outside
     * to shadowInner on the inside.</p>
     * 
     * <p>The south and east edges are set up as follows:</p>
     * 
     * <p>The color smoothly interpolates
     * from highlightOuter on the outside
     * to highlightInner on the inside.</p>
     * 
     * <p>Note that these color settings are the opposite of
     * those for a raised border.</p>
     * 
     * <p>It is an error if levels is not at least 2 or if any
     * color is <code>null</code>.  In that case, a default
     * bevel border is returned.</p>
     * 
     * <p>The parameter order outer-inner-inner-outer
     * is different from that used in the Java class
     * <code>BorderFactory</code> for similar methods.</p>
     * 
     * @param levels         the thickness of each edge
     * @param highlightOuter the outer highlight color
     * @param highlightInner the inner highlight color
     * @param shadowInner    the inner shadow color
     * @param shadowOuter    the outer shadow color
     */
    public static Border bevelLowered
        (int levels,
         Color highlightOuter, Color highlightInner,
         Color shadowInner,    Color shadowOuter)
    {
        boolean error =
            (levels <= 1)
            || (highlightOuter == null)
            || (highlightInner == null)
            || (shadowInner == null)
            || (shadowOuter == null);
        
        if (error)
            return bevelLowered();
        
        Border border = null;
        
        Color[] high = new Color[levels];
        Color[] shad = new Color[levels];
        
        int N = levels - 1;
        
        high[0] = highlightInner;
        high[N] = highlightOuter;
        
        shad[0] = shadowInner;
        shad[N] = shadowOuter;
        
        if (N >= 2) {
            int hr0 = high[0].getRed();
            int hg0 = high[0].getGreen();
            int hb0 = high[0].getBlue();
            int ha0 = high[0].getAlpha();
            
            int hrN = high[N].getRed();
            int hgN = high[N].getGreen();
            int hbN = high[N].getBlue();
            int haN = high[N].getAlpha();
            
            int sr0 = shad[0].getRed();
            int sg0 = shad[0].getGreen();
            int sb0 = shad[0].getBlue();
            int sa0 = shad[0].getAlpha();
            
            int srN = shad[N].getRed();
            int sgN = shad[N].getGreen();
            int sbN = shad[N].getBlue();
            int saN = shad[N].getAlpha();
            
            int r, g, b, a;
            
            for (int i = 1; i < N; i++) {
                r = (hr0 * (N - i) + hrN * i) / N;
                g = (hg0 * (N - i) + hgN * i) / N;
                b = (hb0 * (N - i) + hbN * i) / N;
                a = (ha0 * (N - i) + haN * i) / N;
                
                high[i] = new Color(r, g, b, a);
                
                r = (sr0 * (N - i) + srN * i) / N;
                g = (sg0 * (N - i) + sgN * i) / N;
                b = (sb0 * (N - i) + sbN * i) / N;
                a = (sa0 * (N - i) + saN * i) / N;
                
                shad[i] = new Color(r, g, b, a);
            }
        }
        
        for (int i = 0; i <= N; i++) {
            Border shadNW =
                BorderFactory.createMatteBorder(1, 1, 0, 0, shad[i]);
            
            Border highSE =
                BorderFactory.createMatteBorder(0, 0, 1, 1, high[i]);
            
            border = BorderFactory.createCompoundBorder(shadNW, border);
            
            border = BorderFactory.createCompoundBorder(highSE, border);
        }
        
        return border;
    }
    
    
    /**
     * <p>Creates a default raised bevel border.</p>
     */
    public static Border bevelRaised()
    {
        return BorderFactory.createBevelBorder(BevelBorder.RAISED);
    }
    
    
    /**
     * <p>Creates a raised bevel border
     * with the given highlight and shadow colors.</p>
     * 
     * <p>If either color is <code>null</code>,
     * returns a default raised bevel border.</p>
     * 
     * @param highlight the highlight color
     * @param shadow    the shadow color
     */
    public static Border bevelRaised
        (Color highlight, Color shadow)
    {
        return bevelRaised(2, highlight, shadow);
    }
    
    
    /**
     * <p>Creates a lowered bevel border
     * with the given outer and inner highlight colors
     * and the given outer and inner shadow colors.</p>
     * 
     * <p>If any color is <code>null</code>,
     * returns a default lowered bevel border.</p>
     * 
     * <p>The parameter order outer-inner-inner-outer
     * is different from that used in the Java class
     * <code>BorderFactory</code> for similar methods.</p>
     * 
     * @param highlightOuter the outer highlight color
     * @param highlightInner the inner highlight color
     * @param shadowInner    the inner shadow color
     * @param shadowOuter    the outer shadow color
     */
    public static Border bevelRaised
        (Color highlightOuter, Color highlightInner,
         Color shadowInner,    Color shadowOuter)
    {
        return bevelRaised
            (2, highlightOuter, highlightInner,
                shadowInner,    shadowOuter);
    }
    
    
    /**
     * <p>Creates a raised bevel border in which the thickness
     * of each edge is equal to the number of levels.</p>
     * 
     * <p>The north and west edges are set up as follows:</p>
     * 
     * <ul>
     *   <li>color: highlight</li>
     * </ul>
     * 
     * <p>The south and east edges are set up as follows:</p>
     * 
     * <ul>
     *   <li>color: shadow</li>
     * </ul>
     * 
     * <p>Note that these color settings are the opposite of
     * those for a lowered border.</p>
     * 
     * <p>It is an error if levels is not at least 1 or if either
     * color is <code>null</code>.  In that case, a default
     * bevel border is returned.</p>
     * 
     * @param levels    the thickness of each edge
     * @param highlight the highlight color
     * @param shadow    the shadow color
     */
    public static Border bevelRaised
        (int levels, Color highlight, Color shadow)
    {
        boolean error =
            (levels <= 0)
            || (highlight == null)
            || (shadow == null);
        
        if (error)
            return bevelRaised();
        
        return bevelLowered(levels, shadow, highlight);
    }
    
    
    /**
     * <p>Creates a raised bevel border in which the thickness
     * of each edge is equal to the number of levels.</p>
     * 
     * <p>The north and west edges are set up as follows:</p>
     * 
     * <p>The color smoothly interpolates
     * from highlightOuter on the outside
     * to highlightInner on the inside.</p>
     * 
     * <p>The south and east edges are set up as follows:</p>
     * 
     * <p>The color smoothly interpolates
     * from shadowOuter on the outside
     * to shadowInner on the inside.</p>
     * 
     * <p>Note that these color settings are the opposite of
     * those for a lowered border.</p>
     * 
     * <p>It is an error if levels is not at least 2 or if any
     * color is <code>null</code>.  In that case, a default
     * bevel border is returned.</p>
     * 
     * <p>The parameter order outer-inner-inner-outer
     * is different from that used in the Java class
     * <code>BorderFactory</code> for similar methods.</p>
     * 
     * @param levels         the thickness of each edge
     * @param highlightOuter the outer highlight color
     * @param highlightInner the inner highlight color
     * @param shadowInner    the inner shadow color
     * @param shadowOuter    the outer shadow color
     */
    public static Border bevelRaised
        (int levels,
         Color highlightOuter, Color highlightInner,
         Color shadowInner,    Color shadowOuter)
    {
        boolean error =
            (levels <= 1)
            || (highlightOuter == null)
            || (highlightInner == null)
            || (shadowInner == null)
            || (shadowOuter == null);
        
        if (error)
            return bevelRaised();
            
        return bevelLowered
            (levels,
             shadowOuter,    shadowInner,
             highlightInner, highlightOuter);
    }
    
    
    /**
     * <p>Returns a <code>TitledBorder</code> using the given
     * parameters.</p>
     * 
     * <p>If <code>t</code> is <code>null</code>,
     * it is set to an empty string.</p>
     * 
     * <p>The title is centered in the top position.</p>
     * 
     * <p>A title is inserted into an existing border.  
     * This constructor uses a default line border of
     * thickness 2 in black.</p>
     * 
     * @param t         the title string
     */
    public static TitledBorder title(String t)
    {
        return title(t, H_DEFAULT, V_DEFAULT, null, null, null);
    }
    
    
    /**
     * <p>Returns a <code>TitledBorder</code> using the given
     * parameters.</p>
     * 
     * <p>If <code>t</code> is <code>null</code>,
     * it is set to an empty string.</p>
     * 
     * <p>The title is centered in the top position.</p>
     * 
     * <p>A title is inserted into an existing border.  If the
     * <code>base</code> is non-<code>null</code> that border is
     * used; otherwise a default line border is used.  We have
     * changed the default line border to a line border of
     * thickness 2 in black.  The Java default is of thickness 1
     * and is in a pale blue that is almost invisible.</p>
     * 
     * @param t         the title string
     * @param base      the base border in which to place the title
     */
    public static TitledBorder title(String t, Border base)
    {
        return title(t, H_DEFAULT, V_DEFAULT, base, null, null);
    }
    
    
    /**
     * <p>Returns a <code>TitledBorder</code> using the given
     * parameters.</p>
     * 
     * <p>If <code>t</code> is <code>null</code>,
     * it is set to an empty string.</p>
     * 
     * <p>The title is centered in the top position.</p>
     * 
     * <p>A title is inserted into an existing border.  
     * This constructor uses a default line border of
     * thickness 2 in black.</p>
     * 
     * <p>The <code>font</code> and <code>fontcolor</code> may be
     * supplied or left as <code>null</code> to accept defaults.</p>
     * 
     * @param t         the title string
     * @param font      the title font
     * @param fontcolor the title font color
     */
    public static TitledBorder title
        (String t,
         Font   font,
         Color  fontcolor)
    {
        return title(t, H_DEFAULT, V_DEFAULT, null, font, fontcolor);
    }
    
    
    /**
     * <p>Returns a <code>TitledBorder</code> using the given
     * parameters.</p>
     * 
     * <p>If <code>t</code> is <code>null</code>,
     * it is set to an empty string.</p>
     * 
     * <p>The title is centered in the top position.</p>
     * 
     * <p>A title is inserted into an existing border.  If the
     * <code>base</code> is non-<code>null</code> that border is
     * used; otherwise a default line border is used.  We have
     * changed the default line border to a line border of
     * thickness 2 in black.  The Java default is of thickness 1
     * and is in a pale blue that is almost invisible.</p>
     * 
     * <p>The <code>font</code> and <code>fontcolor</code> may be
     * supplied or left as <code>null</code> to accept defaults.</p>
     * 
     * @param t         the title string
     * @param base      the base border in which to place the title
     * @param font      the title font
     * @param fontcolor the title font color
     */
    public static TitledBorder title
        (String t,
         Border base,
         Font   font,
         Color  fontcolor)
    {
        return title(t, H_DEFAULT, V_DEFAULT, base, font, fontcolor);
    }
    
    
    /**
     * <p>Returns a <code>TitledBorder</code> using the given
     * parameters.</p>
     * 
     * <p>If <code>t</code> is <code>null</code>,
     * it is set to an empty string.</p>
     * 
     * <p>The valid values for <code>hPosition</code> are:</p>
     * 
     * <ul>
     *   <li><code>LEFT</code></li>
     *   <li><code>RIGHT</code></li>
     *   <li><code>CENTER</code></li>
     *   <li><code>LEADING</code></li>
     *   <li><code>TRAILING</code></li>
     *   <li><code>H_DEFAULT</code> (<code><i>CENTER</i></code>)</li>
     * </ul>
     * 
     * <p>The valid values for <code>vPosition</code> are:</p>
     * 
     * <ul>
     *   <li><code>TOP</code></li>
     *   <li><code>ABOVE_TOP</code></li>
     *   <li><code>BELOW_TOP</code></li>
     *   <li><code>BOTTOM</code></li>
     *   <li><code>ABOVE_BOTTOM</code></li>
     *   <li><code>BELOW_BOTTOM</code></li>
     *   <li><code>V_DEFAULT</code> (<code><i>TOP</i></code>)</li>
     * </ul>
     * 
     * <p>If either <code>hPosition</code> or <code>vPosition</code>
     * are invalid, they are set to the corresponding default.</p>
     * 
     * <p>The constants listed above are defined in the Java class
     * <code>TitledBorder</code> except for the default constants.
     * All constants are redefined in this class and the defaults
     * are added as specified above.</p>
     * 
     * <p>A title is inserted into an existing border.  
     * This constructor uses a default line border of
     * thickness 2 in black.</p>
     * 
     * @param t         the title string
     * @param hPosition the horizontal position
     * @param vPosition the vertical   position
     */
    public static TitledBorder title
        (String t,
         int    hPosition,
         int    vPosition)
    {
        return title(t, hPosition, vPosition, null, null, null);
    }
    
    
    /**
     * <p>Returns a <code>TitledBorder</code> using the given
     * parameters.</p>
     * 
     * <p>If <code>t</code> is <code>null</code>,
     * it is set to an empty string.</p>
     * 
     * <p>The valid values for <code>hPosition</code> are:</p>
     * 
     * <ul>
     *   <li><code>LEFT</code></li>
     *   <li><code>RIGHT</code></li>
     *   <li><code>CENTER</code></li>
     *   <li><code>LEADING</code></li>
     *   <li><code>TRAILING</code></li>
     *   <li><code>H_DEFAULT</code> (<code><i>CENTER</i></code>)</li>
     * </ul>
     * 
     * <p>The valid values for <code>vPosition</code> are:</p>
     * 
     * <ul>
     *   <li><code>TOP</code></li>
     *   <li><code>ABOVE_TOP</code></li>
     *   <li><code>BELOW_TOP</code></li>
     *   <li><code>BOTTOM</code></li>
     *   <li><code>ABOVE_BOTTOM</code></li>
     *   <li><code>BELOW_BOTTOM</code></li>
     *   <li><code>V_DEFAULT</code> (<code><i>TOP</i></code>)</li>
     * </ul>
     * 
     * <p>If either <code>hPosition</code> or <code>vPosition</code>
     * are invalid, they are set to the corresponding default.</p>
     * 
     * <p>The constants listed above are defined in the Java class
     * <code>TitledBorder</code> except for the default constants.
     * All constants are redefined in this class and the defaults
     * are added as specified above.</p>
     * 
     * <p>A title is inserted into an existing border.  If the
     * <code>base</code> is non-<code>null</code> that border is
     * used; otherwise a default line border is used.  We have
     * changed the default line border to a line border of
     * thickness 2 in black.  The Java default is of thickness 1
     * and is in a pale blue that is almost invisible.</p>
     * 
     * @param t         the title string
     * @param hPosition the horizontal position
     * @param vPosition the vertical   position
     * @param base      the base border in which to place the title
     */
    public static TitledBorder title
        (String t,
         int    hPosition,
         int    vPosition,
         Border base)
    {
        return title(t, hPosition, vPosition, base, null, null);
    }
    
    
    /**
     * <p>Returns a <code>TitledBorder</code> using the given
     * parameters.</p>
     * 
     * <p>If <code>t</code> is <code>null</code>,
     * it is set to an empty string.</p>
     * 
     * <p>The valid values for <code>hPosition</code> are:</p>
     * 
     * <ul>
     *   <li><code>LEFT</code></li>
     *   <li><code>RIGHT</code></li>
     *   <li><code>CENTER</code></li>
     *   <li><code>LEADING</code></li>
     *   <li><code>TRAILING</code></li>
     *   <li><code>H_DEFAULT</code> (<code><i>CENTER</i></code>)</li>
     * </ul>
     * 
     * <p>The valid values for <code>vPosition</code> are:</p>
     * 
     * <ul>
     *   <li><code>TOP</code></li>
     *   <li><code>ABOVE_TOP</code></li>
     *   <li><code>BELOW_TOP</code></li>
     *   <li><code>BOTTOM</code></li>
     *   <li><code>ABOVE_BOTTOM</code></li>
     *   <li><code>BELOW_BOTTOM</code></li>
     *   <li><code>V_DEFAULT</code> (<code><i>TOP</i></code>)</li>
     * </ul>
     * 
     * <p>If either <code>hPosition</code> or <code>vPosition</code>
     * are invalid, they are set to the corresponding default.</p>
     * 
     * <p>The constants listed above are defined in the Java class
     * <code>TitledBorder</code> except for the default constants.
     * All constants are redefined in this class and the defaults
     * are added as specified above.</p>
     * 
     * <p>A title is inserted into an existing border.  
     * This constructor uses a default line border of
     * thickness 2 in black.</p>
     * 
     * <p>The <code>font</code> and <code>fontcolor</code> may be
     * supplied or left as <code>null</code> to accept defaults.</p>
     * 
     * @param t         the title string
     * @param hPosition the horizontal position
     * @param vPosition the vertical   position
     * @param font      the title font
     * @param fontcolor the title font color
     */
    public static TitledBorder title
        (String t,
         int    hPosition,
         int    vPosition,
         Font   font,
         Color  fontcolor)
    {
        return title(t, hPosition, vPosition, null, font, fontcolor);
    }
    
    
    /**
     * <p>Returns a <code>TitledBorder</code> using the given
     * parameters.</p>
     * 
     * <p>If <code>t</code> is <code>null</code>,
     * it is set to an empty string.</p>
     * 
     * <p>The valid values for <code>hPosition</code> are:</p>
     * 
     * <ul>
     *   <li><code>LEFT</code></li>
     *   <li><code>RIGHT</code></li>
     *   <li><code>CENTER</code></li>
     *   <li><code>LEADING</code></li>
     *   <li><code>TRAILING</code></li>
     *   <li><code>H_DEFAULT</code> (<code><i>CENTER</i></code>)</li>
     * </ul>
     * 
     * <p>The valid values for <code>vPosition</code> are:</p>
     * 
     * <ul>
     *   <li><code>TOP</code></li>
     *   <li><code>ABOVE_TOP</code></li>
     *   <li><code>BELOW_TOP</code></li>
     *   <li><code>BOTTOM</code></li>
     *   <li><code>ABOVE_BOTTOM</code></li>
     *   <li><code>BELOW_BOTTOM</code></li>
     *   <li><code>V_DEFAULT</code> (<code><i>TOP</i></code>)</li>
     * </ul>
     * 
     * <p>If either <code>hPosition</code> or <code>vPosition</code>
     * are invalid, they are set to the corresponding default.</p>
     * 
     * <p>The constants listed above are defined in the Java class
     * <code>TitledBorder</code> except for the default constants.
     * All constants are redefined in this class and the defaults
     * are added as specified above.</p>
     * 
     * <p>A title is inserted into an existing border.  If the
     * <code>base</code> is non-<code>null</code> that border is
     * used; otherwise a default line border is used.  We have
     * changed the default line border to a line border of
     * thickness 2 in black.  The Java default is of thickness 1
     * and is in a pale blue that is almost invisible.</p>
     * 
     * <p>The <code>font</code> and <code>fontcolor</code> may be
     * supplied or left as <code>null</code> to accept defaults.</p>
     * 
     * @param t         the title string
     * @param hPosition the horizontal position
     * @param vPosition the vertical   position
     * @param base      the base border in which to place the title
     * @param font      the title font
     * @param fontcolor the title font color
     */
    public static TitledBorder title
        (String t,
         int    hPosition,
         int    vPosition,
         Border base,
         Font   font,
         Color  fontcolor)
    {
        if (t == null)
            t = "";
        
        TitledBorder titled = new TitledBorder(t);
        
        switch (hPosition) {
            case LEFT:
            case RIGHT:
            case CENTER:
            case LEADING:
            case TRAILING:
                break;
            
            default:
                hPosition = H_DEFAULT;
                break;
        }
        
        switch (vPosition) {
            case TOP:
            case ABOVE_TOP:
            case BELOW_TOP:
            case BOTTOM:
            case ABOVE_BOTTOM:
            case BELOW_BOTTOM:
            break;
        
            default:
                vPosition = V_DEFAULT;
                break;
        }
        
        titled.setTitleJustification(hPosition);
        
        titled.setTitlePosition(vPosition);
        
        if (base == null)
            base = DEFAULT_LINE_BORDER;
        
        titled.setBorder(base);
        
        if (font != null)
            titled.setTitleFont(font);
        
        if (fontcolor != null)
            titled.setTitleColor(fontcolor);
        
        return titled;
    }
    
    
    /**
     * <p>Returns a compound border constructed from the
     * given outer and inner borders.</p>
     * 
     * <p>If either border is <code>null</code>, the other
     * border is returned directly.</p>
     * 
     * @param outer the outer border
     * @param inner the inner border
     */
   public static Border compound(Border outer, Border inner) {
       if (outer == null)
           return inner;
       
       if (inner == null)
           return outer;
       
       return BorderFactory.createCompoundBorder(outer, inner);
   }
   
   
   /**
    * <p>Returns a compound border constructed from the
    * given array of borders.</p>
    * 
    * <p>The initial border in the array will be the
    * outermost border in the compound.</p>
    * 
    * <p>The sequence of array elements is viewed as
    * outermost to innermost.</p>
    * 
    * @param array an array of borders to compound
    */
   public static Border compound(Border[] array) {
       if (array == null)
           return null;
       
       int length = array.length;
       
       int N = length - 1;
       
       Border border = null;
       
       for (int i = N; i >= 0; i--)
           border = compound(array[i], border);
       
       return border;
   }
   
   
   /**
    * <p>Returns a compound with the given center border
    * in the middle and the wrap border as outer and inner.</p>
    * 
    * @param center the border in the middle
    * @param wrap   the border to wrap as both outer and inner
    */
   public static Border sandwich(Border center, Border wrap) {
       return compound(wrap, compound(center, wrap));
   }
}

