import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.util.*;

/**
 * StringSpinControl.
 * This component creates a spin-control (incr/decr of a list of values accessed
 *  by two arrows (one incr, one decr)) for use in such things as getting user
 *  input relating to dates and values.  This spin control only deals with strings
 *
  *	Copyright (C) 2000  Shazron Abdullah
 *	  
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA * 
 * @author Shazron Abdullah (implementation)
 * @date 1999/01/18
 *
 **/
public class StringSpinControl extends JPanel implements ActionListener
{
	private ArrowButton 	upArrow,downArrow;
	private JTextField 	    valueDisplay;
	private int			    currentIndex;
	private Vector		    valueList;

	//private static int _defaultWidth  = 60;
	//private static int _defaultHeight = 30;
	//private static Dimension _defaultDimension = new Dimension( _defaultWidth, _defaultHeight );

	private static final int HEIGHT_PADDING = 5;
	private static final int TEXT_WIDTH_PADDING  = 5;
	private static final int ARROW_WIDTH    = 7;
	private static final int COMPONENT_HEIGHT = 24;
	
	/**
	 *
	 * Constructor method which takes in a set of values and prepares the
	 *  control for use.
	 * 
	 * @value theValues a vector of strings which can be displayed and iterated through by the spincontrol
	 *
	 **/
	public StringSpinControl(Vector theValues)
	{
		valueList = theValues;
		currentIndex = 0;
		
		// Create the buttons and the displayField
		createControls();
		setupEventListeners();

		// set the currentValueStr to the first item in the vector			
		if ( valueList != null ) {
			setValue ( (String) valueList.elementAt(0) );
		}

		// set it to a default size
		//setSize(_defaultDimension);
		determineSizeOfControl();
	}

	/**
	 * Determine the size of this, by getting the width of the 
	 * longest string to display
	 */
	private void determineSizeOfControl()
	{
		int textfield_width = 0;
		FontMetrics myFontMetrics = valueDisplay.getFontMetrics(valueDisplay.getFont());

		// iterate through the list, get the largest width
		for (Enumeration e = valueList.elements(); e.hasMoreElements();){
			String theString = (String) e.nextElement();
			textfield_width = Math.max( textfield_width, myFontMetrics.stringWidth(theString) );
		}

		//System.out.println(textfield_width);

		int final_width  = textfield_width + ( 2 * TEXT_WIDTH_PADDING ) + ARROW_WIDTH;
		int final_height = myFontMetrics.getHeight() + HEIGHT_PADDING; 

		// by now we will have the max width
		setSize( new Dimension(final_width, final_height));
		
		setPreferredSize(new Dimension(final_width,COMPONENT_HEIGHT));
		//setBorder(BorderFactory.createLineBorder(Color.green));
	}
	
	/**
	 * Create the controls for this component.
	 */
	private void createControls()
	{
		GridBagLayout gridbag = new GridBagLayout();
		GridBagConstraints gc = new GridBagConstraints();
		setLayout(gridbag);
		gc.fill = GridBagConstraints.BOTH;

		/// --- Init the UP arrow
		upArrow = new ArrowButton(SwingConstants.NORTH);
		gc.anchor = GridBagConstraints.SOUTHWEST;
		gc.ipadx = ARROW_WIDTH/2;
       	gc.gridx = 1;       
       	gc.gridy = 0;
       	gc.weighty = 0.5;   //request any extra vertical space
		gc.weightx = 0.0;   //request any extra horiz space
       	gc.gridwidth = 1;   //1 column wide
		gridbag.setConstraints(upArrow, gc);
		this.add(upArrow);

		/// --- Init the DOWN arrow
		downArrow = new ArrowButton(SwingConstants.SOUTH);
		gc.anchor = GridBagConstraints.NORTHWEST;
        gc.gridx = 1;       
       	gc.gridy = 1;
       	gc.weighty = 0.5;   //request any extra vertical space
		gc.weightx = 0.0;   //request any extra horiz space
       	gc.gridwidth = 1;   //1 column wide
		gridbag.setConstraints(downArrow, gc);
		this.add(downArrow);

		/// --- Init the text field
		valueDisplay = new JTextField();
		
		gc.anchor = GridBagConstraints.EAST;
		gc.ipadx = TEXT_WIDTH_PADDING;
        gc.gridx = 0;       
        gc.gridy = 0;
       	gc.weighty = 1.0;   //request any extra vertical space
		gc.weightx = 1.0;   //request any extra horiz space
       	gc.gridwidth = 1;   //1 column wide
		gc.gridheight = 2;	//2 rows deep
		gridbag.setConstraints(valueDisplay, gc);
		this.add(valueDisplay);
	}

