/*
 * @(#)DisplayPanel.java    2.4.0   6 August 2005
 *
 * Copyright 2005
 * 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.*;
import edu.neu.ccs.codec.*;
import edu.neu.ccs.util.*;
import java.awt.*;
import java.util.Vector;
import javax.swing.*;

/**
 * <p>A <code>{@link Displayable Displayable}</code> is a
 * <code>{@link JPanel JPanel}</code> that recursively propagates 
 * <code>Displayable</code> method calls 
 * only to contained components 
 * that are also <code>Displayable</code>.</p>
 *
 * <p>This class is the base class for all panel-style containers
 * provided by the JPT.</p>
 *
 * <p>As of the 2.3.3, methods have been provided that permit a
 * <code>DisplayPanel</code> to <i>self-actualize</i>, that is,
 * to install inself in a window and open that window.</p>
 *
 * <p>The various <i>frame</i> methods install this panel into a
 * <code>JPTFrame</code> which is an extension of the Java class
 * <code>JFrame</code>.</p>
 *
 * <p>The various methods that have <i>dialog</i> in their name 
 * install this panel into a <code>GeneralDialog</code> which is
 * an extension of the Java class <code>JDialog</code>.</p>
 *
 * <p>Since no component should be installed in more than one
 * frame or dialog, the user should take care to call at most
 * one of the above methods and do this at most once while an
 * object is alive.  To support this rule and to ensure that
 * the panel is installed in a window at construction, we
 * recommend the following convention.</p>
 *
 * <p><i>Convention:</i> If you choose to call a <i>frame</i> or
 * <i>dialog</i> method, do this either as the last line in a
 * constructor or soon after the constructor returns.</p>
 * 
 * <p>Since the methods that install a <code>DisplayPanel</code>
 * in a frame or dialog return that window, the caller may save
 * the window reference and later close and reopen the window if
 * desired.</p>
 *
 * <p>As of 2.4.0, the class allows a caller to obtain an array
 * of all <code>Displayable</code>s installed in the panel.</p>
 *
 * @author  Richard Rasala
 * @author  Jeff Raab
 * @version 2.4.0
 * @since   1.0
 */
