/*
 * @(#)JPTComponent.java    2.5.0  15 May 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 edu.neu.ccs.util.*;

import java.awt.*;

import javax.swing.*;
import javax.swing.border.Border;

import java.beans.*;

/**
 * <P>The abstract class <CODE>JPTComponent</CODE> extends <CODE>JComponent</CODE>
 * in order to support property change and add mouse and key adapters.</P>
 *
 * <P>Specifically, this class:</P>
 *
 * <UL>
 *   <LI>implements those methods in <CODE>SupportsPropertyChange</CODE> that are
 *       not already (publically) implemented in <CODE>JComponent</CODE></LI>
 *   <LI>adds a <CODE>KeyActionAdapter</CODE> for this component</LI>
 *   <LI>adds a <CODE>MouseActionAdapter</CODE> for this component</LI>
 * </UL>
 *
 * <p>As of 2.5.0, convenience methods were added to add borders
 * based on the methods in class <code>Borders</code>.</p>
 * 
 * @author  Richard Rasala
 * @version 2.5.0
 * @since   2.3
 */
public abstract class JPTComponent
    extends JComponent
    implements JPTConstants, SupportsPropertyChange
{
    /**
     * <P>The forwarding listener for this JPTComponent object to implement
     * the interface <CODE>SupportsPropertyChange</CODE>.</P>
     *
     * @see #getForwardingListener()
     */
    private PropertyChangeForwardingListener forwardingListener
        = new PropertyChangeForwardingListener(this);
    
    
    /** The key action adapter. */
    private KeyActionAdapter keyActionAdapter = new KeyActionAdapter(this);
    
    
    /** The mouse action adapter. */
    private MouseActionAdapter mouseActionAdapter = new MouseActionAdapter(this);
    
    
    /**
     * Add all items in the given <CODE>PropertyChangeListener</CODE> array
     * to the listener list.  These items are registered for all properties.
     *
     * @param listeners the PropertyChangeListener array to be added
     */
    public final void addPropertyChangeListeners(PropertyChangeListener[] listeners)
    {
        if (listeners == null)
            return;
        
        int length = listeners.length;
        
        for (int i = 0; i < length; i++)
            addPropertyChangeListener(listeners[i]);
    }
    
    
    /**
     * Add all items in the given <CODE>PropertyChangeListener</CODE> array to
     * the listener list for a specific property.  These items will be invoked
     * only when a call on <CODE>firePropertyChange</CODE> names that specific
     * property.
     *
     * @param listeners the PropertyChangeListener array to be added
     */
    public final void addPropertyChangeListeners(
        String propertyName,
        PropertyChangeListener[] listeners)
    {
        if (listeners == null)
            return;
        
        int length = listeners.length;
        
        for (int i = 0; i < length; i++)
            addPropertyChangeListener(propertyName, listeners[i]);
    }
    
    
    /**
     * Check if there are any listeners for a specific property.
     *
     * @param propertyName the name of the property to check 
     */
    public final boolean hasListeners(String propertyName) {
        PropertyChangeListener[] listeners
            = getPropertyChangeListeners(propertyName);
        
        return (listeners == null) ? false : (listeners.length > 0);
    }
    
    
    /**
     * <P>Report a bound property update to any registered listeners.</P>
     *
     * <P>No event is fired if the old and new values are equal and
     * non-<CODE>null</CODE>.</P>
     *
     * <P>In order to implement <CODE>SupportsPropertyChange</CODE>, this
     * method makes <CODE>public</CODE> a method that is declared
     * <CODE>protected</CODE> in <CODE>JComponent</CODE>.</P>
     *
     * @param propertyName the programmatic name of the property that was changed
     * @param oldValue the old value of the property
     * @param newValue the new value of the property
     */
    public final void firePropertyChange(
        String propertyName,
        Object oldValue,
        Object newValue)
    {
        super.firePropertyChange(propertyName, oldValue, newValue);
    }
    
    
    /**
     * <P>Fire an existing <CODE>PropertyChangeEvent</CODE> to any registered
     * listeners.</P>
     
     * <P>No event is fired if the given event's old and new values are equal
     * and non-<CODE>null</CODE>.</P>
     *
     * @param evt the PropertyChangeEvent object
     */
    public final void firePropertyChange(PropertyChangeEvent evt) {
        if (evt == null)
            return;
        
        String property = evt.getPropertyName();
        Object oldValue = evt.getOldValue();
        Object newValue = evt.getNewValue();
        
        firePropertyChange(property, oldValue, newValue);
    }
    
    
    /**
     * Returns the <CODE>PropertyChangeForwardingListener</CODE> that
     * will forward the property change events it receives to this object.
     *
     * @return the forwarding listener
     */
    public final PropertyChangeForwardingListener getForwardingListener() {
        return forwardingListener;
    }
    
    

    /**
     * Add the forwarding listener as a property change listener
     * for the given object if the object supports property change.
     *
     * @param object the object that should add the forwarding listener
     */
    public final void addForwardingListener(Object object) {
        if (object instanceof SupportsPropertyChange) {
            SupportsPropertyChange spc = (SupportsPropertyChange) object;
            spc.addPropertyChangeListener(getForwardingListener());
        }
    }
    
    
    /**
     * Remove the forwarding listener as a property change listener
     * for the given object if the object supports property change.
     *
     * @param object the object that should remove the forwarding listener
     */
    public final void removeForwardingListener(Object object) {
        if (object instanceof SupportsPropertyChange) {
            SupportsPropertyChange spc = (SupportsPropertyChange) object;
            spc.removePropertyChangeListener(getForwardingListener());
        }
    }
    
    
    /**
     * Remove the forwarding listener as a property change listener
     * for the old object if the old object supports property change
     * and add the forwarding listener as a property change listener
     * for the new object if the new object supports property change.
     *
     * @param oldobject the old object that should remove the forwarding listener
     * @param newobject the new object that should add the forwarding listener
     */
    public final void removeAndAddForwardingListener(Object oldobject, Object newobject)
    {
        removeForwardingListener(oldobject);
        addForwardingListener(newobject);
    }    
    
    
    /**
     * Returns this component's key action adapter.
     *
     * @return this component's key action adapter
     */
    public final KeyActionAdapter getKeyActionAdapter() {
        return keyActionAdapter;
    }
    
    
    /**
     * Returns this component's mouse action adapter.
     *
     * @return this component's mouse action adapter
     */
    public final MouseActionAdapter getMouseActionAdapter() {
        return mouseActionAdapter;
    }
    
    
    /**
     * <p>Adds the given border as an outer wrapper
     * around the current border of the component.</p>
     * 
     * <p>If the current border is <code>null</code>,
     * then sets the given border as the border.</p>
     * 
     * <p>Does nothing if the given border is
     * <code>null</code>.</p>
     * 
     * <p>Returns the final border of the component
     * 
     * @param border the border to add as a wrapper
     */
    public Border addBorder(Border border) {
        Border current = getBorder();
        
        if (border == null)
            return current;
        
        if (current == null) {
            setBorder(border);
            return border;
        }
        
        Border compound
            = BorderFactory.createCompoundBorder
                (border, current);
        
        setBorder(compound);
        
        return compound;
    }
    
    
    /**
     * <p>Removes the current border and returns it.</p>
     */
    public Border removeBorder() {
        Border current = getBorder();
        
        setBorder(null);
        
        return current;
    }
    
    
    /**
     * <p>Adds an empty border
     * with the given thickness.</p>
     * 
     * @param thickness the border thickness
     */
    public void emptyBorder(int thickness)
    {
        emptyBorder(thickness, thickness, thickness, thickness);
    }
    
    
    /**
     * <p>Adds 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 void emptyBorder(int vgap, int hgap)
    {
        emptyBorder(vgap, hgap, vgap, hgap);
    }
    
    
    /**
     * <p>Adds 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 void emptyBorder
        (int top, int left, int bottom, int right)
    {
        addBorder(Borders.empty(top, left, bottom, right));
    }
    
    
    /**
     * <p>Adds 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 void matteBorder(int thickness, Color color)
    {
        matteBorder(thickness, thickness, thickness, thickness, color);
    }
    
    
    /**
     * <p>Adds 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 void matteBorder(int vgap, int hgap, Color color)
    {
        matteBorder(vgap, hgap, vgap, hgap, color);
    }
    
    
    /**
     * <p>Adds 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 void matteBorder
        (int top, int left, int bottom, int right, Color color)
    {
        addBorder(Borders.matte(top, left, bottom, right, color));
    }
    
    
    /**
     * <p>Adds 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 void matteBorder(int thickness, Icon tileicon)
    {
        matteBorder(thickness, thickness, thickness, thickness, tileicon);
    }
    
    
    /**
     * <p>Adds 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 void matteBorder(int vgap, int hgap, Icon tileicon)
    {
        matteBorder(vgap, hgap, vgap, hgap, tileicon);
    }
    
    
    /**
     * <p>Adds 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 void matteBorder
        (int top, int left, int bottom, int right, Icon tileicon)
    {
        addBorder(Borders.matte(top, left, bottom, right, tileicon));
    }
    
    
    /**
     * <p>Adds a line border
     * with the color black.</p>
     */
    public void lineBorder()
    {
        lineBorder(Color.black, 1);
    }
    
    
    /**
     * <p>Adds a line border
     * with the given color.</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 void lineBorder(Color color)
    {
        lineBorder(color, 1);
    }
    
    
    /**
     * <p>Adds 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>   matteBorder(thickness, color);</pre>
     * 
     * <p>but the parameters are in the opposite order.</p>
     * 
     * @param color     the border color
     * @param thickness the border thickness
     */
    public void lineBorder(Color color, int thickness)
    {
        addBorder(Borders.line(color, thickness));
    }
    
    
    /**
     * <p>Adds a default lowered etch border.</p>
     */
    public void etchBorderLowered()
    {
        addBorder(Borders.etchLowered());
    }
    
    
    /**
     * <p>Adds a lowered etch border
     * with the given highlight and shadow colors.</p>
     * 
     * @param highlight the highlight color
     * @param shadow    the shadow color
     */
    public void etchBorderLowered
        (Color highlight, Color shadow)
    {
        addBorder(Borders.etchLowered(highlight, shadow));
    }
    
    
    /**
     * <p>Adds a lowered etch border
     * with the given parameters.</p>
     * 
     * @param levels    the number of levels in each color band
     * @param highlight the highlight color
     * @param shadow    the shadow color
     */
    public void etchBorderLowered
        (int levels, Color highlight, Color shadow)
    {
        addBorder(Borders.etchLowered(levels, highlight, shadow));
    }
    
    
    /**
     * <p>Adds a default raised etch border.</p>
     */
    public void etchBorderRaised()
    {
        addBorder(Borders.etchRaised());
    }
    
    
    /**
     * <p>Adds a raised etch border
     * with the given highlight and shadow colors.</p>
     * 
     * @param highlight the highlight color
     * @param shadow    the shadow color
     */
    public void etchBorderRaised
        (Color highlight, Color shadow)
    {
        addBorder(Borders.etchRaised(highlight, shadow));
    }
    
    
    /**
     * <p>Adds a raised etch border
     * with the given parameters.</p>
     * 
     * @param levels    the number of levels in each color band
     * @param highlight the highlight color
     * @param shadow    the shadow color
     */
    public void etchBorderRaised
        (int levels, Color highlight, Color shadow)
    {
        addBorder(Borders.etchRaised(levels, highlight, shadow));
    }
    
    
    /**
     * <p>Adds a default lowered bevel border.</p>
     */
    public void bevelBorderLowered()
    {
        addBorder(Borders.bevelLowered());
    }
    
    
    /**
     * <p>Adds a lowered bevel border
     * with the given highlight and shadow colors.</p>
     * 
     * @param highlight the highlight color
     * @param shadow    the shadow color
     */
    public void bevelBorderLowered
        (Color highlight, Color shadow)
    {
        addBorder(Borders.bevelLowered(highlight, shadow));
    }
    
    
    /**
     * <p>Adds a lowered bevel border
     * with the given colors.</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 void bevelBorderLowered
        (Color highlightOuter, Color highlightInner,
         Color shadowInner,    Color shadowOuter)
    {
        Border border =
            Borders.bevelLowered
                (highlightOuter, highlightInner,
                 shadowInner,    shadowOuter);
        
        addBorder(border);
    }
    
    
    /**
     * <p>Adds a lowered bevel border
     * with the given parameters.</p>
     * 
     * @param levels    the thickness of each edge
     * @param highlight the highlight color
     * @param shadow    the shadow color
     */
    public void bevelBorderLowered
        (int levels, Color highlight, Color shadow)
    {
        addBorder(Borders.bevelLowered(levels, highlight, shadow));
    }
    
    
    /**
     * <p>Adds a lowered bevel border
     * with the given parameters.</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 void bevelBorderLowered
        (int levels,
            Color highlightOuter, Color highlightInner,
            Color shadowInner,    Color shadowOuter)
    {
        Border border =
            Borders.bevelLowered
                (levels,
                 highlightOuter, highlightInner,
                 shadowInner,    shadowOuter);
        
        addBorder(border);
    }
    
    
    /**
     * <p>Adds a raised bevel border.</p>
     */
    public void bevelBorderRaised()
    {
        addBorder(Borders.bevelRaised());
    }
    
    
    /**
     * <p>Adds a raised bevel border
     * with the given highlight and shadow colors.</p>
     * 
     * @param highlight the highlight color
     * @param shadow    the shadow color
     */
    public void bevelBorderRaised
        (Color highlight, Color shadow)
    {
        addBorder(Borders.bevelRaised(highlight, shadow));
    }
    
    
    /**
     * <p>Adds a raised bevel border
     * with the given colors.</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 void bevelBorderRaised
        (Color highlightOuter, Color highlightInner,
         Color shadowInner,    Color shadowOuter)
    {
        Border border =
            Borders.bevelRaised
                (highlightOuter, highlightInner,
                 shadowInner,    shadowOuter);
        
        addBorder(border);
    }
    
    
    /**
     * <p>Adds a raised bevel border
     * with the given parameters.</p>
     * 
     * @param levels    the thickness of each edge
     * @param highlight the highlight color
     * @param shadow    the shadow color
     */
    public void bevelBorderRaised
        (int levels, Color highlight, Color shadow)
    {
        addBorder(Borders.bevelRaised(levels, highlight, shadow));
    }
    
    
    /**
     * <p>Adds a raised bevel border
     * with the given parameters.</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 void bevelBorderRaised
        (int levels,
            Color highlightOuter, Color highlightInner,
            Color shadowInner,    Color shadowOuter)
    {
        Border border =
            Borders.bevelRaised
                (levels,
                 highlightOuter, highlightInner,
                 shadowInner,    shadowOuter);
        
        addBorder(border);
    }
    
    
    /**
     * <p>Adds a title border
     * with 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>LEADING</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>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.</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 void titleBorder
        (String t,
         int    hPosition,
         int    vPosition,
         Border base,
         Font   font,
         Color  fontcolor)
    {
        Border border =
            Borders.title
                (t, hPosition, vPosition, base, font, fontcolor);
        
        addBorder(border);
    }
    
    
    /**
     * Sets the background of this object to the given color; then sets
     * the background of each component within this object to the same
     * color using <code>setDeepBackground</code> recursively for each
     * component that happens to extend <code>DisplayPanel</code> or
     * <code>JPTComponent</code>.
     *
     * @param c the background color
     */
    public void setDeepBackground(Color c) {
        setBackground(c);
        
        Component[] components = getComponents();
        
        int length = components.length;
        
        for (int i = 0; i < length; i++) {
            Component component = components[i];
            
            if (component == null)
                continue;
            
            if (component instanceof DisplayPanel) {
                DisplayPanel panel = (DisplayPanel) component;
                panel.setDeepBackground(c);
            }
            else
            if (component instanceof JPTComponent) {
                JPTComponent thing = (JPTComponent) component;
                thing.setDeepBackground(c);
            }
            else
                component.setBackground(c);
        }
    }
    
    
    /**
     * <p>Sets the background of all components in the hierarchy
     * of this object that are instances of the given class.</p>
     *
     * <p>The background of the object itself will be set only if
     * it is an instance of the given class.</p>
     *
     * <p>The hierarchy is determined recursively as entities in
     * instances of either <code>DisplayPanel</code> or
     * <code>JPTComponent</code>.
     *
     * @param c the background color
     * @param type the class whose objects should change
     */
    public void setDeepBackground(Color c, Class type) {
        if (type == null)
            return;
        
        if (type.isInstance(this))
            setBackground(c);
        
        Component[] components = getComponents();
        
        int length = components.length;
        
        for (int i = 0; i < length; i++) {
            Component component = components[i];
            
            if (component == null)
                continue;
            
            if (component instanceof DisplayPanel) {
                DisplayPanel panel = (DisplayPanel) component;
                panel.setDeepBackground(c, type);
            }
            else
            if (component instanceof JPTComponent) {
                JPTComponent thing = (JPTComponent) component;
                thing.setDeepBackground(c, type);
            }
            else
            if (type.isInstance(component)) {
                component.setBackground(c);
            }
        }
    }
    
    
    /**
     * <p>Frame this panel in a JPTFrame
     * and open the frame;
     * return the frame constructed.</p>
     *
     * @return the frame constructed
     * @since 2.3.5
     */
    public JPTFrame frame() {
        return frame(null, CENTER, null);
    }
    
    
    /**
     * <p>Frame this panel in a JPTFrame
     * and open the frame;
     * use the given location which should be either
     * <code>CENTER</code> or one of the standard constants
     * for one of the eight compass directions;
     * return the frame constructed.</p>
     *
     * @param location the constant representing the frame location
     * @return the frame constructed
     * @since 2.3.5
     */
    public JPTFrame frame(int location) {
        return frame(null, location, null);
    }
    
    
    /**
     * <p>Frame this panel in a JPTFrame
     * and open the frame;
     * use the given title for the frame;
     * return the frame constructed.</p>
     *
     * @param title the title for the frame
     * @return the frame constructed
     * @since 2.3.5
     */
    public JPTFrame frame(String title) {
        return frame(title, CENTER, null);
    }
    
    
    /**
     * <p>Frame this panel in a JPTFrame
     * and open the frame;
     * use the given title for the frame;
     * use the given location which should be either
     * <code>CENTER</code> or one of the standard constants
     * for one of the eight compass directions;
     * return the frame constructed.</p>
     *
     * @param title    the title for the frame
     * @param location the constant representing the frame location
     * @return the frame constructed
     * @since 2.3.5
     */
    public JPTFrame frame(String title, int location) {
        return frame(title, location, null);
    }
    
    
    /**
     * <p>Frame this panel in a JPTFrame
     * and open the frame;
     * use the given title for the frame;
     * use the given insets to inset the frame in the screen;
     * return the frame constructed.</p>
     *
     * @param title    the title for the frame
     * @param insets   the screen insets to use to adjust the location
     * @return the frame constructed
     * @since 2.3.5
     */
    public JPTFrame frame(String title, Insets insets) {
        return frame(title, CENTER, insets);
    }
    
    
    /**
     * <p>Frame this panel in a JPTFrame
     * and open the frame;
     * use the given title for the frame;
     * use the given location which should be either
     * <code>CENTER</code> or one of the standard constants
     * for one of the eight compass directions;
     * use the given insets to inset the frame in the screen;
     * return the frame constructed.</p>
     *
     * @param title    the title for the frame
     * @param location the constant representing the frame location
     * @param insets   the screen insets to use to adjust the location
     * @return the frame constructed
     * @since 2.3.5
     */
    public JPTFrame frame(String title, int location, Insets insets) {
        return JPTFrame.frame(this, title, location, insets);
    }
    
    
    /**
     * <p>Place this panel in a modal OK dialog
     * and open the dialog;
     * return the dialog constructed.</p>
     *
     * @return the dialog constructed
     * @since 2.3.5
     */
    public GeneralDialog OKDialog() {
        return OKDialog(null);
    }
    
    
    /**
     * <p>Place this panel in a modal OK dialog
     * and open the dialog;
     * use the given title for the dialog;
     * return the dialog constructed.</p>
     *
     * @param title the title for the dialog
     * @return the dialog constructed
     * @since 2.3.5
     */
    public GeneralDialog OKDialog(String title) {
        GeneralDialog dialog = GeneralDialog.makeOKDialog(this, title);
        dialog.setVisible(true);
        return dialog;
    }
    
    
    /**
     * <p>Place this panel in a modal OK-Cancel dialog
     * and open the dialog;
     * return the dialog constructed.</p>
     *
     * @return the dialog constructed
     * @since 2.3.5
     */
    public GeneralDialog OKCancelDialog() {
        return OKCancelDialog(null);
    }
    
    
    /**
     * <p>Place this panel in a modal OK-Cancel dialog
     * and open the dialog;
     * use the given title for the dialog;
     * return the dialog constructed.</p>
     *
     * @param title the title for the dialog
     * @return the dialog constructed
     * @since 2.3.5
     */
    public GeneralDialog OKCancelDialog(String title) {
        GeneralDialog dialog = GeneralDialog.makeOKCancelDialog(this, title);
        dialog.setVisible(true);
        return dialog;
    }
    
    
    /**
     * <p>Place this panel in a modal Yes-No-Cancel dialog
     * and open the dialog;
     * return the dialog constructed.</p>
     *
     * @return the dialog constructed
     * @since 2.3.5
     */
    public GeneralDialog YesNoCancelDialog() {
        return YesNoCancelDialog(null);
    }
    
    
    /**
     * <p>Place this panel in a modal Yes-No-Cancel dialog
     * and open the dialog;
     * use the given title for the dialog;
     * return the dialog constructed.</p>
     *
     * @param title the title for the dialog
     * @return the dialog constructed
     * @since 2.3.5
     */
    public GeneralDialog YesNoCancelDialog(String title) {
        GeneralDialog dialog = GeneralDialog.makeYesNoCancelDialog(this, title);
        dialog.setVisible(true);
        return dialog;
    }
    
    
    /**
     * <p>Place this panel in a modal general dialog
     * and open the dialog;
     * use the action data to define the dialog buttons;
     * return the dialog constructed.</p>
     *
     * <p>The parameter <code>actionData</code> 
     * is an <code>Object[][]</code> array that specifies 
     * each dialog <code>Action</code> in one of the following ways:</p>
     * 
     * <ul>
     *   <li> A subarray { action }             with an <code>Action</code></li>
     *   <li> A subarray { name }               with an action name</li>
     *   <li> A subarray { name, icon }         with an action name and icon</li>
     *   <li> A subarray { action, option }
     *   <li> A subarray { name, option }
     *   <li> A subarray { name, icon, option }
     * </ul>
     *
     * <p>If the option parameter is specified, it must be one of the following
     * values that will determine what is done to the dialog when the action is
     * finished:</p>
     *
     * <ul><code>
     *   <li> DialogAction.KEEP_OPEN</li>
     *   <li> DialogAction.AUTO_CLOSE</li>
     *   <li> DialogAction.SET_CANCEL</li>
     * </code></ul>
     *
     * <p>If the option parameter is not specified, it is taken to be
     * <code>DialogAction.AUTO_CLOSE</code>.</p>
     *
     * @param actionData the action data
     * @return the dialog constructed
     * @since 2.3.5
     */
    public GeneralDialog generalDialog(Object[][] actionData) {
        return generalDialog(null, actionData, null);
    }
    
    
    /**
     * <p>Place this panel in a modal general dialog
     * and open the dialog;
     * use the action data to define the dialog buttons;
     * use the default action object to define the default button;
     * return the dialog constructed.</p>
     *
     * <p>The parameter <code>actionData</code> 
     * is an <code>Object[][]</code> array that specifies 
     * each dialog <code>Action</code> in one of the following ways:</p>
     * 
     * <ul>
     *   <li> A subarray { action }             with an <code>Action</code></li>
     *   <li> A subarray { name }               with an action name</li>
     *   <li> A subarray { name, icon }         with an action name and icon</li>
     *   <li> A subarray { action, option }
     *   <li> A subarray { name, option }
     *   <li> A subarray { name, icon, option }
     * </ul>
     *
     * <p>If the option parameter is specified, it must be one of the following
     * values that will determine what is done to the dialog when the action is
     * finished:</p>
     *
     * <ul><code>
     *   <li> DialogAction.KEEP_OPEN</li>
     *   <li> DialogAction.AUTO_CLOSE</li>
     *   <li> DialogAction.SET_CANCEL</li>
     * </code></ul>
     *
     * <p>If the option parameter is not specified, it is taken to be
     * <code>DialogAction.AUTO_CLOSE</code>.</p>
     *
     * <p>The <code>Object</code> supplied for the default action must either be
     * an <code>Action</code> or a name that is supplied in the action data.  If
     * the parameter is <code>null</code>, no default action is set.</p>
     * 
     * @param actionData    the action data
     * @param defaultAction the default action
     * @return the dialog constructed
     * @since 2.3.5
     */
    public GeneralDialog generalDialog(Object[][] actionData, Object defaultAction) {
        return generalDialog(null, actionData, defaultAction);
    }
    
    
    /**
     * <p>Place this panel in a modal general dialog
     * and open the dialog;
     * use the given title for the dialog;
     * use the action data to define the dialog buttons;
     * return the dialog constructed.</p>
     *
     * <p>The parameter <code>actionData</code> 
     * is an <code>Object[][]</code> array that specifies 
     * each dialog <code>Action</code> in one of the following ways:</p>
     * 
     * <ul>
     *   <li> A subarray { action }             with an <code>Action</code></li>
     *   <li> A subarray { name }               with an action name</li>
     *   <li> A subarray { name, icon }         with an action name and icon</li>
     *   <li> A subarray { action, option }
     *   <li> A subarray { name, option }
     *   <li> A subarray { name, icon, option }
     * </ul>
     *
     * <p>If the option parameter is specified, it must be one of the following
     * values that will determine what is done to the dialog when the action is
     * finished:</p>
     *
     * <ul><code>
     *   <li> DialogAction.KEEP_OPEN</li>
     *   <li> DialogAction.AUTO_CLOSE</li>
     *   <li> DialogAction.SET_CANCEL</li>
     * </code></ul>
     *
     * <p>If the option parameter is not specified, it is taken to be
     * <code>DialogAction.AUTO_CLOSE</code>.</p>
     *
     * @param title      the title for the dialog
     * @param actionData the action data
     * @return the dialog constructed
     * @since 2.3.5
     */
    public GeneralDialog generalDialog(String title, Object[][] actionData) {
        return generalDialog(title, actionData, null);
    }
    
    
    /**
     * <p>Place this panel in a modal general dialog
     * and open the dialog;
     * use the given title for the dialog;
     * use the action data to define the dialog buttons;
     * use the default action object to define the default button;
     * return the dialog constructed.</p>
     *
     * <p>The parameter <code>actionData</code> 
     * is an <code>Object[][]</code> array that specifies 
     * each dialog <code>Action</code> in one of the following ways:</p>
     * 
     * <ul>
     *   <li> A subarray { action }             with an <code>Action</code></li>
     *   <li> A subarray { name }               with an action name</li>
     *   <li> A subarray { name, icon }         with an action name and icon</li>
     *   <li> A subarray { action, option }
     *   <li> A subarray { name, option }
     *   <li> A subarray { name, icon, option }
     * </ul>
     *
     * <p>If the option parameter is specified, it must be one of the following
     * values that will determine what is done to the dialog when the action is
     * finished:</p>
     *
     * <ul><code>
     *   <li> DialogAction.KEEP_OPEN</li>
     *   <li> DialogAction.AUTO_CLOSE</li>
     *   <li> DialogAction.SET_CANCEL</li>
     * </code></ul>
     *
     * <p>If the option parameter is not specified, it is taken to be
     * <code>DialogAction.AUTO_CLOSE</code>.</p>
     *
     * <p>The <code>Object</code> supplied for the default action must either be
     * an <code>Action</code> or a name that is supplied in the action data.  If
     * the parameter is <code>null</code>, no default action is set.</p>
     * 
     * @param title         the title for the dialog
     * @param actionData    the action data
     * @param defaultAction the default action
     * @return the dialog constructed
     * @since 2.3.5
     */
    public GeneralDialog generalDialog
        (String title, Object[][] actionData, Object defaultAction)
    {
        GeneralDialog dialog = new GeneralDialog(this, title, actionData, defaultAction);
        
        dialog.setVisible(true);
        return dialog;
    }
    
    
    /** Refreshes the component by repacking the parent window. */
    public void refreshComponent() {
        Refresh.packParentWindow(this);
    }
    
}
