/* $Id$
 *
 * Copyright (c) 1999 Xenonsoft Limited. All Rights Reserved.
 *
 * This software is protected by copyright. You are hereby notified from
 * now by reading this message. This software is also the confidential 
 * and proprietary information of Xenonsoft Limited.  ("Confidential
 * Information"). 
 *
 * This software is distributed under the Xenonsoft Public end user 
 * License ("XPeL"), where the machine-readable source code is provided 
 * under the "Open Source" model.
 * For more information, please read the file "LICENSE-XPL.txt" 
 */

// WholeNumberField.java

//
// Description:
//	A specialized JTextField that only allows whole numbers to be entered
//
// Author:
//	Somebody at JavaSoft
//	Surf to the Swing Tutorial
//	http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
//
// RCS HEADER 
// 
// $Author$
// $Date$
// $Source$
// $Revision$    $State$    $Locker$
//
// ================================================================================
// History
//	  $Log$
// 
// 
//

package xenon.soundkit.gui;

import java.beans.*;				// for PropertyChangeSupport
import javax.swing.*;				// for JTextField
import javax.swing.event.*;			// for SwingPropertyChangeSupport
import javax.swing.text.*;			// for Document

import java.awt.Toolkit;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;

public class WholeNumberField extends JTextField 
{
    public final static String	VALUE_CHANGED_PROPERTY="value_changed";

    protected Toolkit		toolkit;
    protected NumberFormat	integerFormatter;
    private int			minimum;
    private int			maximum;
    private SwingPropertyChangeSupport changeSupport=null;
    
    protected void checkLimits()
    {
	if (minimum > maximum )
	    new IllegalArgumentException( 
		"impossible bounded range; min:"+minimum+" > max:"+maximum );
    }
    
    /**
     * Constructs a whole number field with infinite limits
     * with 10 columns
     *
     * @param value the value
     */
    public WholeNumberField( int value ) 
    {
	this( value, 10 );
    }

    /**
     * Constructs a whole number field with infinite limits
     *
     * @param value the value
     * @param columns the number of columns to display
     */
    public WholeNumberField( int value, int columns ) 
    {
	this( Integer.MIN_VALUE, value, Integer.MAX_VALUE, columns );
    }

    /**
     * Constructs a whole number field with specified limits
     *
     * @param minval the minimum value
     * @param minval the value
     * @param maxval the maximum value
     * @param columns the number of columns to display
     */
    public WholeNumberField( int minval, int value, int maxval, int columns ) 
    {
        super(columns);

        toolkit = Toolkit.getDefaultToolkit();
        integerFormatter = NumberFormat.getNumberInstance(Locale.US);
        integerFormatter.setParseIntegerOnly(true);

	setMinimum( minval );
	setMaximum( maxval );
	checkLimits();
	if ( value < minimum || value > maximum )
	    throw new IllegalArgumentException( 
		"value:"+value +" out of range ["+minimum+","+maximum+"]" );
        setValue(value);
    }

    public int getMinimum() 
    {
	return (minimum);
    }
    
    public void setMinimum( int newMinimum ) 
    {
	minimum = newMinimum;
    }
    
    public int getMaximum() 
    {
	return (maximum);
    }
    
    public void setMaximum( int newMaximum ) 
    {
	maximum = newMaximum;
    }
    
    public int getValue() 
    {
        int retVal = Integer.MIN_VALUE;
        try {
            int tmpVal = integerFormatter.parse(getText()).intValue();
	    retVal = tmpVal;
        } 
	catch (ParseException e) {
            // This should never happen because insertString allows
            // only properly formatted data to get in the field.
            // toolkit.beep();
        }
        return retVal;
    }

    public void setValue(int value) 
    {
	if (value < minimum) 
	    value = minimum;
	else if (value > maximum) 
	    value = maximum;
        setText(integerFormatter.format(value));
    }

    protected Document createDefaultModel() 
    {
        return new WholeNumberDocument();
    }

    public void addPropertyChangeListener( PropertyChangeListener l )
    {
	if ( changeSupport == null) 
	    changeSupport = new SwingPropertyChangeSupport(this);
	changeSupport.addPropertyChangeListener(l);
    }
    
    public void removePropertyChangeListener( PropertyChangeListener l )
    {
	if ( changeSupport != null) 
	    changeSupport.removePropertyChangeListener(l);
    }

    protected void firePropertyChange( 
	String propertyName, Object oldValue, Object newValue )
    {
	if ( changeSupport != null) 
	    changeSupport.firePropertyChange( propertyName, oldValue, newValue );
    }
    
    

    // ======================================================================
    // I N N E R  C L A S S 
    // ======================================================================

    protected class WholeNumberDocument extends PlainDocument 
    {
        public void insertString(int offs, 
                                 String str,
                                 AttributeSet a) 
	    throws BadLocationException 
	{
            char[] source = str.toCharArray();
            char[] result = new char[source.length];
            int j = 0;
	    boolean errors=false;
	    
            for (int i = 0; i < result.length; i++) {
		char ch = source[i];
                if (Character.isDigit(ch) || ( i==0 && ch == '-' ))
                    result[j++] = ch;
                else {
		    errors = true;
                    toolkit.beep();
                    // DEBUG: System.err.println("insertString: " + source[i]);
                }
            }
	    super.insertString(offs, new String(result, 0, j), a);

	    checkLimits();
	    int value = getValue();
	    if (value < minimum) {
		value = minimum;
		setValue(value);
	    }
	    else if (value > maximum) {
		value = maximum;
		setValue(value);
	    }
	    
	    if ( !errors && value != Integer.MIN_VALUE ) {
		firePropertyChange( VALUE_CHANGED_PROPERTY, null, new Integer(value) );
	    }
        }


//	  public void remove( int offset, int len ) 
//	      throws BadLocationException
//	  {
//	      super.remove( offset, len );
//	      int value = getValue();
//	      firePropertyChange( VALUE_CHANGED_PROPERTY, null, new Integer(value) );
//	  }
    }

}

// fini
