/*
 * @(#)ColorView.java    2.3.3   3 January 2005
 *
 * Copyright 2004
 * 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.gui.*;
import edu.neu.ccs.quick.*;
import edu.neu.ccs.util.*;

import java.awt.*;
import java.awt.event.*;
import java.text.ParseException;
import java.util.*;
import java.beans.*;
import javax.swing.*;
import javax.swing.border.*;

/**
 * <p>A <code>{@link TypedView TypedView}</code> for the input
 * of a <code>Color</code> that contains a color swatch on the
 * left that displays the selected color and an optional drop
 * down view on the right that allows for both the direct input
 * of numeric color data (rgba or hex) and for the selection of
 * certain colors using swatch-name pairs shown in the dropdown.</p>
 *
 * <p>The currently selected color value may be changed through
 * a <code>JColorChooser</code> dialog that appears when the
 * color swatch view is clicked or double clicked depending on
 * the current chooser click count value.  As of 2.3.2,
 * the default chooser click count is set to 1 since
 * in practice this is the value we have used most often.</p>
 *
 * <p>The currently selected color value may be changed in the
 * drop down view by selecting a color swatch-name pair or by
 * entering the color data into the editable text field in one
 * of the following formats.</p>
 *
 * <p><I>Data format 1</I>: A comma and/or blank separated text 
 * string with 3 <code>int</code> components: 
 * <I>red</I>, <I>green</I>, <I>blue</I>
 * or 4 <code>int</code> components: 
 * <I>red</I>, <I>green</I>, <I>blue</I>, <I>alpha</I>.</p>
 *
 * <p>The <code>int</code> components should be in the range
 * [0, 255].</p>
 *
 * <p>If the <I>alpha</I> component is omitted then it is
 * assumed to be 255.</p>
 *
 * <p><I>Data format 2</I>: A <code>String</code> of the form
 * <code>#rRgGbB</code> or <code>#rRgGbBaA</code>
 * where <code>r, R, g, G, b, B, a, A</code> are hex digits with
 * red   = <code>rR</code>,
 * green = <code>gG</code>,
 * blue  = <code>bB</code>,
 * alpha = <code>aA</code>.</p>
 * 
 * <p><I>Data format 3</I>: A <code>String</code> of the form
 * <code>#rgb</code> or <code>#rgba</code> that is interpreted as
 * <code>#rrggbb</code> or <code>#rrggbbaa</code> respectively.</p>
 *
 * <p><I>Data format 4</I>: A color name that is contained in
 * the internal <code>static</code> structure of the class
 * <code>Colors</code>.  An array with the currently valid
 * installed color names can be obtained via the call:</p>
 *
 * <p><code>Colors.getColorNamesAsArray()</code></p>
 *
 * <p>Although the user can type in a color name in the drop down
 * view text area, the same result can be obtained more quickly
 * by using the drop down list.</p>
 *
 * <p>Changes to the input state in the drop down view take effect
 * when a swatch-name selection is made or when the user presses
 * the <code>Return</code> key in the drop down view text area.
 *
 * <p>As of 2.3.3, the constructor default has been changed to
 * display both the color swatch on the left and the color drop
 * down view on the right.  To show only the color swatch, set
 * the <code>boolean</code> parameter <code>showDropdown</code>
 * to <code>false</code> in the constructors that offer that
 * parameter.</p>
 *
 * @author  Richard Rasala
 * @author  Jeff Raab
 * @version 2.3.3
 * @since   1.0
 */
