/*
 * @(#)Dropdown.java    2.3.3   2 Januaryn 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.util.*;
import edu.neu.ccs.filter.*;

import java.awt.*;
import java.math.*;
import java.text.*;
import java.util.*;
import javax.swing.*;

/**
 * <p>Class <code>Dropdown</code> provides the base functionality
 * for dropdown lists.</p>
 *
 * <p>The class extends the functionality of <code>JComboBox</code>.</p>
 *
 * <p>In turn, this class is the base class for <code>DropdownView</code>
 * and for <code>StringObjectDropdown</code>.</p> 
 *
 * @author  Richard Rasala
 * @author  Jeff Raab
 * @version 2.3.3
 * @since   2.3.3
 */
public class Dropdown extends JComboBox 
    implements Displayable, JPTConstants 
{
    /** Bound property name for the set preferred width property. */
    public static final String PREFERRED_WIDTH = "set.preferred.width";
    
    
    /**
     * The default preferred width used in constructors if
     * there is no other data with which to set the width.
     */
    public static final int DEFAULT_WIDTH = 125;
    
    
    /** Default initially selected label for a dropdown view. */
    protected static final String DEFAULT_SELECTION = "";


    /**
     * The current preferred width if one is enabled or -1
     * if preferred width is disabled.
     */
    protected int preferredWidth = DEFAULT_WIDTH;
    
    
    /** The current minimum width for automatic width computations. */
    protected int minimumWidth = 0;
    
    
    /** The list of current items. */
    protected Vector itemList = null;
    
    
    /** The default view state. */
    protected String defaultViewState = "";
    
    
    //////////////////
    // Constructors //
    //////////////////
    
    /**
     * Constructs a view with no initial items.
     *
     * <p>Other constructors:</p>
     *
     * <ul><code>
     *   <li>Dropdown(int)</li>
     *   <li>Dropdown(Font)</li>
     *   <li>Dropdown(Font, int)</li>
     *   <li>Dropdown(String[])</li>
     *   <li>Dropdown(String[], int)</li>
     *   <li>Dropdown(String[], Font)</li>
     *   <li>Dropdown(String[], Font, int)</li>
     *   <li>Dropdown(String[], String)</li>
     *   <li>Dropdown(String[], String, int)</li>
     *   <li>Dropdown(String[], String, Font)</li>
     *   <li>Dropdown(String[], String, Font, int)</li>
     *   <li>Dropdown(String[], String, boolean)</li>
     *   <li>Dropdown(String[], String, int, boolean)</li>
     *   <li>Dropdown(String[], String, Font, boolean)</li>
     *   <li>Dropdown(String[], String, Font, int, boolean)</li>
     * </code></ul>
     */
    public Dropdown() {
        initializeDropdown(null, DEFAULT_SELECTION, null, 0, false);
    }
    
    
    /**
     * Constructs a view with no initial items and the given minimum width.
     *
     * @param minWidth the preferred minimum width
     * @see   #Dropdown()
     */
    public Dropdown(int minWidth) {
        initializeDropdown(null, DEFAULT_SELECTION, null, minWidth, false);
    }
    
    
    /**
     * Constructs a view with no initial items and the given font.
     *
     * @param font the font for the view
     * @see   #Dropdown()
     */
    public Dropdown(Font font) {
        initializeDropdown(null, DEFAULT_SELECTION, font, 0, false);
    }
    
    
    /**
     * Constructs a view with no initial items, the given font, and the
     * given minimum width.
     *
     * @param font the font for the view
     * @param minWidth the preferred minimum width
     * @see   #Dropdown()
     */
    public Dropdown(Font font, int minWidth) {
        initializeDropdown(null, DEFAULT_SELECTION, font, minWidth, false);
    }
    
    
    /**
     * <p>Constructs a view with the given array of items,
     * for which the first item is selected by default.</p>
     *
     * <p>If the given array of items is <code>null</code>,
     * no items are initially added to the view.</p>
     *
     * @param items the array of dropdown items
     * @see   #Dropdown()
     */
    public Dropdown(String[] items) {
        initializeDropdown(items, DEFAULT_SELECTION, null, 0, false);
    }
    
    
    /**
     * <p>Constructs a view with the given array of items,
     * for which the first item is selected by default
     * and with the given minimum width.</p>
     *
     * <p>If the given array of items is <code>null</code>,
     * no items are initially added to the view.</p>
     *
     * @param items    the array of dropdown items
     * @param minWidth the preferred minimum width
     * @see   #Dropdown()
     */
    public Dropdown(String[] items, int minWidth) {
        initializeDropdown(items, DEFAULT_SELECTION, null, minWidth, false);
    }
    
    
    /**
     * <p>Constructs a view with the given array of items,
     * for which the first item is selected by default
     * and with the given font.</p>
     *
     * <p>If the given array of items is <code>null</code>,
     * no items are initially added to the view.</p>
     *
     * @param items the array of dropdown items
     * @param font  the font for the view
     * @see   #Dropdown()
     */
    public Dropdown(String[] items, Font font) {
        initializeDropdown(items, DEFAULT_SELECTION, font, 0, false);
    }
    
    
    /**
     * <p>Constructs a view with the given array of items,
     * for which the first item is selected by default
     * and with the given font and minimum width.</p>
     *
     * <p>If the given array of items is <code>null</code>,
     * no items are initially added to the view.</p>
     *
     * @param items    the array of dropdown items
     * @param font     the font for the view
     * @param minWidth the preferred minimum width
     * @see   #Dropdown()
     */
    public Dropdown(String[] items, Font font, int minWidth) {
        initializeDropdown(items, DEFAULT_SELECTION, font, minWidth, false);
    }
    
    
    /**
     * <p>Constructs a view with the given array of items, 
     * for which the given selection is selected by default.</p>
     *
     * <p>If the given array of items is <code>null</code>,
     * no items are initially added to the view.</p>
     *
     * <p>If the view is not editable and the selection is
     * not in the array of items then the selection will
     * set the default state but will not be displayed.</p>
     *
     * @param items     the array of dropdown items
     * @param selection the default initial choice
     * @see   #Dropdown()
     */
    public Dropdown(String[] items, String selection) {
        initializeDropdown(items, selection, null, 0, false);
    }
    
    
    
    /**
     * <p>Constructs a view with the given array of items, 
     * for which the given selection is selected by default,
     * and with the given minimum width.</p>
     *
     * <p>If the given array of items is <code>null</code>,
     * no items are initially added to the view.</p>
     *
     * <p>If the view is not editable and the selection is
     * not in the array of items then the selection will
     * set the default state but will not be displayed.</p>
     *
     * @param items     the array of dropdown items
     * @param selection the default initial choice
     * @param minWidth  the preferred minimum width
     * @see   #Dropdown()
     */
    public Dropdown(String[] items, String selection, int minWidth) {
        initializeDropdown(items, selection, null, minWidth, false);
    }
    
    
    /**
     * <p>Constructs a view with the given array of items, 
     * for which the given selection is selected by default
     * and with the given font.</p>
     *
     * <p>If the given array of items is <code>null</code>,
     * no items are initially added to the view.</p>
     *
     * <p>If the view is not editable and the selection is
     * not in the array of items then the selection will
     * set the default state but will not be displayed.</p>
     *
     * @param items     the array of dropdown items
     * @param selection the default initial choice
     * @param font      the font for the view
     * @see   #Dropdown()
     */
    public Dropdown(String[] items, String selection, Font font) {
        initializeDropdown(items, selection, font, 0, false);
    }
    
    
    
    /**
     * <p>Constructs a view with the given array of items, 
     * for which the given selection is selected by default,
     * and with the given font and minimum width.</p>
     *
     * <p>If the given array of items is <code>null</code>,
     * no items are initially added to the view.</p>
     *
     * <p>If the view is not editable and the selection is
     * not in the array of items then the selection will
     * set the default state but will not be displayed.</p>
     *
     * @param items     the array of dropdown items
     * @param selection the default initial choice
     * @param font      the font for the view
     * @param minWidth  the preferred minimum width
     * @see   #Dropdown()
     */
    public Dropdown(String[] items, String selection, Font font, int minWidth) {
        initializeDropdown(items, selection, font, minWidth, false);
    }
    
    
    /**
     * <p>Constructs a view with the given array of items, 
     * for which the given selection is selected by default,
     * and with the given editable state.</p>
     *
     * <p>If the given array of items is <code>null</code>,
     * no items are initially added to the view.</p>
     *
     * <p>If the view is not editable and the selection is
     * not in the array of items then the selection will
     * set the default state but will not be displayed.</p>
     *
     * @param items     the array of dropdown items
     * @param selection the label for the initial default choice
     * @param editable  whether or not the view is user editable
     * @see   #Dropdown()
     */
    public Dropdown(
        String[] items,
        String   selection,
        boolean  editable )
    {
        initializeDropdown(items, selection, null, 0, editable);
    }
    
    
    /**
     * <p>Constructs a view with the given array of items, 
     * for which the given selection is selected by default,
     * and with the given width and editable state.</p>
     *
     * <p>If the given array of items is <code>null</code>,
     * no items are initially added to the view.</p>
     *
     * <p>If the view is not editable and the selection is
     * not in the array of items then the selection will
     * set the default state but will not be displayed.</p>
     *
     * @param items     the array of dropdown items
     * @param selection the label for the initial default choice
     * @param minWidth  the preferred minimum width
     * @param editable  whether or not the view is user editable
     * @see   #Dropdown()
     */
    public Dropdown(
        String[] items,
        String   selection,
        int      minWidth,
        boolean  editable )
    {
        initializeDropdown(items, selection, null, minWidth, editable);
    }
    
    
    /**
     * <p>Constructs a view with the given array of items, 
     * for which the given selection is selected by default,
     * and with the given font and editable state.</p>
     *
     * <p>If the given array of items is <code>null</code>,
     * no items are initially added to the view.</p>
     *
     * <p>If the view is not editable and the selection is
     * not in the array of items then the selection will
     * set the default state but will not be displayed.</p>
     *
     * @param items     the array of dropdown items
     * @param selection the label for the initial default choice
     * @param font      the font for the view
     * @param editable  whether or not the view is user editable
     * @see   #Dropdown()
     */
    public Dropdown(
        String[] items,
        String   selection,
        Font     font,
        boolean  editable )
    {
        initializeDropdown(items, selection, font, 0, editable);
    }
    
    
    /**
     * <p>Constructs a view with the given array of items, 
     * for which the given selection is selected by default,
     * and with the given font, width, and editable state.</p>
     *
     * <p>If the given array of items is <code>null</code>,
     * no items are initially added to the view.</p>
     *
     * <p>If the view is not editable and the selection is
     * not in the array of items then the selection will
     * set the default state but will not be displayed.</p>
     *
     * @param items     the array of dropdown items
     * @param selection the label for the initial default choice
     * @param font      the font for the view
     * @param minWidth  the preferred minimum width
     * @param editable  whether or not the view is user editable
     * @see   #Dropdown()
     */
    public Dropdown(
        String[] items,
        String   selection,
        Font     font,
        int      minWidth,
        boolean  editable )
    {
        initializeDropdown(items, selection, font, minWidth, editable);
    }
    
    
    /**
     * The initializer.
     *
     * @param items     the array of dropdown items
     * @param selection the label for the initial default choice
     * @param font      the font for the view
     * @param minWidth  the preferred minimum width
     * @param editable  whether or not the view is user editable
     */
    protected void initializeDropdown(
        String[]         items,
        String           selection,
        Font             font,
        int              minWidth,
        boolean          editable)
    {
        itemList = new Vector();
        
        setEditable(editable);
        
        if (font != null)
            super.setFont(font);
        
        if (items != null) {
            int length = items.length;
            
            for (int i = 0; i < length; i++)
                addItem(items[i]);
        }
        
        setMinimumWidth(minWidth);
        autoSetPreferredWidth();
        
        setViewState(selection);
        setDefaultViewState(selection);
    }
    
    
    ////////////////
    // Public API //
    ////////////////
    
    /**
     * <p>Adds the item to the view by calling the method in the parent
     * and also maintains a record that this item was added.</p>
     *
     * <p>This override of an inherited method will only add the item
     * if it is a non-<code>null</code> instance of <code>String</code>.</p>
     *
     * <p>To avoid multiple refresh operations after each add operation,
     * this method does not auto refresh.</p>
     *
     * @param item the item to add
     */
    public void addItem(Object item) {
        if (item == null)
            return;
        
        if (! (item instanceof String))
            return;
        
        itemList.add(item);
        super.addItem(item);
    }
   
   
    /**
     * <p>Inserts an item into the item list at a given index by calling the
     * method in the parent and also maintains a record that this item was
     * added at that position.</p>
     *
     * <p>This override of an inherited method will only insert the item
     * if it is a non-<code>null</code> instance of <code>String</code>.</p>
     *
     * <p>To avoid multiple refresh operations after each insert operation,
     * this method does not auto refresh.</p>
     *
     * @param item the item to add
     * @param index the position at which to add the item
     */
    public void insertItemAt(Object item, int index) {
        if (item == null)
            return;
        
        if (! (item instanceof String))
            return;
        
        // notice the reversal of the parameter order in the two calls
        // since Vector and JComboBox are inconsistent
        itemList.add(index, item);
        super.insertItemAt(item, index);
    }
    
    
    /**
     * <p>Removes the item from the view by calling the method in the parent
     * and also maintains a record that this item was removed.</p>
     *
     * <p>This override of an inherited method will only remove the item
     * if it is a non-<code>null</code> instance of <code>String</code>.</p>
     *
     * <p>To avoid multiple refresh operations after each remove operation,
     * this method does not auto refresh.</p>
     *
     * @param item the item to remove
     */
    public void removeItem(Object item) {
        if (item == null)
            return;
        
        if (! (item instanceof String))
            return;
        
        itemList.remove(item);
        super.removeItem(item);
    }
   
    
    /**
     * <p>Removes an item from the item list at a given index by calling the
     * method in the parent and also maintains a record that this item was
     * removed from that position.</p>
     *
     * <p>To avoid multiple refresh operations after each remove operation,
     * this method does not auto refresh.</p>
     *
     * @param index the position at which to remove the item
     */
    public void removeItemAt(int index) {
        itemList.remove(index);
        super.removeItemAt(index);
    }
    
    
    /**
     * <p>Removes all items from the view by calling the method in the parent
     * and also maintains a record that all items were removed.</p>
     */
    public void removeAllItems() {
        super.removeAllItems();
        itemList.clear();
    }
    
    
    /**
     * <p>Adds the given <code>String</code> array of items to the view.</p>
     *
     * <p>Calls <code>refresh()</code> if items are added.</p>
     *
     * <p>If the items array is <code>null</code>, then does nothing.</p>
     *
     * @param items the items to be added
     */
    public void addItems(String[] items) {
        if (items == null)
            return;
        
        for (int i = 0; i < items.length; i++)
            addItem(items[i]);
        
        refresh();
    }
    
    
    /**
     * <p>Removes all items in the view and then adds the given
     * <code>String</code> array of items to the view.</p>
     *
     * <p>Calls <code>refresh()</code> if items are added.</p>
     *
     * <p>If the items array is <code>null</code>, then simply removes
     * all items in the view and leaves the view empty.</p>
     *
     * @param items the items to be added after removing existing items
     */
    public void setItems(String[] items) {
        removeAllItems();
        addItems(items);
    }
    
    
    /**
     * <p>Removes the given array of items from the view.</p>
     *
     * <p>If the items array is <code>null</code>, then does nothing.</p>
     *
     * @param items the items to be removed
     */
    public void removeItems(String[] items) {
        if (items == null)
            return;
        
        for (int i = 0; i < items.length; i++)
            if (items[i] != null)
                removeItem(items[i]);
    }
    
    
    /**
     * Returns an array of all items currently installed in the view.
     *
     * @return an array of all items
     */
    public String[] getItems() {
        String[] base = new String[0];
        
        if ((itemList == null) || (itemList.size() == 0))
            return base;
        else 
            return (String[]) itemList.toArray(base);
    }
    
    
    /**
     * <p>Returns the preferred size of this view.</p>
     *
     * <p>This method proceeds as follows.</p>
     *
     * <p>The preferred size is calculated by the super class
     * method.  Then, if the preferred width set in this class
     * is positive, the width field of the preferred size is
     * replaced by the sum of the preferred width and the
     * height field of the preferred size.  The preferred size
     * then is returned.</p>
     *
     * <p>This calculation is done so that the preferred width
     * controls the width of the text portion of the dropdown
     * and the actual width includes room for the control.</p>
     *
     * @return the value of the preferredSize property
     */
    public Dimension getPreferredSize() {
        Dimension dimension = super.getPreferredSize();
        
        int width = getPreferredWidth();
        
        if (width > 0)
            dimension.width = width + dimension.height;
        
        return dimension;
    }
    
    
    /**
     * <p>Returns the preferred width setting of this view.</p>
     *
     * <p>The value returned is positive or -1.</p>
     *
     * <p>If the value is -1 then it is not used to compute the
     * preferred size.</p>
     *
     * @return the current preferred width of this view
     */
    public int getPreferredWidth() {
        return preferredWidth;
    }
    
    
    /**
     * <p>Automatically sets the preferred width based on the current setting
     * of the minimum width, the current items in the dropdown list, and the
     * current font.</p>
     *
     * <p>It is recommended that this method be used to set the width
     * of the dropdown rather than an explicit call to one of the
     * methods <code>setPreferredWidth</code>.  This method is called
     * in the constructors and in the method <code>refresh()</code>.</p>
     */
    public void autoSetPreferredWidth() {
        int minWidth = getMinimumWidth();
        int samWidth = TextFieldView.getWidthEstimate(getFont(), getItems());
        
        setPreferredWidth(minWidth, samWidth);
    }
    
    
    /**
     * <p>Sets the minimum width for use in the method
     * <code>autoSetPreferredWidth</code>.</p>
     *
     * <p>This minimum width will be interpreted later`as follows.</p>
     *
     * <ul>
     *   <li>If minimum width is positive, it is used as is.</li>
     *   <li>If minimum width is zero, it is replaced by DEFAULT_WIDTH.</li>
     *   <li>If minimum width is negative, it is ignored.</li>
     * </ul>
     */
    public void setMinimumWidth(int minWidth) {
        minimumWidth = minWidth;
    }
    
    
    /**
     * Returns the current minimum width setting.
     */
    public int getMinimumWidth() {
        return minimumWidth;
    }
    
    
    /**
     * <p>Sets the preferred width of text portion of the view to the
     * given width if the given width is positive.</p>
     *
     * <p>If the given width is zero, sets the preferred width of the
     * text portion of the view to DEFAULT_WIDTH.</p>
     *
     * <p>If the given width is negative, sets the preferred width to
     * -1 and the preferred size of the view will be computed entirely
     * from the data of the super class.</p>
     *
     * <p>Refreshes the component in its parent view.</p>
     *
     * <p>It is recommended that the user call the method
     * <code>autoSetPreferredWidth</code> rather than call this method
     * directly.  This method is public for backward compatibility and
     * to permit the user to set an absolute preferred width if that
     * behavior is really desired.</p>
     *
     * <p>Fires property change PREFERRED_WIDTH.</p>
     *
     * @param width the desired preferred width
     */
    public void setPreferredWidth(int width) {
        int oldWidth = getPreferredWidth();
        
        if (width < 0)
            preferredWidth = -1;
        else
        if (width == 0)
            preferredWidth = DEFAULT_WIDTH;
        else
            preferredWidth = width;
        
        refreshComponent();

        // notify listeners of property change
        firePropertyChange(PREFERRED_WIDTH, oldWidth, preferredWidth);
    }
    
    
    /**
     * <p>Sets the preferred width of the text portion of the view using
     * the given minimum width and sample string.</p>
     *
     * <p>This method proceeds as follows.</p>
     *
     * <p>If the sample string is <code>null</code> or of length zero,
     * then the preferred width is set via the call:
     * <code>setPreferredWidth(minWidth)</code>.</p>
     *
     * <p>Otherwise, if minWidth is zero, it is set to DEFAULT_WIDTH.
     * Then, the method <code>getSampleWidth</code> is called using
     * the current font of the view and the sample string.  The
     * preferred width is then set to be the maximum of minWidth and
     * the sample width.</p>
     *
     * <p>Always calls <code>setMinimumWidth(minWidth)</code> to save the
     * minimum width setting.</p>
     *
     * <p>Refreshes the component in its parent view.</p>
     *
     * <p>It is recommended that the user call the method
     * <code>autoSetPreferredWidth</code> rather than call this method
     * directly.  This method is public for backward compatibility and
     * to permit the user to set a width larger than the width of any
     * string installed in the dropdown list.</p>
     *
     * <p>Fires property change PREFERRED_WIDTH.</p>
     *
     * @param minWidth the minimum width
     * @param sample the sample string used to calculate the width
     */
    public void setPreferredWidth(int minWidth, String sample) {
        int samWidth = TextFieldView.getSampleWidth(getFont(), sample);
        
        setPreferredWidth(minWidth, samWidth);
    }
    
    
    /**
     * <p>Sets the preferred width of the text portion of the view to be
     * at least the given minimum width and sufficiently wide to include
     * strings whose width is no greater than the given sample width.</p>
     *
     * <p>Always calls <code>setMinimumWidth(minWidth)</code> to save the
     * minimum width setting.</p>
     *
     * <p>Refreshes the component in its parent view.</p>
     *
     * <p>It is recommended that the user call the method
     * <code>autoSetPreferredWidth</code> rather than call this method
     * directly.  This method is public for backward compatibility and
     * to permit the user to set a width larger than the width of any
     * string installed in the dropdown list.</p>
     *
     * <p>Fires property change PREFERRED_WIDTH.</p>
     *
     * @param minWidth the minimum width
     * @param samWidth the maximum width of one or more sample strings
     * @see #setPreferredWidth(int)
     * @see #setPreferredWidth(int, String)
     */
    public void setPreferredWidth(int minWidth, int samWidth) {
        setMinimumWidth(minWidth);
        
        if (samWidth <= 0) {
            setPreferredWidth(minWidth);
        }
        else {
            if (minWidth == 0)
                minWidth = DEFAULT_WIDTH;
            
            setPreferredWidth(Math.max(minWidth, samWidth));
        }
    }
    
    
    /**
     * <p>Sets the font for this component.</p>
     *
     * <p>If the given font is <code>null</code> then it is set to
     * <code>getDefaultFont()</code>.</p>
     *
     * <p>Fires property change FONT.</p>
     *
     * @param font the font to set for this view
     */
    public void setFont(Font font) {
        // avoid problems during super class initialization
        
        if (itemList == null) {
            super.setFont(font);
            return;
        }
        
        // if needed set the default font
        
        if (font == null)
            font = getDefaultFont();
        
        // reinitialize the drop down to reset all settings
        // except the default view state
        
        String defaultViewState = getDefaultViewState();
        
        String[] items = getItems();
        
        String selection = getViewState();
        
        int minWidth = getMinimumWidth();
        
        boolean editable = isEditable();
        
        removeAllItems();
        
        initializeDropdown(items, selection, font, minWidth, editable);
        
        setDefaultViewState(defaultViewState);
        
        firePropertyChange(FONT, null, null);
    }
    
    
    /**
     * Returns the default font for a JComboBox.
     */
    public static Font getDefaultFont() {
        return new JComboBox().getFont();
    }
    
    
    /**
     * <p>Attempts to set the display area of this view to the given
     * <code>String</code> data.</p>
     *
     * <p>If <code>isEditable()</code> returns <code>true</code> then
     * sets the editable display area to the <code>String</code> data.</p>
     *
     * <p>If <code>isEditable()</code> returns <code>false</code> then
     * changes the display area to the <code>String</code> data only
     * if data is in the dropdown list.</p>
     *
     * <p>In either case, if data is in the dropdown list then will
     * also set the selected index to the first occurence of data in
     * the dropdown list.</p>
     *
     * <p>If data is <code>null</code>, then does nothing.</p>
     *
     * <p>If the current view state already equals data then also does
     * nothing.</p>
     *
     * <p>Fires property change VIEW_STATE.</p>
     *
     * @param data the <code>String</code> to be selected
     * @see #getViewState()
     */
    public void setViewState(String data) {
        if (data == null)
            return;
        
        String olddata = getViewState();
        
        if (data.equals(olddata))
            return;
        
        boolean madeChange = false;
        
        // first check if data is in the dropdown list
        for (int i = 0; i < getItemCount(); i++) {
            String s = getItemAt(i).toString();
            
            if ((s != null) && (s.equals(data))) {
                setSelectedIndex(i);
                madeChange = true;
                break;
            }
        }
        
        // if no change has been made, then check if dropdown is editable
        if (!madeChange && isEditable()) {
            setSelectedItem(data);
            madeChange = true;
        }
        
        // if a change has been made, notify listeners of property change
        if (madeChange)
            firePropertyChange(VIEW_STATE, olddata, data);
    }
    
    
    /**
     * Returns the current contents of the view display.
     *
     * @see #setViewState(String)
     */
    public String getViewState() {
        return (String)getSelectedItem();
    }
    
    
    /**
     * Sets the default view state for this object to the data
     * state represented by the given <CODE>String</CODE> data.
     *
     * @param data the new default data state for this object
     * @see #getDefaultViewState()
     * @see #reset()
     */
    public void setDefaultViewState(String data) {
        String oldDefaultViewState = defaultViewState;
        
        data = (data == null) ? "" : data;

        defaultViewState = data;
        
        if (! oldDefaultViewState.equals(defaultViewState))
            firePropertyChange(DEFAULT_VIEW_STATE, 0, 1);
    }
    
    
    /**
     * Returns a <CODE>String</CODE> representation of the default
     * view state for this object.
     *
     * @return the default view state as a <CODE>String</CODE>
     * @see #setDefaultViewState(String)
     * @see #reset()
     */
    public String getDefaultViewState() {
        return defaultViewState;
    }
    
    
    /**
     * Resets the view state of this object to the default view state for
     * this object.
     */
    public void reset() {
        setViewState(getDefaultViewState());
    }
    
    
    /**
     * <p>Refreshes the component in its parent window
     * by calling <code>autoSetPreferredWidth()</code>
     * and then repacking the parent window.<p>
     */
    public void refresh() {
        autoSetPreferredWidth();
        refreshComponent();
    }
    
    
    /**
     * Refreshes the component by repacking the parent window.
     */
    public void refreshComponent() {
        Refresh.packParentWindow(this);
    }
    
}
