/* This file is part of the source code for 3D-XplorMath-J, Version 1.0 (January 2008).
* Copyright (c) 2008 The 3D-XplorMath Consortium (http://3d-xplormath.org).
* This source code is released under a BSD License, which allows redistribution
* in source and binary form, with or without modification, provided copyright
* and license information are included, and with no warranty or guarantee of
* any kind. For details, see http://3d-xplormath.org/j/source/BSDLicense.txt
*/
package vmm.core;
import javax.swing.JTextField;
import vmm.functions.ParseError;
import vmm.functions.Parser;
/**
* An input box that lets the user enter a value for a {@link vmm.core.Parameter}.
* A ParameterInput box can be used to get a value for a parameter, or the animation start
* value or animation end value for an {@link vmm.core.Animateable} parameter.
*/
public class ParameterInput extends JTextField {
private Parameter param; // the parameter whose value is to be input
private int inputType;
private String originalString;
private static Parser parser = new Parser();
/**
* Indicates that this input box is used to input a parameter's value.
* This constant is for use in the constructor {@link #ParameterInput(Parameter, int)}.
*/
public final static int VALUE = 0;
/**
* Indicates that this input box is used to input the animation start value for an Animateable parameter.
* This constant is for use in the constructor {@link #ParameterInput(Parameter, int)}.
*/
public final static int ANIMATION_START = 1;
/**
* Indicates that this input box is used to input the animation end value for an Animateable parameter.
* This constant is for use in the constructor {@link #ParameterInput(Parameter, int)}.
*/
public final static int ANIMATION_END = 2;
/**
* Creates a text box for inputting the value of a specified parameter.
* @param param The non-null parameter whose value is to be input.
*/
public ParameterInput(Parameter param) {
this(param, VALUE);
}
/**
* Creates a text box for inputting the value, animation start value, or animation end value
* of a specified parameter. Animation start and end values can only be input for animated
* parameters such as {@link RealParamAnimateable}.
* @param param The non-null parameter whose value or other property is to be input.
* @param propertyToInput The property of the parameter that is to be input. This must be one of:
* ParameterInput.VALUE, ParameterInput.ANIMATION_START, or ParameterInput.ANIMATION_END.
* Illegal vaues have the same effect as ParameterInput.VALUE.
*/
public ParameterInput(Parameter param, int propertyToInput) {
super(12);
this.param = param;
if (param instanceof Animateable ) {
inputType = propertyToInput;
if (inputType != VALUE && inputType != ANIMATION_END && inputType != ANIMATION_START)
inputType = VALUE;
switch (inputType) {
case VALUE:
originalString = param.getValueAsString();
break;
case ANIMATION_START:
originalString = ((Animateable)param).getAnimationStartAsString();
break;
default:
originalString = ((Animateable)param).getAnimationEndAsString();
break;
}
}
else {
inputType = VALUE;
originalString = param.getValueAsString();
}
setText(originalString);
String tooltip = null;
if (param instanceof IntegerParam) {
tooltip = "" + I18n.tr("vmm.core.ParameterInput.isInteger");
int min = ((IntegerParam)param).getMinimumValueForInput();
int max = ((IntegerParam)param).getMaximumValueForInput();
if (max == Integer.MAX_VALUE) {
if (min == 1)
tooltip += "
" + I18n.tr("vmm.core.ParameterInput.positive");
else if (min == 0)
tooltip += "
" + I18n.tr("vmm.core.ParameterInput.nonnegative");
else if (min > Integer.MIN_VALUE)
tooltip += "
" + I18n.tr("vmm.core.ParameterInput.greater",min);
}
else if (min == Integer.MIN_VALUE)
tooltip += "
" + I18n.tr("vmm.core.ParameterInput.less",max);
else
tooltip += "
" + I18n.tr("vmm.core.ParameterInput.range",min,max);
}
else if (param instanceof RealParam) {
tooltip = "" + I18n.tr("vmm.core.ParameterInput.isReal");
double min = ((RealParam)param).getMinimumValueForInput();
double max = ((RealParam)param).getMaximumValueForInput();
if (max == Double.POSITIVE_INFINITY) {
if (min == Double.MIN_VALUE)
tooltip += "
" + I18n.tr("vmm.core.ParameterInput.positive");
else if (min == 0)
tooltip += "
" + I18n.tr("vmm.core.ParameterInput.nonnegative");
else if (min > Double.NEGATIVE_INFINITY)
tooltip += "
" + I18n.tr("vmm.core.ParameterInput.greater",min);
}
else if (min == Double.NEGATIVE_INFINITY)
tooltip += "
" + I18n.tr("vmm.core.ParameterInput.less",max);
else
tooltip += "
" + I18n.tr("vmm.core.ParameterInput.range",min,max);
}
else if (param instanceof ComplexParam) {
tooltip = "" + I18n.tr("vmm.core.ParameterInput.isComplex");
Complex min = ((ComplexParam)param).getMinimumValueForInput();
Complex max = ((ComplexParam)param).getMaximumValueForInput();
if (min.re == Double.NEGATIVE_INFINITY && max.re < Double.POSITIVE_INFINITY)
tooltip += "
" + I18n.tr("vmm.core.ParameterInput.less.realpart",max.re);
else if (min.re > Double.NEGATIVE_INFINITY && max.re == Double.POSITIVE_INFINITY)
tooltip += "
" + I18n.tr("vmm.core.ParameterInput.greater.realpart",min.re);
else if (min.re > Double.NEGATIVE_INFINITY && max.re < Double.POSITIVE_INFINITY)
tooltip += "
" + I18n.tr("vmm.core.ParameterInput.range.realpart",min.re,max.re);
if (min.im == Double.NEGATIVE_INFINITY && max.im < Double.POSITIVE_INFINITY)
tooltip += "
" + I18n.tr("vmm.core.ParameterInput.less.imaginarypart",max.im);
else if (min.im > Double.NEGATIVE_INFINITY && max.im == Double.POSITIVE_INFINITY)
tooltip += "
" + I18n.tr("vmm.core.ParameterInput.greater.imaginarypart",min.im);
else if (min.im > Double.NEGATIVE_INFINITY && max.im < Double.POSITIVE_INFINITY)
tooltip += "
" + I18n.tr("vmm.core.ParameterInput.range.imaginarypart",min.im,max.im);
}
setToolTipText(tooltip);
}
/**
* Sets the preferred size to the specified number of columns. In addtion, if the content
* has not been modified and its length is too big for the preferred size, then the content
* is truncated to a size that will fit in the specified number of columns, if possible.
* (The content is changed only in the case of a RealParam.)
*/
public void setColumns(int columns) {
super.setColumns(columns);
String content = getText();
if (content.length() < columns || !content.equals(originalString) || !(param instanceof RealParam))
return;
double value;
switch (inputType) {
case VALUE:
value = ((RealParam)param).getValue();
break;
case ANIMATION_START:
value = ((RealParamAnimateable)param).getAnimationStart();
break;
default:
value = ((RealParamAnimateable)param).getAnimationEnd();
break;
}
String str = String.format("%"+columns+"g",value);
setText(str);
originalString = str;
}
/**
* Returns the parameter whose value is input by this ParameterInput.
*/
public Parameter getParameter() {
return param;
}
/**
* Checks whether the contents of the text box represent a legal value for the parameter.
* Calling this method does not change the value of the parameter.
* @return returns null if the string in the text box is a legal value for the parameter, or an
* error message if the content is not legal
* @see #setValueFromContents()
*/
public String checkContents() {
String contents = getText();
double val = 0;
Complex cval = null;
try {
if (param instanceof ComplexParam)
cval = parser.parseComplexExpression(contents).value();
else
val = parser.parse(contents).value();
}
catch (ParseError e) {
requestFocus();
selectAll();
if (inputType == VALUE)
return I18n.tr("vmm.core.ParameterInput.badExpression", param.getTitle(), contents, e.getMessage() );
else if (inputType == ANIMATION_START)
return I18n.tr("vmm.core.ParameterInput.badStartExpression", param.getTitle(), contents, e.getMessage() );
else
return I18n.tr("vmm.core.ParameterInput.badEndExpression", param.getTitle(), contents, e.getMessage() );
}
if ( (cval == null && (Double.isNaN(val) || Double.isInfinite(val))) ||
(cval != null && (Double.isNaN(cval.re) || Double.isInfinite(cval.re) || Double.isNaN(cval.im) || Double.isInfinite(cval.im))) ){
requestFocus();
selectAll();
if (inputType == VALUE)
return I18n.tr("vmm.core.ParameterInput.undefinedExpression", param.getTitle(), contents);
else if (inputType == ANIMATION_START)
return I18n.tr("vmm.core.ParameterInput.undefinedStartExpression", param.getTitle(), contents);
else
return I18n.tr("vmm.core.ParameterInput.undefinedEndExpression", param.getTitle(), contents);
}
if (param instanceof IntegerParam) {
IntegerParam intParam = (IntegerParam)param;
int intval;
if (val > 0)
intval = (int)(val + 1e-8);
else
intval = (int)(val - 1e-8);
if (Math.abs(val - intval) > 5e-9) {
requestFocus();
selectAll();
return I18n.tr("vmm.core.ParameterInput.badint", param.getTitle());
}
if (intval < intParam.getMinimumValueForInput() || intval > intParam.getMaximumValueForInput()) {
requestFocus();
selectAll();
return I18n.tr("vmm.core.ParameterInput.intOutOfRange", param.getTitle(),
""+intParam.getMinimumValueForInput(),""+intParam.getMaximumValueForInput());
}
}
else if (param instanceof RealParam) {
RealParam realParam = (RealParam)param;
if (val < realParam.getMinimumValueForInput() ||val > realParam.getMaximumValueForInput()) {
requestFocus();
selectAll();
if (realParam.getMinimumValueForInput() == Double.MIN_VALUE) {
if (realParam.getMaximumValueForInput() == Double.POSITIVE_INFINITY)
return I18n.tr("vmm.core.ParameterInput.rangeError1", param.getTitle());
else
return I18n.tr("vmm.core.ParameterInput.rangeError2", param.getTitle(), ""+realParam.getMaximumValueForInput());
}
else if (realParam.getMaximumValueForInput() == Double.POSITIVE_INFINITY)
return I18n.tr("vmm.core.ParameterInput.rangeError3", param.getTitle(), ""+realParam.getMinimumValueForInput());
else if (realParam.getMinimumValueForInput() == Double.NEGATIVE_INFINITY)
return I18n.tr("vmm.core.ParameterInput.rangeError4", param.getTitle(), ""+realParam.getMaximumValueForInput());
else
return I18n.tr("vmm.core.ParameterInput.rangeError5", param.getTitle(), ""+realParam.getMinimumValueForInput(),
""+realParam.getMaximumValueForInput());
}
}
else if (param instanceof ComplexParam) {
ComplexParam realParam = (ComplexParam)param;
if (cval.re < realParam.getMinimumValueForInput().re || cval.re > realParam.getMaximumValueForInput().re ||
cval.im < realParam.getMinimumValueForInput().im || cval.im > realParam.getMaximumValueForInput().im) {
requestFocus();
selectAll();
return I18n.tr("vmm.core.ParameterInput.rangeErrorComplex");
}
}
return null;
}
/**
* Sets the parameter's value, animation start value, or animation end value (as appropriate) from the contents of the text box.
* The parameter is not touched if the contents of the text box has not been modified since it was created (or since
* a previous call to this method or to {@link #setValueAndDefaultFromContents()}, if there was one). It is assumed
* that the checkContents method is called before this method is called, so if an error occurs while the
* value is being set, it is simply ignored and the parameter value is not changed.
* @see #checkContents()
*/
public void setValueFromContents() {
String contents = getText();
if (contents.equals(originalString))
return;
try {
switch (inputType) {
case VALUE:
param.setValueFromString(contents);
break;
case ANIMATION_START:
((Animateable)param).setAnimationStartFromString(contents);
break;
default:
((Animateable)param).setAnimationEndFromString(contents);
}
originalString = contents; // Reset "original state" to match new value.
}
catch (Exception e) {
}
}
/**
* Sets the parameter's value, animation start value, or animation end value (as appropriate) from the contents of the text box,
* and also sets the corresponding default for the parameter to the same value.
* The parameter is not touched if the contents of the text box has not been modified since it was created (or since
* a previous call to this method or to {@link #setValueFromContents()}, if there was one). It is assumed
* that the checkContents method is called before this method is called, so if an error occurs while the
* value is being set, it is simply ignored and the parameter value is not changed.
* @see #checkContents()
*/
public void setValueAndDefaultFromContents() {
String contents = getText();
if (contents.equals(originalString))
return;
try {
switch (inputType) {
case VALUE:
param.setValueAndDefaultFromString(contents);
break;
case ANIMATION_START:
((Animateable)param).setAnimationStartFromString(contents);
((Animateable)param).setDefaultAnimationStartFromString(contents);
break;
default:
((Animateable)param).setAnimationEndFromString(contents);
((Animateable)param).setDefaultAnimationEndFromString(contents);
}
originalString = contents; // Reset "original state" to match new value.
}
catch (Exception e) {
}
}
/**
* Reset the contents of the text box to its original value. The original string is taken from the parameter when this
* object is constructed.
*/
public void revert() {
setText(originalString);
}
/**
* Set the contents of the text box to the appropriate default value, as stored in the Parameter.
*/
public void defaultVal() {
switch (inputType) {
case VALUE:
setText(param.getDefaultValueAsString());
break;
case ANIMATION_START:
setText(((Animateable)param).getDefaultAnimationStartAsString());
break;
default:
setText(((Animateable)param).getDefaultAnimationEndAsString());
break;
}
}
}