	/**
	 *
	 */
	private void setupEventListeners()
	{
		upArrow.addActionListener(this);
		downArrow.addActionListener(this);
	}

	/**
	 * Set the String value in the textfield.
	 */
	public void setValue(String value)
	{
		valueDisplay.setText(value);
	}

	/**
	 * Get the value in the textfield.
	 */
	public String getValue()
	{
		return valueDisplay.getText();	
	}

	/**
	 * Adds a value to the end of the list.
	 */
	public void addListValue(String value)
	{
		valueList.addElement(value);
	}
	
	/**
	 * Sets the list value at the index to the value provided. 
	 */
	public void setListValueAt(int index, String value) throws ArrayIndexOutOfBoundsException
	{
		valueList.setElementAt(value, index);
	}

	/**
	 * Removes the list value from the list.
	 *
	 * @return true if the value was in the list, false if not.
	 */
	public boolean removeListValue(String value)
	{
		int objIndex = valueList.indexOf(value);
		boolean retVal = false;

		if ( objIndex != -1 ) { // found
			retVal = valueList.removeElement(value);
			if ( objIndex > getMaxIndex() ) {
				currentIndex = getMaxIndex();
			}
		}
		return retVal;
	}

	/**
	 * Removes the list value from the list.
	 *
	 */
	public void removeListValueAt(int value) throws ArrayIndexOutOfBoundsException
	{
		valueList.removeElementAt(value);
		if ( value > getMaxIndex() ) {
			currentIndex = getMaxIndex();
		}
	}

	/**
	 *
	 * Get the number of values available.
	 *
	 **/	
	public int getMaxIndex()
	{
		return Math.max( (valueList.size() - 1), 0 );
	}

	/**
	 *
	 * Get the value from a specified index point.
	 *
	 **/	
	public String getListValueAt(int index)
	{
		return (String) valueList.elementAt(index);	
	}

	/**
	 * Up arrow button handler.
	 */
	private void upArrow_Clicked(ActionEvent e)
	{
		/// --- Check if the value in the textfield is a valid value,
		/// --- increment to next index
		int index = valueList.indexOf(getValue()) ; 

		if ( index != -1 ){ currentIndex = index; }
		
		if ( currentIndex != getMaxIndex() ) {
			currentIndex++;
			setValue( (String) valueList.elementAt(currentIndex) );
		}
	}

	/**
	 * Down arrow button handler.
	 */
	private void downArrow_Clicked(ActionEvent e)
	{
		/// --- Check if the value in the textfield is a valid value,
		/// --- increment to next index
		int index = valueList.indexOf(getValue()) ; 

		if ( index != -1 ) { currentIndex = index; }
		if ( currentIndex != 0 ) {
			currentIndex--;
			setValue( (String) valueList.elementAt(currentIndex) );
		}
	}

	/**
	 * ActionListener implementation.
	 */
	public void actionPerformed(ActionEvent evt)
	{
		Object source = evt.getSource();

		if (source instanceof ArrowButton) {
			ArrowButton pab = (ArrowButton) source;

			if ( pab.equals(upArrow) ) {
				upArrow_Clicked(evt);
			} else if ( pab.equals(downArrow) ) {
				downArrow_Clicked(evt);
			}
		}
	}

	// Testing purpose
	public static void main(String args[])
	{
		JFrame j = new JFrame();
		Vector myStrings = new Vector();
		myStrings.addElement("jan");
		myStrings.addElement("feb");
		myStrings.addElement("mar");
		myStrings.addElement("apr");
		myStrings.addElement("may");
		myStrings.addElement("jun");
		myStrings.addElement("jul");
		myStrings.addElement("aug");
		myStrings.addElement("sep");
		myStrings.addElement("oct");
		myStrings.addElement("nov");
		myStrings.addElement("december");

		StringSpinControl psc = new StringSpinControl(myStrings);
		psc.addListValue("foo");
		psc.addListValue("bar");
		//psc.setSize(40,30);

		j.getContentPane().setLayout(null);
		j.getContentPane().add(psc);
		j.setSize(new Dimension(100,100));

		j.setVisible(true);
	}
}
