/*
* Copyright (C) 2009 Quadduc <quadduc@gmail.com>
*
* This file is part of LibMaker.
* LibMaker is free software and comes with ABSOLUTELY NO WARRANTY.
* See LICENSE for details.
*/
package org.lateralgm.libmaker.components;
import java.awt.event.FocusEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.NumberFormat;
import java.text.ParseException;
import javax.swing.JFormattedTextField;
import javax.swing.text.Caret;
import javax.swing.text.NumberFormatter;
public class NumberField extends JFormattedTextField
{
private static final long serialVersionUID = 1L;
protected final NumberFormatter formatter;
public NumberField(int value)
{
this(Integer.MIN_VALUE,Integer.MAX_VALUE,value);
}
public NumberField(int min, int max)
{
this(min,max,0);
}
public NumberField(int min, int max, int value)
{
this(min,max,value,getFormatter(getIntegerFormat()));
setColumns(1 + Math.max(numDigits(min),numDigits(max)));
}
public NumberField(double min, double max, double value)
{
this(min,max,value,getFormatter(getNumberFormat()));
}
public <T extends Number & Comparable<T>> NumberField(T min, T max, T value, NumberFormatter f)
{
super(f);
formatter = f;
f.setMinimum(min);
f.setMaximum(max);
if (value != null)
setValue(value.compareTo(min) < 0 ? min : value.compareTo(max) > 0 ? max : value);
}
public <T extends Number & Comparable<T>>void setRange(T min, T max)
{
formatter.setMinimum(min);
formatter.setMaximum(max);
commitOrRevert();
}
public Integer getIntValue()
{
return (Integer) getValue();
}
public void revertEdit()
{
setValue(getValue());
}
public void setCommitsOnValidEdit(boolean val)
{
formatter.setCommitsOnValidEdit(val);
}
public void commitOrRevert()
{
try
{
commitEdit();
}
catch (ParseException e)
{
revertEdit();
}
}
private PropertyChangeListener valListener = null;
/**
* equivalent to registering a <code>PropertyChangeListener</code> on <code>value</code>.
* @param l
*/
public void addValueChangeListener(ValueChangeListener listener)
{
if (valListener == null)
{
valListener = new PropertyChangeListener()
{
public void propertyChange(PropertyChangeEvent evt)
{
fireValueChange(evt.getOldValue(),evt.getNewValue());
}
};
super.addPropertyChangeListener("value",valListener); //$NON-NLS-1$
}
listenerList.add(ValueChangeListener.class,listener);
// listener.valueChange(new ValueChangeEvent(this,getValue(),getValue()));
}
public void removeValueChangeListener(ValueChangeListener listener)
{
listenerList.remove(ValueChangeListener.class,listener);
}
protected void fireValueChange(Object oldValue, Object newValue)
{
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length - 2; i >= 0; i -= 2)
if (listeners[i] == ValueChangeListener.class)
((ValueChangeListener) listeners[i + 1]).valueChange(new ValueChangeEvent(this,oldValue,
newValue));
}
//This is a workaround for a java bug causing the caret to jump to 0
//on focus gain since the value is recalculated. bug #4740914 (rejected)
protected void processFocusEvent(FocusEvent e)
{
if (e.getID() == FocusEvent.FOCUS_GAINED)
{
Caret c = getCaret();
int cd = getCaret().getDot();
int cm = getCaret().getMark();
super.processFocusEvent(e);
//Assumes this won't go out of bounds (e.g. text didn't change).
//This is normally a safe assumption, since it seems like the value can't change.
c.setDot(cm);
c.moveDot(cd);
}
else
super.processFocusEvent(e);
}
private static NumberFormatter getFormatter(NumberFormat f)
{
NumberFormatter nf = new NumberFormatter(f);
nf.setCommitsOnValidEdit(true);
return nf;
}
private static NumberFormat getIntegerFormat()
{
NumberFormat f = NumberFormat.getIntegerInstance();
f.setGroupingUsed(false);
return f;
}
private static NumberFormat getNumberFormat()
{
NumberFormat f = NumberFormat.getNumberInstance();
f.setGroupingUsed(false);
return f;
}
private static int numDigits(int n)
{
return n == 0 ? 1 : 1 + (int) Math.log10(Math.abs(n));
}
public static interface ValueChangeListener extends java.util.EventListener
{
void valueChange(ValueChangeEvent evt);
}
public static class ValueChangeEvent extends java.util.EventObject
{
private static final long serialVersionUID = 1L;
private Object oldValue, newValue;
public ValueChangeEvent(Object source, Object oldValue, Object newValue)
{
super(source);
this.oldValue = oldValue;
this.newValue = newValue;
}
public Object getOldValue()
{
return oldValue;
}
public Object getNewValue()
{
return newValue;
}
}
}