/*
 * @(#)JPTFrame.java    2.3.4   6 February 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.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/**
 * <p>An extended <code>{@link JFrame JFrame}</code> class 
 * that allows more flexible window closing operations 
 * and automatically resizes to ensure that the contents
 * of the frame are the desired size regardless of frame
 * insets.</p>
 *
 * <p>This class adds two new default close operations
 * to the <code>JFrame</code> class:</p>
 *
 * <ul>
 *    <li><code>EXIT_ON_CLOSE</code>: exits the 
 *        Java Virtual Machine when this frame is disposed</li>
 *    <li><code>EXIT_ON_CLOSE_IF_LAST</code>: exits the 
 *        Java Virtual Machine when the last 
 *        instance of <code>JPTFrame</code> is disposed</li>
 * </ul>
 *
 * <p>The static convenience methods <code>createQuickJPTFrame</code>
 * may be used to create and display a <code>JPTFrame</code> that is
 * automatically packed and placed in a desired location.</p>
 *
 * <p>In version 2.3.2, the various <code>createQuickJPTFrame</code>
 * methods were changed to accept a general <code>Object</code> rather
 * than requiring a <code>Component</code>.  The object is converted
 * into a component using the tools of <code>ComponentFactory</code>.</p>
 *
 * <p>In version 2.3.4, methods named <code>frame</code> were introduced
 * as less verbose versions of <code>createQuickJPTFrame</code>.  At the
 * same time, these methods make the object being framed the first
 * argument which is, in retrospect, more a natural design.</p>
 *
 * <p>The same static <code>frame</code> methods have also been introduced into
 * the class <code>JPF</code> as convenience methods for experimentation.</p>
 *
 * <p>The class <code>DisplayPanel</code> has a parallel set of member
 * <code>frame</code> methods that permit a <code>DisplayPanel</code> to
 * frame itself.  We call this ability <i>self actualization</i>.</p>
 *
 * @author  Jeff Raab
 * @author  Richard Rasala
 * @version 2.3.4
 * @since   1.0
 */
