/*
* Ext GWT - Ext for GWT
* Copyright(c) 2007-2009, Ext JS, LLC.
* licensing@extjs.com
*
* http://extjs.com/license
*/
package com.extjs.gxt.ui.client.widget.form;
import java.util.ArrayList;
import java.util.List;
import com.extjs.gxt.ui.client.GXT;
import com.extjs.gxt.ui.client.event.FieldEvent;
import com.extjs.gxt.ui.client.util.Format;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.i18n.client.constants.NumberConstants;
import com.google.gwt.user.client.Element;
/**
* Numeric text field that provides automatic keystroke filtering and numeric
* validation.
*
* <p>
* When the field wraps any thing other than Double, either
* {@link #setPropertyEditorType(Class)} or
* {@link #setPropertyEditor(PropertyEditor)} should be called with the
* appropriate number type.
*
* <code><pre>
* NumberField<Integer> field = new NumberField<Integer>;
* field.setPropertyEdtiorType(Integer.class);
* </pre></code>
*
* <dl>
* <dt>Inherited Events:</dt>
* <dd>Field Focus</dd>
* <dd>Field Blur</dd>
* <dd>Field Change</dd>
* <dd>Field Invalid</dd>
* <dd>Field Valid</dd>
* <dd>Field KeyPress</dd>
* <dd>Field SpecialKey</dd>
* </dl>
*/
public class NumberField extends TextField<Number> {
/**
* NumberField messages.
*/
public class NumberFieldMessages extends TextFieldMessages {
private String maxText;
private String minText;
private String nanText;
private String negativeText = GXT.MESSAGES.numberField_negativeText();
/**
* Returns the max error text.
*
* @return the error text
*/
public String getMaxText() {
return maxText;
}
/**
* Returns the minimum error text.
*
* @return the minimum error text
*/
public String getMinText() {
return minText;
}
/**
* Returns the not a number error text.
*
* @return the not a number error text
*/
public String getNanText() {
return nanText;
}
/**
* Returns the negative error text.
*
* @return the error text
*/
public String getNegativeText() {
return negativeText;
}
/**
* Error text to display if the maximum value validation fails (defaults to
* "The maximum value for this field is {maxValue}").
*
* @param maxText the max error text
*/
public void setMaxText(String maxText) {
this.maxText = maxText;
}
/**
* Sets the Error text to display if the minimum value validation fails
* (defaults to "The minimum value for this field is {minValue}").
*
* @param minText min error text
*/
public void setMinText(String minText) {
this.minText = minText;
}
/**
* Sets the error text to display if the value is not a valid number. For
* example, this can happen if a valid character like '.' or '-' is left in
* the field with no number (defaults to "{value} is not a valid number").
*
* @param nanText the not a number text
*/
public void setNanText(String nanText) {
this.nanText = nanText;
}
/**
* Sets the negative error text (defaults to 'The value must be greater or
* equal to 0').
*
* @param negativeText the error text
*/
public void setNegativeText(String negativeText) {
this.negativeText = negativeText;
}
}
private boolean allowDecimals = true;
private List<Character> allowed;
private boolean allowNegative = true;
private String baseChars = "0123456789";
private NumberConstants constants = (NumberConstants) GWT.create(NumberConstants.class);
private String decimalSeparator = ".";
private int lastKeyCode;
private Number maxValue = Double.MAX_VALUE;
private Number minValue = Double.NEGATIVE_INFINITY;
/**
* Creates a new number field.
*/
public NumberField() {
messages = new NumberFieldMessages();
propertyEditor = new NumberPropertyEditor();
decimalSeparator = constants.decimalSeparator();
}
/**
* Returns true of decimal values are allowed.
*
* @return the allow decimal state
*/
public boolean getAllowDecimals() {
return allowDecimals;
}
/**
* Returns true if negative values are allowed.
*
* @return the allow negative value state
*/
public boolean getAllowNegative() {
return allowNegative;
}
/**
* Returns the base characters.
*
* @return the base characters
*/
public String getBaseChars() {
return baseChars;
}
/**
* Returns the field's number format.
*
* @return the number format
*/
public NumberFormat getFormat() {
return getPropertyEditor().getFormat();
}
/**
* Returns the fields max value.
*
* @return the max value
*/
public Number getMaxValue() {
return maxValue;
}
@Override
public NumberFieldMessages getMessages() {
return (NumberFieldMessages) messages;
}
/**
* Returns the field's minimum value.
*
* @return the min value
*/
public Number getMinValue() {
return minValue;
}
@Override
public NumberPropertyEditor getPropertyEditor() {
return (NumberPropertyEditor) propertyEditor;
}
/**
* Returns the number property editor number type.
*
* @see NumberPropertyEditor#setType(Class)
* @return the number type
*/
public Class<?> getPropertyEditorType() {
return getPropertyEditor().getType();
}
/**
* Sets whether decimal value are allowed (defaults to true).
*
* @param allowDecimals true to allow negative values
*/
public void setAllowDecimals(boolean allowDecimals) {
this.allowDecimals = allowDecimals;
}
/**
* Sets whether negative value are allowed.
*
* @param allowNegative true to allow negative values
*/
public void setAllowNegative(boolean allowNegative) {
this.allowNegative = allowNegative;
}
/**
* Sets the base set of characters to evaluate as valid numbers (defaults to
* '0123456789').
*
* @param baseChars the base character
*/
public void setBaseChars(String baseChars) {
assertPreRender();
this.baseChars = baseChars;
}
/**
* Sets the cell's number formatter.
*
* @param format the format
*/
public void setFormat(NumberFormat format) {
getPropertyEditor().setFormat(format);
}
/**
* Sets the field's max allowable value.
*
* @param maxValue the max value
*/
public void setMaxValue(Number maxValue) {
this.maxValue = maxValue.doubleValue();
}
/**
* Sets the field's minimum allowed value.
*
* @param minValue the min value
*/
public void setMinValue(Number minValue) {
this.minValue = minValue.doubleValue();
}
/**
* Specifies the number type used when converting a String to a Number
* instance (defaults to Double).
*
* @param type the number type (Short, Integer, Long, Float, Double).
*/
public void setPropertyEditorType(Class<?> type) {
getPropertyEditor().setType(type);
}
@Override
protected void onKeyDown(FieldEvent fe) {
super.onKeyDown(fe);
// must key code in key code as gwt returns character in key press
lastKeyCode = fe.getKeyCode();
}
@Override
protected void onKeyPress(FieldEvent fe) {
super.onKeyPress(fe);
char key = (char) fe.getKeyCode();
if (fe.isSpecialKey(lastKeyCode) || lastKeyCode == KeyCodes.KEY_BACKSPACE
|| lastKeyCode == KeyCodes.KEY_DELETE || fe.isControlKey()) {
return;
}
if (!allowed.contains(key)) {
fe.stopEvent();
}
}
@Override
protected void onRender(Element target, int index) {
super.onRender(target, index);
allowed = new ArrayList<Character>();
for (int i = 0; i < baseChars.length(); i++) {
allowed.add(baseChars.charAt(i));
}
if (allowNegative) {
allowed.add('-');
}
if (allowDecimals) {
for (int i = 0; i < decimalSeparator.length(); i++) {
allowed.add(decimalSeparator.charAt(i));
}
}
}
@Override
protected boolean validateValue(String value) {
// validator should run after super rules
Validator tv = validator;
validator = null;
if (!super.validateValue(value)) {
validator = tv;
return false;
}
validator = tv;
if (value.length() < 1) { // if it's blank and textfield didn't flag it then
// its valid it's valid
return true;
}
String v = value;
Number d = null;
try {
d = getPropertyEditor().convertStringValue(v);
} catch (Exception e) {
//TODO
System.out.println(e);
String error = "";
if (getMessages().getNanText() == null) {
error = GXT.MESSAGES.numberField_nanText(v);
} else {
error = Format.substitute(getMessages().getNanText(), v);
}
markInvalid(error);
return false;
}
if (d.doubleValue() < minValue.doubleValue()) {
String error = "";
if (getMessages().getMinText() == null) {
error = GXT.MESSAGES.numberField_minText(minValue.doubleValue());
} else {
error = Format.substitute(getMessages().getMinText(), minValue);
}
markInvalid(error);
return false;
}
if (d.doubleValue() > maxValue.doubleValue()) {
String error = "";
if (getMessages().getMaxText() == null) {
error = GXT.MESSAGES.numberField_maxText(maxValue.doubleValue());
} else {
error = Format.substitute(getMessages().getMaxText(), maxValue);
}
markInvalid(error);
return false;
}
if (!allowNegative && d.doubleValue() < 0) {
markInvalid(getMessages().getNegativeText());
return false;
}
if (validator != null) {
String msg = validator.validate(this, value);
if (msg != null) {
markInvalid(msg);
return false;
}
}
return true;
}
}