public class ColorView 
    extends DisplayPanel
    implements TypedView, JPTConstants
{
    //////////////////////////
    // Bound Property Names //
    //////////////////////////
    
    /** Bound property name for the chooser click count property. */
    public static final String CHOOSER_CLICK_COUNT = "chooser click count";
    
    /** Bound property name for set current color. */
    public static final String SET_CURRENT_COLOR = "set current color";
    
    /** Bound property name for set default color. */
    public static final String SET_DEFAULT_COLOR = "set default color";
    
    ///////////////
    // Constants //
    ///////////////
    
    /** The initial color and initial default color for the view. */
    public static final Color DEFAULT_COLOR = Color.black;
    
    /** The default click count to trigger the color chooser. */
    public static final int DEFAULT_CHOOSER_CLICK_COUNT = 1;
    
    /** The default size for the color box. */
    public static final int BOX_SIZE = 20;
    
    /** The default dimension for the color view box. */
    public static final Dimension DEFAULT_DIMENSION =
        new Dimension(BOX_SIZE, BOX_SIZE);
    
    /** The default width for the drop down view. */
    public static final int DEFAULT_WIDTH = 200;
    
    
    /////////////////
    // Member Data //
    /////////////////
    
    /** The current color for this view. */
    private Color currentColor = DEFAULT_COLOR;
    
    /** The default color for this view. */
    private Color defaultColor = DEFAULT_COLOR;
    
    /** The click count to trigger the color chooser. */
    private int chooserClickCount = DEFAULT_CHOOSER_CLICK_COUNT;
    
    
    /** The color box view to display the current color. */
    private PaintSwatch colorBox = new PaintSwatch(DEFAULT_COLOR);
    
    
    /** The mouse action adapter for the color box view. */
    private MouseActionAdapter colorBoxActions =
        colorBox.getMouseActionAdapter();
    
    
    /** The standard color box action. */
    private MouseAction colorBoxAction =
        new MouseAction() {
            public void mouseActionPerformed(MouseEvent evt) {
                colorBoxAction(evt);
            }
        };
    
    
    /** The swatch-name renderer. */
    private SwatchNameRenderer renderer = new SwatchNameRenderer();
        
    
    /** The drop down view. */
    private DropdownView colorDropdownView =
        new DropdownView
            (renderer.getStrings(), "", DEFAULT_WIDTH, true, XColor.class);
    
    
    /** 
     * Action triggering the task of setting the current color
     * to the color represented by input in the drop down view.
     */
    private Action setColorFromDropdownView =
        new SimpleAction("Set Color From Drop Down View") {
            public void perform() { 
                setColorFromDropdownView(); 
            }
        };
    
    
    /** The action sequence for handling set color action listeners. */
    private ActionSequence setColorActions = new ActionSequence();
    
    
    //////////////////
    // Constructors //
    //////////////////
    
    
    /**
     * <p>Constructs a view displaying a color swatch with the default color
     * (black) that when clicked will activate a <code>JColorChooser</code>
     * and a color drop down for the precise entry of color data.<p>
     *
     * <p>Other constructors:</p>
     *
     * <ul><code>
     *   <li>ColorView(Color)
     *   <li>ColorView(Color, Dimension)
     *   <li>ColorView(Color, Dimension, int)
     *   <li>ColorView(Color, int, int)
     *   <li>ColorView(Color, int, int, int)
     *   <li>ColorView(Color, boolean)
     *   <li>ColorView(Color, boolean, Dimension)
     *   <li>ColorView(Color, boolean, Dimension, int)
     *   <li>ColorView(Color, boolean, int, int)
     *   <li>ColorView(Color, boolean, int, int, int)
     * </code></ul>
     */
    public ColorView() {
        this(null, true, null, 0);
    }
    
    
    /**
     * <p>Constructs a view displaying a color swatch with the default color
     * (black) that when clicked will activate a <code>JColorChooser</code>
     * and a color drop down for the precise entry of color data;
     * the initial color to be displayed is the given color.<p>
     *
     * <p>If the given color is <code>null</code>, the default color (black)
     * is used.</p>
     *
     * <p>If the given color is non-<code>null</code>, the default color is
     * reset to the given color.</p>
     *
     * @param color the color to be displayed
     * @see #ColorView()
     */
    public ColorView(Color color) {
        this(color, true, null, 0);
    }
    
    
    /**
     * <p>Constructs a view displaying a color swatch with the default color
     * (black) that when clicked will activate a <code>JColorChooser</code>
     * and a color drop down for the precise entry of color data;
     * the initial color to be displayed is the given color;
     * the size of the color swatch is provided by the given dimension.</p>
     *
     * <p>If the given color is <code>null</code>, the default color (black)
     * is used.</p>
     *
     * <p>If the given color is non-<code>null</code>, the default color is
     * reset to the given color.</p>
     *
     * @param color     the color to be displayed
     * @param dimension the dimension of the color swatch
     * @see #ColorView()
     * @since 2.3.3
     */
    public ColorView(Color color, Dimension dimension)
    {
        this(color, true, dimension, 0);
    }
    
    
    /**
     * <p>Constructs a view displaying a color swatch with the default color
     * (black) that when clicked will activate a <code>JColorChooser</code>
     * and a color drop down for the precise entry of color data;
     * the initial color to be displayed is the given color;
     * the size of the color swatch is provided by the given dimension;
     * the minimum width of the drop down editable area is the given minWidth.</p>
     *
     * <p>If the given color is <code>null</code>, the default color (black)
     * is used.</p>
     *
     * <p>If the given color is non-<code>null</code>, the default color is
     * reset to the given color.</p>
     *
     * <p>The minimal width of the dropdown view is computed internally to
     * ensure that each color swatch and name pair is fully visible.  If
     * the given minWidth is greater than this minimal width then the
     * dropdown view is further widened.</p>
     *
     * @param color     the color to be displayed
     * @param dimension the dimension of the color swatch
     * @param minWidth  the minimum width of the drop down editable area
     * @see #ColorView()
     * @since 2.3.3
     */
    public ColorView
        (Color color, Dimension dimension, int minWidth)
    {
        this(color, true, dimension, minWidth);
    }
    
    
    /**
     * <p>Constructs a view displaying a color swatch with the default color
     * (black) that when clicked will activate a <code>JColorChooser</code>
     * and a color drop down for the precise entry of color data;
     * the initial color to be displayed is the given color;
     * the size of the color swatch is provided by the given width and height.</p>
     *
     * <p>If the given color is <code>null</code>, the default color (black)
     * is used.</p>
     *
     * <p>If the given color is non-<code>null</code>, the default color is
     * reset to the given color.</p>
     *
     * @param color  the color to be displayed
     * @param width  the width of the color swatch
     * @param height the height of the color swatch
     * @see #ColorView()
     * @since 2.3.3
     */
    public ColorView
        (Color color, int width, int height)
    {
        this(color, true, new Dimension(width, height), 0);
    }
    
    
    /**
     * <p>Constructs a view displaying a color swatch with the default color
     * (black) that when clicked will activate a <code>JColorChooser</code>
     * and a color drop down for the precise entry of color data;
     * the initial color to be displayed is the given color;
     * the size of the color swatch is provided by the given width and height;
     * the minimum width of the drop down editable area is the given minWidth.</p>
     *
     * <p>If the given color is <code>null</code>, the default color (black)
     * is used.</p>
     *
     * <p>If the given color is non-<code>null</code>, the default color is
     * reset to the given color.</p>
     *
     * <p>The minimal width of the dropdown view is computed internally to
     * ensure that each color swatch and name pair is fully visible.  If
     * the given minWidth is greater than this minimal width then the
     * dropdown view is further widened.</p>
     *
     * @param color    the color to be displayed
     * @param width    the width of the color swatch
     * @param height   the height of the color swatch
     * @param minWidth the minimum width of the drop down editable area
     * @see #ColorView()
     * @since 2.3.3
     */
    public ColorView
        (Color color, int width, int height, int minWidth)
    {
        this(color, true, new Dimension(width, height), minWidth);
    }
    
    
    /**
     * <p>Constructs a view displaying a color swatch with the default color
     * (black) that when clicked will activate a <code>JColorChooser</code>
     * and, if <code>showDropdown</code> is <code>true</code>,
     * a color drop down for the precise entry of color data;
     * the initial color to be displayed is the given color.<p>
     *
     * <p>If the given color is <code>null</code>, the default color (black)
     * is used.</p>
     *
     * <p>If the given color is non-<code>null</code>, the default color is
     * reset to the given color.</p>
     *
     * @param color        the color to be displayed
     * @param showDropdown whether or not the drop down is in the view
     * @see #ColorView()
     */
    public ColorView(Color color, boolean showDropdown) {
        this(color, showDropdown, null, 0);
    }
    
    
    /**
     * <p>Constructs a view displaying a color swatch with the default color
     * (black) that when clicked will activate a <code>JColorChooser</code>
     * and, if <code>showDropdown</code> is <code>true</code>,
     * a color drop down for the precise entry of color data;
     * the initial color to be displayed is the given color;
     * the size of the color swatch is provided by the given dimension.</p>
     *
     * <p>If the given color is <code>null</code>, the default color (black)
     * is used.</p>
     *
     * <p>If the given color is non-<code>null</code>, the default color is
     * reset to the given color.</p>
     *
     * @param color        the color to be displayed
     * @param showDropdown whether or not the drop down is in the view
     * @param dimension    the dimension of the color swatch
     * @see #ColorView()
     */
    public ColorView(Color color, boolean showDropdown, Dimension dimension)
    {
        this(color, showDropdown, dimension, 0);
    }
    
    
    /**
     * <p>Constructs a view displaying a color swatch with the default color
     * (black) that when clicked will activate a <code>JColorChooser</code>
     * and, if <code>showDropdown</code> is <code>true</code>,
     * a color drop down for the precise entry of color data;
     * the initial color to be displayed is the given color;
     * the size of the color swatch is provided by the given dimension;
     * the minimum width of the drop editable area is the given minWidth.</p>
     *
     * <p>If the given color is <code>null</code>, the default color (black)
     * is used.</p>
     *
     * <p>If the given color is non-<code>null</code>, the default color is
     * reset to the given color.</p>
     *
     * <p>The minimal width of the dropdown view is computed internally to
     * ensure that each color swatch and name pair is fully visible.  If
     * the given minWidth is greater than this minimal width then the
     * dropdown view is further widened.</p>
     *
     * @param color        the color to be displayed
     * @param showDropdown whether or not the drop down is in the view
     * @param dimension    the dimension of the color swatch
     * @param minWidth     the minimum width of the drop down editable area
     * @see #ColorView()
     */
    public ColorView
        (Color color, boolean showDropdown, Dimension dimension, int minWidth)
    {
        setLayout(new TableLayout(1, 2, 5, 5, CENTER));
        
        installColorBox(color, dimension);
        installColorDropdownView(showDropdown, minWidth);
        installColorBoxActions();
        
        reset();
    }
    
    
    /**
     * <p>Constructs a view displaying a color swatch with the default color
     * (black) that when clicked will activate a <code>JColorChooser</code>
     * and, if <code>showDropdown</code> is <code>true</code>,
     * a color drop down for the precise entry of color data;
     * the initial color to be displayed is the given color;
     * the size of the color swatch is provided by the given width and height.</p>
     *
     * <p>If the given color is <code>null</code>, the default color (black)
     * is used.</p>
     *
     * <p>If the given color is non-<code>null</code>, the default color is
     * reset to the given color.</p>
     *
     * @param color        the color to be displayed
     * @param showDropdown whether or not the drop down is in the view
     * @param width        the width of the color swatch
     * @param height       the height of the color swatch
     * @see #ColorView()
     */
    public ColorView
        (Color color, boolean showDropdown, int width, int height)
    {
        this(color, showDropdown, new Dimension(width, height), 0);
    }
    
    
    /**
     * <p>Constructs a view displaying a color swatch with the default color
     * (black) that when clicked will activate a <code>JColorChooser</code>
     * and, if <code>showDropdown</code> is <code>true</code>,
     * a color drop down for the precise entry of color data;
     * the initial color to be displayed is the given color;
     * the size of the color swatch is provided by the given width and height;
     * the minimum width of the drop down editable area is the given minWidth.</p>
     *
     * <p>If the given color is <code>null</code>, the default color (black)
     * is used.</p>
     *
     * <p>If the given color is non-<code>null</code>, the default color is
     * reset to the given color.</p>
     *
     * <p>The minimal width of the dropdown view is computed internally to
     * ensure that each color swatch and name pair is fully visible.  If
     * the given minWidth is greater than this minimal width then the
     * dropdown view is further widened.</p>
     *
     * @param color        the color to be displayed
     * @param showDropdown whether or not the drop down is in the view
     * @param width        the width of the color swatch
     * @param height       the height of the color swatch
     * @param minWidth     the minimum width of the drop down editable area
     * @see #ColorView()
     */
    public ColorView
        (Color color, boolean showDropdown, int width, int height, int minWidth)
    {
        this(color, showDropdown, new Dimension(width, height), minWidth);
    }
    
    
    ////////////////
    // Public API //
    ////////////////
    
    
    /**
     * <p>Returns the internal <code>JPTComponent</code> that represents
     * the color box in the view.</p>
     *
     * <p>As of 2.3.2, returns <code>PaintSwatch</code> rather than its
     * base class <code>JPTComponent</code>.</p>
     *
     * @return the internal color box
     */
    public PaintSwatch getColorBox() {
        return colorBox;
    }
    
    
    /**
     * <p>Returns the internal <code>DropdownView</code> that may be used
     * to choose the color via a <code>String</code> representation.</p>
     *
     * <p>By default, this view is not visible.  The view is made visible
     * by passing <code>true</code> in the constructor parameter
     * <code>showDropdown</code>.</p>
     *
     * @return the internal color drop down view
     */
    public DropdownView getColorDropdownView() {
        return colorDropdownView;
    }
    
    
    /**
     * <p>Sets the current color to the given color.</p>
     *
     * <p>If the given color is <code>null</code> or is equal to
     * the current color, then does nothing.</p>
     *
     * <p>Fires property change: SET_CURRENT_COLOR.</p>
     *
     * @param color the new current color for this view
     * @see #getColor()
     * @see #setDefaultColor(Color)
     * @see #getDefaultColor()
     */
    public void setColor(Color color) {
        if ((color == null) || (color.equals(currentColor)))
            return;
        
        currentColor = color;
        
        colorBox.setPaint(color);
        colorDropdownView.setViewState(getViewState());
        
        setColorActions.actionPerformed
            (new ActionEvent
                (this, ActionEvent.ACTION_PERFORMED, SET_CURRENT_COLOR));
        
        firePropertyChange(SET_CURRENT_COLOR, null, null);
    }
    
    
    /**
     * Returns the current color.
     *
     * @see #setColor(Color)
     * @see #setDefaultColor(Color)
     * @see #getDefaultColor()
     */
    public Color getColor() {
        return currentColor;
    }
    
    
    /**
     * <p>Sets the default color to the given color.</p>
     *
     * <p>If the given color is <code>null</code> or is equal to
     * the default color, then does nothing.</p>
     *
     * <p>Fires property change: SET_DEFAULT_COLOR.</p>
     *
     * @param color the new current color for this view
     * @param color the new default color for this view
     * @see #getDefaultColor()
     * @see #setColor(Color)
     * @see #getColor()
     */
    public void setDefaultColor(Color color) {
        if ((color == null) || (color.equals(defaultColor)))
            return;
        
        defaultColor = color;
        
        colorDropdownView.setDefaultViewState(getDefaultViewState());
        
        firePropertyChange(SET_DEFAULT_COLOR, null, null);
    }
    
    
    /**
     * Returns the default color.
     *
     * @see #setDefaultColor(Color)
     * @see #setColor(Color)
     * @see #getColor()
     */
    public Color getDefaultColor() {
        return defaultColor;
    }
    
    
    /**
     * Returns the <code>ActionSequence</code> that holds the actions
     * to be performed when the method <code>setColor</code> is called.
     */
    public ActionSequence getSetColorActions() {
        return setColorActions;
    }
    
    
    /**
     * Append an action listener to be performed when the method
     * <code>setColor</code> is called.
     *
     * @param action the action listener to append
     * @see #addAction(int, ActionListener)
     */
    public void addAction(ActionListener action) {
        setColorActions.add(action);
    }
    
    
    /**
     * Add an action listener to be performed when the method
     * <code>setColor</code> is called and place this action
     * listener at the given index in the set color actions
     * sequence.
     *
     * @param index the place in the sequence to add the listener
     * @param action the action listener to add
     * @see #addAction(ActionListener)
     */
    public void addAction(int index, ActionListener action) {
        setColorActions.add(index, action);
    }
    
    
    /**
     * <p>Remove an action listener that was to be performed when
     * the method <code>setColor</code> is called.</p>
     *
     * <p>Returns <code>true</code> if removal was successful and
     * <code>false</code> otherwise.</p>
     *
     * @param action the action listener to remove
     * @see #removeAction(int)
     */
    public boolean removeAction(ActionListener action) {
        return setColorActions.remove(action);
    }
    
    
    /**
     * <p>Remove an action listener at the given index position that
     * was to be performed when the method <code>setColor</code> is
     * called.</p>
     *
     * <p>Returns the <code>ActionListener</code> removed or
     * <code>null</code>.</p>
     *
     * @param index the index of the action listener to remove
     * @see #removeAction(ActionListener)
     */
    public ActionListener removeAction(int index) {
        return setColorActions.remove(index);
    }
    
    
    /**
     * Returns the mouse action adapter for the color box in this
     * color view.
     *
     * @return the mouse action adapter
     */
    public MouseActionAdapter getMouseActionAdapter() {
        return colorBoxActions;
    }
    
    
    /**
     * <p>Sets the number of mouse clicks required to bring up the
     * color chooser dialog.</p>
     *
     * <p>The valid click counts are:</p>
     *
     * <ul>
     *     <li>1 (click)</li>
     *     <li>2 (double-click)</li>
     * </ul>
     *
     * <p>If the given click count is less than 1, it is set to 1.</p>
     *
     * <p>If the given click count is greater than 2, it is set to 2.</p>
     *
     * <p>As of 2.3.2, the default click count is 1.</p>
     *
     * @see #getChooserClickCount()
     */
    public void setChooserClickCount(int count) {
        count = (count <= 1) ? 1 : 2;
        
        if (count == chooserClickCount)
            return;
        
        chooserClickCount = count;

        firePropertyChange(CHOOSER_CLICK_COUNT, null, null);
    }
    
    
    /**
     * Return the chooser click count.
     *
     * @see #setChooserClickCount(int)
     */
    public int getChooserClickCount() {
        return chooserClickCount;
    }
    
    
    ///////////////
    // TypedView //
    ///////////////
    
    
    /**
     * <p>Returns an <code>XColor</code> object 
     * whose state is set to the view state of this view.</p>
     *
     * @see #requestObject()
     * @see TypedView
     */
    public Stringable demandObject() {
        return new XColor(getColor());
    }
    
    
    /**
     * <p>Returns an <code>XColor</code> object 
     * whose state is set to the view state of this view.</p>
     *
     * <p>There is no need for a <code>CancelledException</code>
     * since the current view state cannot be invalid.</p>
     *
     * @see #demandObject()
     * @see TypedView
     */
    public Stringable requestObject() {
        return demandObject();
    }
    
    
    /**
     * Sets the input properties of the internal color drop down view
     * and fires a property change if the input properties have changed.
     *
     * @param p the new input properties to set
     *
     * @see TypedView
     */
    public void setInputProperties(InputProperties p) {
        getColorDropdownView().setInputProperties(p);

        firePropertyChange(INPUT_PROPERTIES, null, null);
    }
    
    
    /**
     * Returns the input properties of the internal color drop down view.
     *
     * @return the current input properties
     * @see TypedView
     */
    public InputProperties getInputProperties() {
        return getColorDropdownView().getInputProperties();
    }
    
    
    /**
     * Returns the <code>XColor</code> class object.
     *
     * @see TypedView
     */ 
    public Class getDataType() {
        return XColor.class;
    }


    /////////////////
    // Displayable //
    /////////////////
    
    
    /**
     * <p>Sets the current view state for this view to the <code>Color</code>
     * represented by the given <code>String</code> data.</p>
     *
     * <p>Does nothing if the <code>String</code> data fails to parse into a
     * valid <code>Color</code>.</p>
     *
     * @param data the new view state
     * @see #getViewState()
     * @see Displayable
     */
    public void setViewState(String data) {
        try {
            XColor xcolor = new XColor(data);
            setColor(xcolor.getValue());
        }
        catch (ParseException ex) {
        }
    }
    
    
    /**
     * Returns a <code>String</code> encapsulation of the state of
     * the current <code>Color</code> for this view.
     *
     * @see #setViewState(String)
     * @see Displayable
     */
    public String getViewState() {
        return XColor.colorToString(getColor());
    }
    
    
    /**
     * <p>Sets the default view state for this view to the <code>Color</code>
     * represented by the given <code>String</code> data.<p>
     *
     * <p>Does nothing if the <code>String</code> data fails to parse into a
     * valid <code>Color</code>.</p>
     *
     * @param data the new view state
     * @see #getViewState()
     * @see Displayable
     */
    public void setDefaultViewState(String data) {
        try {
            XColor xcolor = new XColor(data);
            setDefaultColor(xcolor.getValue());
        }
        catch (ParseException ex) {
        }
    }
    
    
    /**
     * Returns a <code>String</code> encapsulation of the state 
     * of the default <code>Color</code> for this view.
     *
     * @see #setViewState(String)
     * @see Displayable
     */
    public String getDefaultViewState() {
        return XColor.colorToString(getDefaultColor());
    }
    
    
    /** Resets the color to the default color. */
    public void reset() {
        setColor(defaultColor);
    }
    
    
    ///////////////////////
    // Protected methods //
    ///////////////////////
    
    
    /**
     * Sets the current color for this view to the color
     * represented by the input in its color drop down view.
     */
    protected void setColorFromDropdownView() {
        XColor xcolor = (XColor) colorDropdownView.demandObject();
        setColor(xcolor.getValue());
    }
    
    
    /** Installs the color box for this view. */
    protected void installColorBox(Color color, Dimension dimension)
    {
        // view states
        color = (color == null) ? DEFAULT_COLOR : color;
        
        currentColor = color;
        defaultColor = color;
        
        colorBox.setPaint(color);
        
        // geometry
        if (dimension != null)
            colorBox.setSize(dimension.width, dimension.height);
        
        // install
        add(colorBox);
    }
    
    
    /** Installs the color drop down view. */
    protected void installColorDropdownView
        (boolean showDropdown, int minWidth)
    {
        // view states
        colorDropdownView.setViewState(getViewState());
        colorDropdownView.setDefaultViewState(getDefaultViewState());
        
        // geometry & rendering
        
        Dimension size = renderer.getMaximumPreferredSize();
        minWidth = Math.max(minWidth, size.width);
        
        colorDropdownView.setMinimumWidth(minWidth);
        colorDropdownView.autoSetPreferredWidth();
        
        colorDropdownView.setRenderer(renderer);
        colorDropdownView.setMaximumRowCount(18);
        
        // error settings
        colorDropdownView.setErrorPromptTitleSuggestion
            ("Color Error", "Color Error Dialog", getDefaultViewState());
        
        // listener
        colorDropdownView.addActionListener(setColorFromDropdownView);
        
        // install
        if (showDropdown)
            add(new Halo(colorDropdownView));
    }
    
    
    /**
     * Installs the color box mouse actions triggered by mouse events
     * targeted on the color box contained in this view.
     */
    protected void installColorBoxActions() {
        colorBoxActions.addMouseClickedAction(colorBoxAction);
    }
    
    
    /** The standard color box action method. */
    private void colorBoxAction(MouseEvent evt) {
        if (colorBox.isEnabled()) {
            if (evt.getClickCount() >= chooserClickCount) {
                Color c = JColorChooser.showDialog(
                    new JLabel("Choose Color:"),
                    "",
                    getColor());
           
                if (c != null)
                    setColor(c);
            }
        }
    }
    
}