public class JPTFrame 
    extends JFrame 
    implements JPTConstants 
{
    /** 
     * Value designating that the running application 
     * should be exited when this frame is disposed.
     *
     * This constant, <code>EXIT_ON_CLOSE</code>, is mentioned in
     * the API documentation for JDK v1.3, but is not implemented
     * in any version of the JDK.
     *
     * @see #setDefaultCloseOperation(int)
     */
    public static final int EXIT_ON_CLOSE = 
        DISPOSE_ON_CLOSE + HIDE_ON_CLOSE + DO_NOTHING_ON_CLOSE;

    // The above definition ensures that the constant is distinct
    // from the related constants defined in the JDK
    
    
    /**
     * Value designating that the running application 
     * should be exited when the last <code>JPTFrame</code>
     * is disposed.
     *
     * @see #setDefaultCloseOperation(int)
     */
    public static final int EXIT_ON_CLOSE_IF_LAST = 
        EXIT_ON_CLOSE + EXIT_ON_CLOSE;

    // The above definition ensures that the constant is distinct
    // from the related constants defined in the JDK
    // and the constant defined above
    
    
    /** 
     * The default window closing operation 
     * for a <code>JPTFrame</code>. 
     *
     * @see #setDefaultCloseOperation(int)
     */
    public static final int DEFAULT_CLOSE_OPERATION = 
        EXIT_ON_CLOSE_IF_LAST;
    
    
    /**
     * The default insets for the frame in the screen window,
     * namely, 50, 50, 50, 50.
     */
    private static final Insets INSETS = new Insets(50, 50, 50, 50);
    
    
    /** Shared counter of <code>JPTFrame</code>s in existence. */
    protected static XInt counter = new XInt(0);
    
    
    /** 
     * The window closing operation for this frame. 
     *
     * @see #setDefaultCloseOperation(int)
     */
    protected int closeOperation = DEFAULT_CLOSE_OPERATION;
    
    
    /**
     * The current insets to use in <code>setLocation</code> calls
     * that do not provide explicit insets.
     */
    protected Insets screen_insets = (Insets) INSETS.clone();
    
    
    //////////////////
    // Constructors //
    //////////////////
    
    /**
     * Constructs a frame with no title.
     *
     * @see #JPTFrame(String)
     */
    public JPTFrame() {
        this("");
    }
    
    
    /**
     * Constructs a frame with the given title.
     *
     * @param title the title for this frame
     * @see #JPTFrame()
     */
    public JPTFrame(String title) {
        super(title);

        // disable the superclass window closing behavior
        super.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
        
        // initialize default local window closing behavior
        setDefaultCloseOperation(DEFAULT_CLOSE_OPERATION);
        
        // establish the mandatory window listener 
        // that implements automatic window closing behavior
        installWindowAdapter();

        // increment shared counter
        synchronized(counter) {
            counter.setValue(counter.getValue() + 1);
        }
    }
    
    
    ////////////////
    // Public API //
    ////////////////
    
    
    /** 
     * Centers the frame on the user screen relative to the current
     * screen insets.
     *
     * @see #setLocation(int, int)
     */
    public void center() {
        setLocation(CENTER);
    }
    
    
    /**
     * <p>Sets the location of this frame on the user screen to the
     * location representing the logical screen location of the
     * given compass direction;
     * uses the current screen insets saved with this frame.</p>
     *
     * <p>For best results, this method should be called after the
     * <code>JPTFrame</code> is made visible.  The reason for this
     * is that, in making the frame visible, Java calls the method
     * <code>addNotify()</code> and this method has been overridden
     * in this class to adjust the frame size to take into account
     * the insets of the native window in order to get the true size.</p>
     *
     * <p>The methods <code>createQuickJPTFrame</code> are designed
     * to set the location after the frame is made visible.</p>
     *
     * <p>This method does not anchor this frame in the given logical
     * position; calling this method leads to a call of the standard
     * <code>setLocation(int, int)</code> method.</p>
     *
     * <p>The location is given by one of the following constants in
     * the interface <code>SwingConstants</code> that are included
     * for convenience in the interface <code>JPTConstants</code> as
     * well:</p>
     *
     * <ul>
     *    <li><code>CENTER</code></li>
     *    <li><code>NORTH</code></li>
     *    <li><code>NORTH_EAST</code></li>
     *    <li><code>EAST</code></li>
     *    <li><code>SOUTH_EAST</code></li>
     *    <li><code>SOUTH</code></li>
     *    <li><code>SOUTH_WEST</code></li>
     *    <li><code>WEST</code></li>
     *    <li><code>NORTH_WEST</code></li>
     * </ul>
     *
     * <p>The <code>DEFAULT</code> location is the <code>CENTER</code>
     * location.</p>
     *
     * <p>If the given compass direction is not valid, the location
     * is set to the <code>DEFAULT</code> location.  This is a change
     * in how to handle an erroneous parameter that was introduced in
     * version 2.3.</p>
     *
     * @param location the compass direction representing the desired location
     * @see #setLocation(int, Insets)
     * @see #center()
     * @see Component#setLocation(int, int)
     * @see JPTConstants#DEFAULT
     * @since 1.1
     */
    public void setLocation(int location) {
        setLocation(location, screen_insets);
    }
    
    
    /**
     * <p>Sets the location of this frame on the user screen to the
     * location representing the logical screen location of the
     * given compass direction;
     * the given insets bound the frame away from the borders of the
     * screen and also replace the current screen insets for future
     * calls of <code>setLocation(int)</code> on the frame.</p>
     *
     * <p>For best results, this method should be called after the
     * <code>JPTFrame</code> is made visible.  The reason for this
     * is that, in making the frame visible, Java calls the method
     * <code>addNotify()</code> and this method has been overridden
     * in this class to adjust the frame size to take into account
     * the insets of the native window in order to get the true size.</p>
     *
     * <p>The methods <code>createQuickJPTFrame</code> are designed
     * to set the location after the frame is made visible.</p>
     *
     * <p>This method does not anchor this frame in the given logical
     * position; calling this method leads to a call of the standard
     * <code>setLocation(int, int)</code> method.</p>
     *
     * <p>The location is given by one of the following constants in
     * the interface <code>SwingConstants</code> that are included
     * for convenience in the interface <code>JPTConstants</code> as
     * well:</p>
     *
     * <ul>
     *    <li><code>CENTER</code></li>
     *    <li><code>NORTH</code></li>
     *    <li><code>NORTH_EAST</code></li>
     *    <li><code>EAST</code></li>
     *    <li><code>SOUTH_EAST</code></li>
     *    <li><code>SOUTH</code></li>
     *    <li><code>SOUTH_WEST</code></li>
     *    <li><code>WEST</code></li>
     *    <li><code>NORTH_WEST</code></li>
     * </ul>
     *
     * <p>The <code>DEFAULT</code> location is the <code>CENTER</code>
     * location.</p>
     *
     * <p>If the given compass direction is not valid, the location
     * is set to the <code>DEFAULT</code> location.  This is a change
     * in how to handle an erroneous parameter that was introduced in
     * version 2.3.</p>
     *
     * <p>The insets provide a margin from the edge of the screen so
     * the frame will avoid being placed underneath OS objects such
     * as menu bars or program selection widgets.</p>
     *
     * <p>If the given insets is <code>null</code>, then the current
     * screen insets revert to the default of 50, 50, 50, 50.</p>
     *
     * @param location the compass direction representing the desired location
     * @param insets    the screen insets to use to adjust the location
     * @see #setLocation(int)
     * @see #center()
     * @see Component#setLocation(int, int)
     * @see JPTConstants#DEFAULT
     * @since 1.1
     */
    public void setLocation(int location, Insets insets) {
        // check for null insets
        if (insets == null)
            insets = INSETS;
        
        // save a copy of the insets
        screen_insets = (Insets) insets.clone();
        
        // check the screen insets for validity
        if (screen_insets.left < 0)
            screen_insets.left = 0;
        
        if (screen_insets.right < 0)
            screen_insets.right = 0;
        
        if (screen_insets.top < 0)
            screen_insets.top = 0;
        
        if (screen_insets.bottom < 0)
            screen_insets.bottom = 0;
        
        // calculate the location based on the alignment parameter,
        // the screen insets, and the screen size
        
        Dimension size = Toolkit.getDefaultToolkit().getScreenSize();
        Point p = new Point();

        // calculate extra space available on the screen
        int dx = size.width  - screen_insets.left - screen_insets.right  - getWidth();
        int dy = size.height - screen_insets.top  - screen_insets.bottom - getHeight();
        
        if (dx < 0) dx = 0;
        if (dy < 0) dy = 0;
        
        // calculate possible adjustments to the location
        int flushWest  = screen_insets.left;
        int centerWE   = screen_insets.left + (dx/2);
        int flushEast  = screen_insets.left + dx;
        
        int flushNorth = screen_insets.top;
        int centerNS   = screen_insets.top  + (dy/2);
        int flushSouth = screen_insets.top  + dy;
        
        // set adjusted location
        switch (location) {
            case NORTH:
                p.setLocation(centerWE,  flushNorth);
                break;

            case NORTH_EAST:
                p.setLocation(flushEast, flushNorth);
                break;

            case EAST:
                p.setLocation(flushEast, centerNS);
                break;

            case SOUTH_EAST:  
                p.setLocation(flushEast, flushSouth);
                break;

            case SOUTH:  
                p.setLocation(centerWE,  flushSouth);
                break;

            case SOUTH_WEST: 
                p.setLocation(flushWest, flushSouth);
                break;

            case WEST:
                p.setLocation(flushWest, centerNS);
                break;

            case NORTH_WEST:
                p.setLocation(flushWest, flushNorth);
                break;

            default:
                p.setLocation(centerWE,  centerNS);
                break;
        }

        // set the onscreen location
        setLocation(p.x, p.y);
    }
    
    
    /**
     * Returns a copy of the current screen insets used in calls to
     * <code>setLocation(int)</code>.
     *
     * @return the current screen insets
     * @see #setLocation(int)
     * @since 2.3
     */
    public Insets getScreenInsets() {
        return (Insets) screen_insets.clone();
    }
    
    
    /**
     * Resizes the frame upon creation of its peer window to make its contents
     * the proper size regardless of platform specific insets size.
     */
    public void addNotify() {
        super.addNotify();
        
        setSize(
            getSize().width + 
                getInsets().left + 
                getInsets().right,
            getSize().height + 
                getInsets().top + 
                getInsets().bottom);
    }
    
    
    /**
     * <p>Sets the window closing operation to the operation specified
     * by the given value.</p>
     *
     * <p>If the given value is not valid, the current window closing
     * operation is not changed.</p>
     *
     * @param operation the value of the operation to perform 
     *        when the window is closed
     * @see #getDefaultCloseOperation()
     * @see #DO_NOTHING_ON_CLOSE
     * @see #HIDE_ON_CLOSE
     * @see #DISPOSE_ON_CLOSE
     * @see #EXIT_ON_CLOSE
     * @see #EXIT_ON_CLOSE_IF_LAST
     * @see #DEFAULT
     */
    public void setDefaultCloseOperation(int operation) {
        switch (operation) {
            case DEFAULT:
                closeOperation = DEFAULT_CLOSE_OPERATION;
                break;

            case DO_NOTHING_ON_CLOSE:
            case HIDE_ON_CLOSE:
            case DISPOSE_ON_CLOSE:
            case EXIT_ON_CLOSE_IF_LAST:
            case EXIT_ON_CLOSE:
                closeOperation = operation;
                break;

            default:
                return;
        }
    }
    
    
    /**
     * Returns the window close operation for this frame.
     *
     * @see #setDefaultCloseOperation(int)
     * @see #DO_NOTHING_ON_CLOSE
     * @see #HIDE_ON_CLOSE
     * @see #DISPOSE_ON_CLOSE
     * @see #EXIT_ON_CLOSE
     * @see #EXIT_ON_CLOSE_IF_LAST
     */
    public int getDefaultCloseOperation() {
        return closeOperation;
    }
    
    
    /**
     * Returns the number of <code>JPTFrame</code>s in existence.
     *
     * @see #EXIT_ON_CLOSE_IF_LAST
     */
    public static int getJPTFrameCount() {
        synchronized(counter) {
            return counter.getValue();
        }
    }
    
    
    /**
     * Disposes of the resources used by this frame and decrements
     * the number of <code>JPTFrame</code>s in existence.
     */
    public void dispose() {

        // decrement shared counter
        synchronized(counter) {
            counter.setValue(counter.getValue() - 1);

            super.dispose();
        }
    }
    
    
    ////////////////////
    // Static methods //
    ////////////////////
    
    
    /**
     * <p>Returns a new <code>JPTFrame</code> 
     * with the given title and content pane 
     * that is packed and centered on the screen.</p>
     *
     * <p>If the given object is <code>null</code>,
     * no contents are added to the frame.</p>
     *
     * <p>The layout of the content pane is set
     * to <code>CenterLayout</code>.</p>
     *
     * @param title  the title for the frame
     * @param object the object to frame
     */
    public static JPTFrame createQuickJPTFrame(
        String title,
        Object object)
    {
        return createQuickJPTFrame
            (title, object, new CenterLayout(), DEFAULT, null);
    }
    
    
    /**
     * <p>Returns a new <code>JPTFrame</code> 
     * with the given title and content pane 
     * that is packed and located on the screen
     * in the given screen location.</p>
     *
     * <p>If the given object is <code>null</code>,
     * no contents are added to the frame.</p>
     *
     * <p>The layout of the content pane is set
     * to <code>CenterLayout</code>.</p>
     *
     * <p>The location is given by one of the following
     * constants in the interface <code>SwingConstants</code>
     * that are included for convenience in the interface
     * <code>JPTConstants</code> as well:</p>
     *
     * <ul>
     *    <li><code>CENTER</code></li>
     *    <li><code>NORTH</code></li>
     *    <li><code>NORTH_EAST</code></li>
     *    <li><code>EAST</code></li>
     *    <li><code>SOUTH_EAST</code></li>
     *    <li><code>SOUTH</code></li>
     *    <li><code>SOUTH_WEST</code></li>
     *    <li><code>WEST</code></li>
     *    <li><code>NORTH_WEST</code></li>
     * </ul>
     *
     * @param title    the title for the frame
     * @param object   the object to frame
     * @param location the compass direction representing the desired location
     */
    public static JPTFrame createQuickJPTFrame(
        String title,
        Object object,
        int    location) 
    {
        return createQuickJPTFrame
            (title, object, new CenterLayout(), location, null);
    }
    
    
    /**
     * <p>Returns a new <code>JPTFrame</code> 
     * with the given title and content pane
     * that is packed and centered on the screen;
     * the given insets replace the current insets for
     * future calls of <code>setLocation(int)</code>
     * on the frame.</p>
     *
     * <p>If the given object is <code>null</code>,
     * no contents are added to the frame.</p>
     *
     * <p>The layout of the content pane is set
     * to <code>CenterLayout</code>.</p>
     *
     * <p>The insets provide a margin from the edge of
     * the screen so the frame will avoid being placed
     * underneath OS objects such as menu bars or
     * program selection widgets.</p>
     *
     * <p>If the given insets is <code>null</code>,
     * then the insets are set to the default of
     * 50, 50, 50, 50.</p>
     *
     * @param title    the title for the frame
     * @param object   the object to frame
     * @param insets   the screen insets to use to adjust the location
     */
    public static JPTFrame createQuickJPTFrame(
        String title,
        Object object,
        Insets insets) 
    {
        return createQuickJPTFrame
            (title, object, new CenterLayout(), DEFAULT, insets);
    }
    
    
    /**
     * <p>Returns a new <code>JPTFrame</code> 
     * with the given title and content pane
     * that is packed and located on the screen
     * in the given screen location;
     * the given insets bound the frame away from
     * the borders of the screen and also replace
     * the current insets for future calls of
     * <code>setLocation(int)</code> on the frame.</p>
     *
     * <p>If the given object is <code>null</code>,
     * no contents are added to the frame.</p>
     *
     * <p>The layout of the content pane is set
     * to <code>CenterLayout</code>.</p>
     *
     * <p>The location is given by one of the following
     * constants in the interface <code>SwingConstants</code>
     * that are included for convenience in the interface
     * <code>JPTConstants</code> as well:</p>
     *
     * <ul>
     *    <li><code>CENTER</code></li>
     *    <li><code>NORTH</code></li>
     *    <li><code>NORTH_EAST</code></li>
     *    <li><code>EAST</code></li>
     *    <li><code>SOUTH_EAST</code></li>
     *    <li><code>SOUTH</code></li>
     *    <li><code>SOUTH_WEST</code></li>
     *    <li><code>WEST</code></li>
     *    <li><code>NORTH_WEST</code></li>
     * </ul>
     *
     * <p>The insets provide a margin from the edge of
     * the screen so the frame will avoid being placed
     * underneath OS objects such as menu bars or
     * program selection widgets.</p>
     *
     * <p>If the given insets is <code>null</code>,
     * then the insets are set to the default of
     * 50, 50, 50, 50.</p>
     *
     * @param title    the title for the frame
     * @param object   the object to frame
     * @param location the compass direction representing the desired location
     * @param insets   the screen insets to use to adjust the location
     */
    public static JPTFrame createQuickJPTFrame(
        String title,
        Object object,
        int    location,
        Insets insets) 
    {
        return createQuickJPTFrame
            (title, object, new CenterLayout(), location, insets);
    }
    
    
    /**
     * <p>Returns a new <code>JPTFrame</code> 
     * with the given title, content pane,
     * and layout manager
     * that is packed and centered on the screen.</p>
     *
     * <p>If the given object is <code>null</code>,
     * no contents are added to the frame.</p>
     *
     * <p>If the given layout manager is <code>null</code>,
     * the default layout for the content pane of a
     * <code>JFrame</code> is used.<p>
     *
     * @param title  the title for the frame
     * @param object the object to frame
     * @param layout the layout manager for the content pane
     */
    public static JPTFrame createQuickJPTFrame(
        String        title,
        Object        object,
        LayoutManager layout) 
    {
        return createQuickJPTFrame
            (title, object, layout, DEFAULT, null);
    }
    
    
    /**
     * <p>Returns a new <code>JPTFrame</code> 
     * with the given title, content pane,
     * and layout manager,
     * that is packed and located on the screen
     * in the given screen location.</p>
     *
     * <p>If the given object is <code>null</code>,
     * no contents are added to the frame.</p>
     *
     * <p>If the given layout manager is <code>null</code>,
     * the default layout for the content pane of a
     * <code>JFrame</code> is used.<p>
     *
     * <p>The location is given by one of the following
     * constants in the interface <code>SwingConstants</code>
     * that are included for convenience in the interface
     * <code>JPTConstants</code> as well:</p>
     *
     * <ul>
     *    <li><code>CENTER</code></li>
     *    <li><code>NORTH</code></li>
     *    <li><code>NORTH_EAST</code></li>
     *    <li><code>EAST</code></li>
     *    <li><code>SOUTH_EAST</code></li>
     *    <li><code>SOUTH</code></li>
     *    <li><code>SOUTH_WEST</code></li>
     *    <li><code>WEST</code></li>
     *    <li><code>NORTH_WEST</code></li>
     * </ul>
     *
     * @param title    the title for the frame
     * @param object   the object to frame
     * @param layout   the layout manager for the content pane
     * @param location the compass direction representing the desired location
     */
    public static JPTFrame createQuickJPTFrame(
        String        title,
        Object        object,
        LayoutManager layout,
        int           location) 
    {
        return createQuickJPTFrame
            (title, object, layout, location, null);
    }
    
    
    /**
     * <p>Returns a new <code>JPTFrame</code> 
     * with the given title, content pane,
     * and layout manager,
     * that is packed and centered on the screen;
     * the given insets replace the current insets for
     * future calls of <code>setLocation(int)</code>
     * on the frame.</p>
     *
     * <p>If the given object is <code>null</code>,
     * no contents are added to the frame.</p>
     *
     * <p>If the given layout manager is <code>null</code>,
     * the default layout for the content pane of a
     * <code>JFrame</code> is used.<p>
     *
     * <p>The insets provide a margin from the edge of
     * the screen so the frame will avoid being placed
     * underneath OS objects such as menu bars or
     * program selection widgets.</p>
     *
     * <p>If the given insets is <code>null</code>,
     * then the insets are set to the default of
     * 50, 50, 50, 50.</p>
     *
     * @param title    the title for the frame
     * @param object   the object to frame
     * @param layout   the layout manager for the content pane
     * @param insets   the screen insets to use to adjust the location
     */
    public static JPTFrame createQuickJPTFrame(
        String        title,
        Object        object,
        LayoutManager layout,
        Insets        insets) 
    {
        return createQuickJPTFrame
            (title, object, layout, DEFAULT, insets);
    }
    
    
    /**
     * <p>Returns a new <code>JPTFrame</code> 
     * with the given title, content pane,
     * and layout manager,
     * that is packed and located on the screen
     * in the given screen location;
     * the given insets bound the frame away from
     * the borders of the screen and also replace
     * the current insets for future calls of
     * <code>setLocation(int)</code> on the frame.</p>
     *
     * <p>If the given object is <code>null</code>,
     * no contents are added to the frame.</p>
     *
     * <p>If the given layout manager is <code>null</code>,
     * the default layout for the content pane of a
     * <code>JFrame</code> is used.<p>
     *
     * <p>The location is given by one of the following
     * constants in the interface <code>SwingConstants</code>
     * that are included for convenience in the interface
     * <code>JPTConstants</code> as well:</p>
     *
     * <ul>
     *    <li><code>CENTER</code></li>
     *    <li><code>NORTH</code></li>
     *    <li><code>NORTH_EAST</code></li>
     *    <li><code>EAST</code></li>
     *    <li><code>SOUTH_EAST</code></li>
     *    <li><code>SOUTH</code></li>
     *    <li><code>SOUTH_WEST</code></li>
     *    <li><code>WEST</code></li>
     *    <li><code>NORTH_WEST</code></li>
     * </ul>
     *
     * <p>The insets provide a margin from the edge of
     * the screen so the frame will avoid being placed
     * underneath OS objects such as menu bars or
     * program selection widgets.</p>
     *
     * <p>If the given insets is <code>null</code>,
     * then the insets are set to the default of
     * 50, 50, 50, 50.</p>
     *
     * @param title    the title for the frame
     * @param object   the object to frame
     * @param layout   the layout manager for the content pane
     * @param location the compass direction representing the desired location
     * @param insets   the screen insets to use to adjust the location
     */
    public static JPTFrame createQuickJPTFrame(
        String        title,
        Object        object,
        LayoutManager layout,
        int           location,
        Insets        insets) 
    {
        // build the component
        Component contents = ComponentFactory.makeComponent(object);
        
        // build the frame
        JPTFrame f = new JPTFrame(title == null ? "" : title);

        // set the layout of the content pane if appropriate
        if (layout != null)
            f.getContentPane().setLayout(layout);

        // add the given component if appropriate
        if (contents != null)
            f.getContentPane().add(contents);
        
        // pack, show the frame, and relocate
        f.pack();
        f.setVisible(true);
        f.setLocation(location, insets);
        
        // return the created frame
        return f;
    }
    
    
    /**
     * <p>Returns a new <code>JPTFrame</code> 
     * with the given content pane and blank title
     * that is packed and centered on the screen.</p>
     *
     * <p>If the given object is <code>null</code>,
     * no contents are added to the frame.</p>
     *
     * <p>The layout of the content pane is set
     * to <code>CenterLayout</code>.</p>
     *
     * <p>The method call <code>frame(object)</code>
     * is the same as the method call
     * <code>createQuickJPTFrame("",object)</code>.</p>
     *
     * @param object the object to frame
     */
    public static JPTFrame frame(Object object)
    {
        return createQuickJPTFrame
            ("", object, new CenterLayout(), DEFAULT, null);
    }
    
    
    /**
     * <p>Returns a new <code>JPTFrame</code> 
     * with the given content pane and blank title
     * that is packed and located on the screen
     * in the given screen location.</p>
     *
     * <p>If the given object is <code>null</code>,
     * no contents are added to the frame.</p>
     *
     * <p>The layout of the content pane is set
     * to <code>CenterLayout</code>.</p>
     *
     * <p>The location is given by one of the following
     * constants in the interface <code>SwingConstants</code>
     * that are included for convenience in the interface
     * <code>JPTConstants</code> as well:</p>
     *
     * <ul>
     *    <li><code>CENTER</code></li>
     *    <li><code>NORTH</code></li>
     *    <li><code>NORTH_EAST</code></li>
     *    <li><code>EAST</code></li>
     *    <li><code>SOUTH_EAST</code></li>
     *    <li><code>SOUTH</code></li>
     *    <li><code>SOUTH_WEST</code></li>
     *    <li><code>WEST</code></li>
     *    <li><code>NORTH_WEST</code></li>
     * </ul>
     *
     * <p>The method call <code>frame(object,location)</code>
     * is the same as the method call
     * <code>createQuickJPTFrame("",object,location)</code>.</p>
     *
     * @param object   the object to frame
     * @param location the compass direction representing the desired location
     */
    public static JPTFrame frame(
        Object object,
        int    location) 
    {
        return createQuickJPTFrame
            ("", object, new CenterLayout(), location, null);
    }
    
    
    /**
     * <p>Returns a new <code>JPTFrame</code> 
     * with the given content pane and title
     * that is packed and centered on the screen.</p>
     *
     * <p>If the given object is <code>null</code>,
     * no contents are added to the frame.</p>
     *
     * <p>The layout of the content pane is set
     * to <code>CenterLayout</code>.</p>
     *
     * <p>This method is the same as
     * <code>createQuickJPTFrame(String,Object)</code>
     * but places the <code>Object</code> argument first
     * and is less verbose.</p>
     *
     * @param object the object to frame
     * @param title  the title for the frame
     */
    public static JPTFrame frame(
        Object object,
        String title)
    {
        return createQuickJPTFrame
            (title, object, new CenterLayout(), DEFAULT, null);
    }
    
    
    /**
     * <p>Returns a new <code>JPTFrame</code> 
     * with the given content pane and title
     * that is packed and located on the screen
     * in the given screen location.</p>
     *
     * <p>If the given object is <code>null</code>,
     * no contents are added to the frame.</p>
     *
     * <p>The layout of the content pane is set
     * to <code>CenterLayout</code>.</p>
     *
     * <p>The location is given by one of the following
     * constants in the interface <code>SwingConstants</code>
     * that are included for convenience in the interface
     * <code>JPTConstants</code> as well:</p>
     *
     * <ul>
     *    <li><code>CENTER</code></li>
     *    <li><code>NORTH</code></li>
     *    <li><code>NORTH_EAST</code></li>
     *    <li><code>EAST</code></li>
     *    <li><code>SOUTH_EAST</code></li>
     *    <li><code>SOUTH</code></li>
     *    <li><code>SOUTH_WEST</code></li>
     *    <li><code>WEST</code></li>
     *    <li><code>NORTH_WEST</code></li>
     * </ul>
     *
     * <p>This method is the same as
     * <code>createQuickJPTFrame(String,Object,int)</code>
     * but places the <code>Object</code> argument first
     * and is less verbose.</p>
     *
     * @param object   the object to frame
     * @param title    the title for the frame
     * @param location the compass direction representing the desired location
     */
    public static JPTFrame frame(
        Object object,
        String title,
        int    location) 
    {
        return createQuickJPTFrame
            (title, object, new CenterLayout(), location, null);
    }
    
    
    /**
     * <p>Returns a new <code>JPTFrame</code> 
     * with the given content pane and title
     * that is packed and centered on the screen;
     * the given insets replace the current insets for
     * future calls of <code>setLocation(int)</code>
     * on the frame.</p>
     *
     * <p>If the given object is <code>null</code>,
     * no contents are added to the frame.</p>
     *
     * <p>The layout of the content pane is set
     * to <code>CenterLayout</code>.</p>
     *
     * <p>The insets provide a margin from the edge of
     * the screen so the frame will avoid being placed
     * underneath OS objects such as menu bars or
     * program selection widgets.</p>
     *
     * <p>If the given insets is <code>null</code>,
     * then the insets are set to the default of
     * 50, 50, 50, 50.</p>
     *
     * <p>This method is the same as
     * <code>createQuickJPTFrame(String,Object,Insets)</code>
     * but places the <code>Object</code> argument first
     * and is less verbose.</p>
     *
     * @param object   the object to frame
     * @param title    the title for the frame
     * @param insets   the screen insets to use to adjust the location
     */
    public static JPTFrame frame(
        Object object,
        String title,
        Insets insets) 
    {
        return createQuickJPTFrame
            (title, object, new CenterLayout(), DEFAULT, insets);
    }
    
    
    /**
     * <p>Returns a new <code>JPTFrame</code> 
     * with the given content pane and title
     * that is packed and located on the screen
     * in the given screen location;
     * the given insets bound the frame away from
     * the borders of the screen and also replace
     * the current insets for future calls of
     * <code>setLocation(int)</code> on the frame.</p>
     *
     * <p>If the given object is <code>null</code>,
     * no contents are added to the frame.</p>
     *
     * <p>The layout of the content pane is set
     * to <code>CenterLayout</code>.</p>
     *
     * <p>The location is given by one of the following
     * constants in the interface <code>SwingConstants</code>
     * that are included for convenience in the interface
     * <code>JPTConstants</code> as well:</p>
     *
     * <ul>
     *    <li><code>CENTER</code></li>
     *    <li><code>NORTH</code></li>
     *    <li><code>NORTH_EAST</code></li>
     *    <li><code>EAST</code></li>
     *    <li><code>SOUTH_EAST</code></li>
     *    <li><code>SOUTH</code></li>
     *    <li><code>SOUTH_WEST</code></li>
     *    <li><code>WEST</code></li>
     *    <li><code>NORTH_WEST</code></li>
     * </ul>
     *
     * <p>The insets provide a margin from the edge of
     * the screen so the frame will avoid being placed
     * underneath OS objects such as menu bars or
     * program selection widgets.</p>
     *
     * <p>If the given insets is <code>null</code>,
     * then the insets are set to the default of
     * 50, 50, 50, 50.</p>
     *
     * <p>This method is the same as
     * <code>createQuickJPTFrame(String,Object,int,Insets)</code>
     * but places the <code>Object</code> argument first
     * and is less verbose.</p>
     *
     * @param object   the object to frame
     * @param title    the title for the frame
     * @param location the compass direction representing the desired location
     * @param insets   the screen insets to use to adjust the location
     */
    public static JPTFrame frame(
        Object object,
        String title,
        int    location,
        Insets insets) 
    {
        return createQuickJPTFrame
            (title, object, new CenterLayout(), location, insets);
    }
    
    
    ///////////////////////
    // Protected methods //
    ///////////////////////
    
    /** Returns the shared counter object. */
    protected static XInt getCounter() {
        return counter;
    }
    
    
    /** Installs the window adapter for this frame. */
    public void installWindowAdapter() {
        WindowActionAdapter adapter = new WindowActionAdapter(this);

        adapter.addWindowClosingAction(new SimpleAction() {
            public void perform() {
                synchronized(getCounter()) {
                    int operation = getDefaultCloseOperation();

                    if (operation == DO_NOTHING_ON_CLOSE)
                        return;
               
                    // hide the frame
                    setVisible(false);    
               
                    if (operation == HIDE_ON_CLOSE)
                        return;
               
                    // dispose resources and decrement frame count
                    dispose();
               
                    if (operation == DISPOSE_ON_CLOSE)
                        return;
               
                    // handles EXIT_ON_CLOSE 
                    // and EXIT_ON_CLOSE_IF_LAST
                    if ((operation == EXIT_ON_CLOSE) ||
                        (getJPTFrameCount() == 0))
                            System.exit(0);
                }    
            }
        });
    }
}