public class DisplayPanel
    extends JPanel 
    implements Displayable,
               MalformedDataListener,
               JPTConstants 
{
    /** Bound property name for the codec property. */
    public static final String CODEC = "codec";

    /** Background color before alert status was entered. */
    protected Color background = SystemColor.control;
    
    /** Identifier for codec used by this panel. */
    protected String codec = CodecUtilities.getDefaultCodec();
    
    
    //////////////////
    // Constructors //
    //////////////////
    
    /**
     * Constructs a panel with the default layout manager 
     * for a <code>{@link JPanel JPanel}</code>.
     */
    public DisplayPanel() {
        super();
    }
    
    
    /**
     * Constructs a panel with the default layout manager 
     * for a <code>{@link JPanel JPanel}</code>
     * and the given double-buffering strategy.
     *
     * @param isDoubleBuffered whether or not the panel
     *      makes use of double-buffering
     */
    public DisplayPanel(boolean isDoubleBuffered) {
        super(isDoubleBuffered);
    }
    
    
    /**
     * Constructs a panel with the given layout manager.
     *
     * @param layout the desired layout manager
     */
    public DisplayPanel(LayoutManager layout) {
        super(layout);
    }
    
    
    /**
     * Constructs a panel with the given layout manager
     * and the given double-buffering strategy.
     *
     * @param layout the desired layout manager
     * @param isDoubleBuffered whether or not the panel
     *      makes use of double-buffering
     */
    public DisplayPanel(
        LayoutManager layout, 
        boolean isDoubleBuffered) 
    {
        super(layout, isDoubleBuffered);
    }
    
    
    /////////////////
    // Displayable //
    /////////////////
    
    /**
     * <p>Returns an array of all <code>Displayable</code> objects
     * installed as <code>Component</code>s in this panel.</p>
     *
     * <p>This method uses the Java method <code>getComponent(int)</code>
     * to obtain <code>Component</code>s in order and filter out those
     * that implement the <code>Displayable</code> interface.</p>
     *
     * <p>Returns a non-<code>null</code> empty array if this panel does
     * not contain any <code>Displayable</code>s.</p>
     */
    public Displayable[] getDisplayables() {
        int count = 0;
        
        for (int i = 0; i < getComponentCount(); i++) {
            Component c = getComponent(i);

            if (c instanceof Displayable)
                count++;
        }
        
        Displayable[] array = new Displayable[count];
        
        count = 0;
        
        for (int i = 0; i < getComponentCount(); i++) {
            Component c = getComponent(i);

            if (c instanceof Displayable)
                array[count++] = (Displayable) c;
        }
        
        return array;
    }
    
    
    /**
     * <p>Sets the view states for the
     * <code>Displayable</code> objects in the container
     * to the data encoded in the given <code>String</code>.</p>
     * 
     * <p>If the given encoded data is <code>null</code>,
     * then the view states are not changed.</p>
     *
     * <p>The <code>String</code> data is decoded using the
     * method <code>Strings.decode</code> to allow the data
     * to be submitted in several formats.</p>
     *
     * <p>Sets the view states of as many
     * <code>Displayable</code> objects as possible but
     * ignores any mismatch in the number of <code>Displayable</code>
     * objects and the number of <code>String</code>s decoded from
     * the given encoded <code>String</code> data.</p>
     *
     * <p>Fires property change VIEW_STATE.</p>
     *
     * @param data the encoded <code>String</code> data
     */
    public void setViewState(String data) {
        if (data == null)
            return;
    
        setViewStates(Strings.decode(data));
    }
    
    
    /**
     * <p>Sets the view states for the
     * <code>Displayable</code> objects in the container
     * to the corresponding elements in the given array
     * of <code>String</code>s.</p>
     * 
     * <p>If the given data array is <code>null</code>,
     * then the view states are not changed.</p>
     * 
     * <p>Sets the view states of as many
     * <code>Displayable</code> objects as possible but
     * ignores any mismatch in the number of <code>Displayable</code>
     * objects and the number of <code>String</code>s in the array.</p>
     *
     * <p>Fires property change VIEW_STATE.</p>
     *
     * @param data the view state array data
     */
    public void setViewStates(String[] data) {
        if (data == null)
            return;
    
        // set the view state for as many Displayable objects 
        // as possible from the data
        //
        // do not change the view states of objects for which
        // no data is provided
        Displayable[] array = getDisplayables();
        
        int count = Math.min(data.length, array.length);
        
        for (int i = 0; i < count; i++)
            array[i].setViewState(data[i]);
        
        // notify listeners of property change
        firePropertyChange(VIEW_STATE, null, null);
    }
    
    
    /**
     * <p>Returns the encoded view state for the 
     * <code>Displayable()</code> objects in the panel.</p>
     *
     * <p>The encoded data is constructed by passing the
     * array returned by <code>getViewStates()</code> to
     * the method <code>CodecUtilities.encode</code>.
     * This method call uses the codec returned by the
     * method <code>getCodec()</code>.</p>
     */
    public String getViewState() {
        return CodecUtilities.encode(getViewStates(), getCodec());
    }
    
    
    /**
     * <p>Returns a <code>String</code> array of the view states
     * for the corresponding <code>Displayable()</code> items
     * in the array returned by <code>getDisplayables()</code>.</p>
     */
    public String[] getViewStates() {
        Displayable[] array = getDisplayables();
        
        int length = array.length;
        
        String[] result = new String[length];
        
        for (int i = 0; i < length; i++)
            result[i] = array[i].getViewState();
        
        return result;
    }
    
    
    /**
     * <p>Sets the default view states for the
     * <code>Displayable</code> objects in the container
     * to the data encoded in the given <code>String</code>.</p>
     * 
     * <p>If the given encoded data is <code>null</code>,
     * then the default view states are not changed.</p>
     *
     * <p>The <code>String</code> data is decoded using the
     * method <code>Strings.decode</code> to allow the data
     * to be submitted in several formats.</p>
     *
     * <p>Sets the default view states of as many
     * <code>Displayable</code> objects as possible but
     * ignores any mismatch in the number of <code>Displayable</code>
     * objects and the number of <code>String</code>s decoded from
     * the given encoded <code>String</code> data.</p>
     *
     * <p>Fires property change DEFAULT_VIEW_STATE.</p>
     *
     * @param data the encoded default <code>String</code> data
     */
    public void setDefaultViewState(String data) {
        if (data == null)
            return;
    
        setDefaultViewStates(Strings.decode(data));
    }
    
    
    /**
     * <p>Sets the default view states for the
     * <code>Displayable</code> objects in the container
     * to the corresponding elements in the given array
     * of <code>String</code>s.</p>
     * 
     * <p>If the given data array is <code>null</code>,
     * then the default view states are not changed.</p>
     * 
     * <p>Sets the default view states of as many
     * <code>Displayable</code> objects as possible but
     * ignores any mismatch in the number of <code>Displayable</code>
     * objects and the number of <code>String</code>s in the array.</p>
     *
     * <p>Fires property change DEFAULT_VIEW_STATE.</p>
     *
     * @param data the default view state array data
     */
    public void setDefaultViewStates(String[] data) {
        if (data == null)
            return;
    
        // set the default view state for as many Displayable objects 
        // as possible from the data
        //
        // do not change the default view states of objects for which
        // no data is provided
        Displayable[] array = getDisplayables();
        
        int count = Math.min(data.length, array.length);
        
        for (int i = 0; i < count; i++)
            array[i].setDefaultViewState(data[i]);
        
        // notify listeners of property change
        firePropertyChange(DEFAULT_VIEW_STATE, null, null);
    }
    
    
    /**
     * <p>Returns the encoded default view state for the 
     * <code>Displayable()</code> objects in the panel.</p>
     *
     * <p>The encoded data is constructed by passing the
     * array returned by <code>getDefaultViewStates()</code>
     * to the method <code>CodecUtilities.encode</code>.
     * This method call uses the codec returned by the
     * method <code>getCodec()</code>.</p>
     */
    public String getDefaultViewState() {
        return CodecUtilities.encode(getDefaultViewStates(), getCodec());
    }
    
    
    /**
     * <p>Returns a <code>String</code> array of the default view
     * states for the corresponding <code>Displayable()</code> items
     * in the array returned by <code>getDisplayables()</code>.</p>
     */
    public String[] getDefaultViewStates() {
        Displayable[] array = getDisplayables();
        
        int length = array.length;
        
        String[] result = new String[length];
        
        for (int i = 0; i < length; i++)
            result[i] = array[i].getDefaultViewState();
        
        return result;
    }
    
    
    /**
     * Enables or disables the panel, 
     * and recursively enables or disables its contents, 
     * based on the given value.
     *
     * @param isEnabled whether or not this panel is to be enabled
     */
    public void setEnabled(boolean isEnabled) {
        for (int i = 0; i < getComponentCount(); i++)
            getComponent(i).setEnabled(isEnabled);
        
        super.setEnabled(isEnabled);
    }
    
    
    /**
     * Recursively resets any <code>Displayable</code> objects
     * held in the panel.
     */
    public void reset() {
        Displayable[] array = getDisplayables();
        
        int count = array.length;
        
        for (int i = 0; i < count; i++)
            array[i].reset();
    }
    
    
    ///////////////////////////
    // MalformedDataListener //
    ///////////////////////////
    
    /**
     * Handles a malformed data event 
     * generated by a fragile input component 
     * by alerting the user 
     * through a change to the background color of the panel.
     *
     * @param evt the malformed data event
     */
    public void dataMalformed(MalformedDataEvent evt) {
        if (evt.isAlertOn())
            startAlert(evt);
        else
            endAlert(evt);    
    }
    
    
    /**
     * Turns on alert status for this component
     * and its children that are also <code>AlertListener</code>s.
     *
     * @param evt the alert event
     */
    public void startAlert(AlertEvent evt) {
        background = getBackground();
        setBackground(SystemColor.textHighlight);

        // recurse on other appropriate components in the container
        for (int i = 0; i < getComponentCount(); i++) {
            Component c = getComponent(i);
            
            if (c instanceof AlertListener) {
                AlertListener a = (AlertListener)c;
                a.startAlert(evt);
            }
        }
    }
    
    
    /**
     * Turns off alert status for this component
     * and its children that are also <code>AlertListener</code>s.
     *
     * @param evt the alert event
     */
    public void endAlert(AlertEvent evt) {
        setBackground(background);

        // recurse on other appropriate components in the container
        for (int i = 0; i < getComponentCount(); i++) {
            Component c = getComponent(i);
            
            if (c instanceof AlertListener) {
                AlertListener a = (AlertListener)c;
                a.endAlert(evt);
            }
        }
    }
    
    
    ////////////////
    // Public API //
    ////////////////

    /**
     * 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);
            }
        }
    }
    
    
    /**
     * Adds the specified component to the end of this container.
     *
     * @param c the component to be added
     * @return the component argument
     */
    public Component add(Component c) {
        return add(c, -1);
    }
    
    
    /**
     * Adds the specified component to this container 
     * at the given position.
     *
     * @param c the component to be added
     * @param index the position at which to insert the component,
     *      or -1 to insert the component at the end.
     * @return the component argument
     */
    public Component add(Component c, int index) {
        super.add(c, index);

        // store the new object on display and add it to the
        // visualization
        if ((c != null) && (c instanceof Fragile)) {
            Fragile f = (Fragile)c;
            f.addMalformedDataListener(this);
        }
    
        // update the visualization
        revalidate();
                
        // return the component argument
        return c;
    }
    
    
    /**
     * Adds the specified component to the end of this container. 
     *
     * Also notifies the layout manager 
     * to add the component to this container's layout 
     * using the given constraints object.
     *
     * @param c the component to be added
     * @param constraints an object expressing layout constraints 
     *      for this component
     */
    public void add(Component c, Object constraints) {
        super.add(c, constraints);

        // store the new object on display and add it to the
        // visualization
        if ((c != null) && (c instanceof Fragile)) {
            Fragile f = (Fragile)c;
            f.addMalformedDataListener(this);
        }
    
        // update the visualization
        revalidate();
    }
    
    
    /**
     * Adds the specified component at the given position. 
     * 
     * Also notifies the layout manager 
     * to add the component to this container's layout 
     * using the specified constraints object.
     *
     * @param c the component to be added
     * @param constraints an object expressing layout constraints 
     *      for this component
     * @param index the position at which to insert the component,
     *      or -1 to insert the component at the end.
     */
    public void add(Component c, Object constraints, int index) {
        super.add(c, constraints, index);

        // store the new object on display and add it to the
        // visualization
        if ((c != null) && (c instanceof Fragile)) {
            Fragile f = (Fragile)c;
            f.addMalformedDataListener(this);
        }
    
        // update the visualization
        revalidate();
    }
    
    
    /**
     * Adds the given component to this container. 
     *
     * It is strongly advised to use the 1.1 method, 
     * <code>{@link #add(Component, Object)
     *               add(Component, Object)}</code>,
     * in place of this method.
     *
     * @param name the name of the component to be added
     * @param c the component to be added
     * @return the component argument
     */
    public Component add(String name, Component c) {
        super.add(name, c);

        // store the new object on display and add it to the
        // visualization
        if ((c != null) && (c instanceof Fragile)) {
            Fragile f = (Fragile)c;
            f.addMalformedDataListener(this);
        }
        
        // update the visualization
        revalidate();
        
        // return the component argument
        return c;
    }
    
    
    /**
     * Add the given object to this <code>DisplayPanel</code>
     * at the next available position
     * after applying the transformation of the method
     * <code>makeComponent</code>.
     *
     * @param  object the object to be transformed and added to the panel
     * @return the component obtained by transforming the object
     * @since 2.2
     */
    public Component addObject(Object object) {
        Component component = makeComponent(object);
        
        if (component != null)
            add(component);
        
        return component;
    }
    
    
    /**
     * Add the given object to this <code>DisplayPanel</code>
     * at the given index position
     * after applying the transformation of the method
     * <code>makeComponent</code>.
     *
     * @param  object the object to be transformed and added to the panel
     * @param  index the position at which to add the component
     * @return the component obtained by transforming the object
     * @since 2.2
     */
    public Component addObject(Object object, int index) {
        Component component = makeComponent(object);
        
        if (component != null)
            add(component, index);
        
        return component;
    }
    
    
    /**
     * Add the given object to this <code>DisplayPanel</code>
     * with the specified constaints
     * at the next appropriate position
     * after applying the transformation of the method
     * <code>makeComponent</code>.
     *
     * @param  object the object to be transformed and added to the panel
     * @param  constraints an object expressing layout constraints for the component
     * @return the component obtained by transforming the object
     * @since 2.2
     */
    public Component addObject(Object object, Object constraints) {
        Component component = makeComponent(object);
        
        if (component != null)
            add(component, constraints);
        
        return component;
    }
    
    
    /**
     * Add the given object to this <code>DisplayPanel</code>
     * at the given index position
     * after applying the transformation of the method
     * <code>makeComponent</code>.
     *
     * @param  object the object to be transformed and added to the panel
     * @param  constraints an object expressing layout constraints for the component
     * @param  index the position at which to add the component
     * @return the component obtained by transforming the object
     * @since 2.2
     */
    public Component addObject(Object object, Object constraints, int index) {
        Component component = makeComponent(object);
        
        if (component != null)
            add(component, constraints, index);
        
        return component;
    }
    
    
    /**
     * By default, this method will return the
     * <code>Component</code> constructed using
     * <code>ComponentFactory.makeComponent</code>.
     *
     * @param  object the object to be transformed
     * @return the component obtained by transforming the object
     * @since 2.2
     */
    public Component makeComponent(Object o) {
        return ComponentFactory.makeComponent(o);
    }
    
    
    /**
     * Removes the given component from this container.
     *
     * @param c the component to be removed
     */
    public void remove(Component c) {
        super.remove(c);

        // remove the listener for this object if necessary
        if ((c != null) && (c instanceof Fragile)) {
            Fragile f = (Fragile)c;
            f.removeMalformedDataListener(this);
        }

        // update the visualization
        revalidate();
    }
    
    
    /**
     * Removes the component at the given index 
     * from this container.
     *
     * @param index the index of the component to be removed
     */
    public void remove(int index) {
        try {
            Component c = getComponent(index);
            
            // remove the listener for this component if necessary
            if ((c != null) && (c instanceof Fragile)) {
                Fragile f = (Fragile)c;
                f.removeMalformedDataListener(this);
            }
        } catch (Exception ex) {}
        finally {
            super.remove(index);

            // update the visualization
            revalidate();
        }
    }
    
    
    /**
     * Removes all of the components from this container.
     */
    public void removeAll() {
        while (getComponentCount() > 0)
            remove(0);
    }
    
    
    /**
     * <p>Sets the preferred size for all <code>JComponent</code>s 
     * in this panel to the same preferred size.</p>
     *
     * <p>The preferred size that is used is created from the
     * widest preferred width and tallest preferred height
     * among the contained components.</p>
     *
     * @since 2.0
     */
    public void uniformizeSize() {
        Component[] list = getComponents();
        int length = list.length;
        
        JComponent component = null;
        
        // find the enclosing preferred size
        Dimension d = new Dimension();
        
        for (int i = 0; i < length; i++) {
            if (list[i] instanceof JComponent) {
                component = (JComponent) list[i];
                
                d = DimensionUtilities.max
                    (d, component.getPreferredSize());
            }
        }
        
        // set the preferred size for all components
        for (int i = 0; i < length; i++) {
            if (list[i] instanceof JComponent) {
                component = (JComponent) list[i];
                component.setPreferredSize(d);
            }
        }
    }
    
    
    /**
     * <p>Sets the preferred width for all <code>JComponent</code>s 
     * in this panel to the same width.</p>
     *
     * <p>The preferred width that is used is created from the
     * widest preferred width among the contained components.</p>
     *
     * @since 2.3
     */
    public void uniformizeWidth() {
        Component[] list = getComponents();
        int length = list.length;
        
        JComponent component = null;
        Dimension d = null;
        int maxWidth = 0;
        
        // find the maximum preferred width
        for (int i = 0; i < length; i++) {
            if (list[i] instanceof JComponent) {
                component = (JComponent) list[i];
                d = component.getPreferredSize();
                maxWidth = Math.max(maxWidth, (int) d.getWidth());
            }
        }
        
        // set the preferred width for all components
        for (int i = 0; i < length; i++) {
            if (list[i] instanceof JComponent) {
                component = (JComponent) list[i];
                d = component.getPreferredSize();
                d.setSize(maxWidth, (int) d.getHeight());
                component.setPreferredSize(d);
            }
        }
    }
    
    
    /**
     * <p>Sets the preferred height for all <code>JComponent</code>s 
     * in this panel to the same height.</p>
     *
     * <p>The preferred height that is used is created from the
     * tallest preferred height among the contained components.</p>
     *
     * @since 2.3
     */
    public void uniformizeHeight() {
        Component[] list = getComponents();
        int length = list.length;
        
        JComponent component = null;
        Dimension d = null;
        int maxHeight = 0;
        
        // find the maximum preferred height
        for (int i = 0; i < length; i++) {
            if (list[i] instanceof JComponent) {
                component = (JComponent) list[i];
                d = component.getPreferredSize();
                maxHeight = Math.max(maxHeight, (int) d.getHeight());
            }
        }
        
        // set the preferred height for all components
        for (int i = 0; i < length; i++) {
            if (list[i] instanceof JComponent) {
                component = (JComponent) list[i];
                d = component.getPreferredSize();
                d.setSize((int) d.getWidth(), maxHeight);
                component.setPreferredSize(d);
            }
        }
    }
    
    
    /**
     * Sets the identifier for the CODEC to be used 
     * to combine multiple child view states 
     * into an encoded <code>String</code>.
     *
     * If the given identifier is <code>null</code>,
     * the CODEC is not changed.
     *
     * @param codecID identifier of the codec to be used
     */
    public void setCodec(String codecID) {
        String oldCodec = getCodec();
    
        if (codecID != null)
            codec = codecID;

        // if codec has changed
        if (getCodec() != oldCodec) {
        
            // notify listeners of property change
            firePropertyChange(
                CODEC,
                oldCodec,
                getCodec());
        }                
    }
    
    
    /**
     * Returns the identifier for the current CODEC to be used
     * to combine multiple child view states 
     * into an encoded <code>String</code>.
     */
    public String getCodec() {
        return codec;
    }
    
    
    /**
     * <p>Frame this panel in a JPTFrame
     * and open the frame;
     * return the frame constructed.</p>
     *
     * @return the frame constructed
     * @since 2.3.3
     */
    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.3
     */
    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.3
     */
    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.3
     */
    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.4
     */
    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.3
     */
    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.3
     */
    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.3
     */
    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.3
     */
    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.3
     */
    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.3
     */
    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.3
     */
    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.3
     */
    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.3
     */
    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.3
     */
    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.3
     */
    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);
    }
    
}
