package variableEditorComponents;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import valueTypes.DecimalValue;
import variableEditorUI.VariableEditorUIUpdateThread;
import variables.Variable;
import expressionConsole.ExpressionConsoleModel;
/**
* A JSlider which is bound to a Variable which contains a DecimalValue. When
* the user moves the slider, the content of the Variable is updated. The scale
* of the slider is also updated such that the value of the Variable is in the
* middle of the slider range. When the Variable is changed from another source,
* the scale of the slider is updated with the new contents of the Variable.
*
* @author Curran Kelleher
*
*/
public class VariableBoundDecimalSlider extends JSlider implements
ChangeListener, MouseListener, Observer, VariableEditorComponent {
private static final long serialVersionUID = -7777499408692975081L;
/**
* The Variable to which this VariableBoundDecimalSlider is bound.
*/
Variable variable;
/**
* The resolution of the slider.
*/
final static int SLIDERSCALE = 800;
/**
* The value at the lowest slider position
*/
double minValue = 0;
/**
* The value at the highest slider position
*/
double maxValue = 10;
/**
* A flag indicating to ignore slider changes. (set to true when change is
* self-induced)
*/
boolean ignoreSliderChange = false;
/**
* A DecimalValue which is used many times as an intermediate for assigning
* a new value to the variable being edited. This is done to avoid creating
* many new Objects.
*/
DecimalValue reusableValue = new DecimalValue(0);
/**
* The Value that is currently being displayed in this text field. This is
* checked against the actual value of the Variable periodically to decide
* whether or not to update the text with the current value of the Variable.
* This String is parseable into the actual value. It is generated by the
* method Value.toParseableString()
*/
String displayedValue = "";
/**
* The value corresponding to the highest (rightmost) slider position when
* the value of the Variable is zero.
*/
protected double maxValueForZeroValue = 1;
/**
* The value corresponding to the lowest (leftmost) slider position when the
* value of the Variable is zero.
*/
protected double minValueForZeroValue = -1;
/**
* Construct a VariableBoundDecimalSlider which is bound to the specified
* Variable.
*
* @param variable
* the variable to edit
*/
public VariableBoundDecimalSlider(Variable variable) {
this.variable = variable;
// listen for the mouse release
addMouseListener(this);
// align the slider with our scale
setMaximum(SLIDERSCALE);
// get notifications every second.
VariableEditorUIUpdateThread.getInstance().addObserver(this);
// initialize the slider
updateWithCurrentVariableValue();
// listen for slider movements
addChangeListener(this);
}
/**
* Updates the range of values that the slider covers such that the current
* value of the variable makes it be in the center.
*
*/
public void updateWithCurrentVariableValue() {
double currentValue = DecimalValue.extractDoubleValue(variable
.evaluate());
maxValue = currentValue * 2;
minValue = 0;
if (maxValue == 0) {
maxValue = maxValueForZeroValue;
minValue = minValueForZeroValue;
} else if (maxValue < 0) {
minValue = maxValue;
maxValue = 0;
}
// ignore the self-induced change
ignoreSliderChange = true;
// set the slider position
setValue((int) ((currentValue - minValue) / (maxValue - minValue) * SLIDERSCALE));
// don't ignore subsequent changes
ignoreSliderChange = false;
// update the displayed Value
displayedValue = "" + currentValue;
}
/**
* Listens to slider changes, updates the variable's value
*
* @param a
* the event from the slider change
*/
public void stateChanged(ChangeEvent a) {
if (!ignoreSliderChange) {
reusableValue.value = getCurrentSliderValue();
variable.set(reusableValue);
displayedValue = reusableValue.toParseableString();
}
}
/**
*
* @return the current value (which the Variable will be set to) derived
* from the slider position.
*/
protected double getCurrentSliderValue() {
return minValue + ((double) getValue() / SLIDERSCALE)
* (maxValue - minValue);
}
public void mouseClicked(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
/**
* Log the change when the mouse is released.
*/
public void mouseReleased(MouseEvent e) {
// log the change for correct replay
ExpressionConsoleModel.getInstance().enterExpression(
variable.toString() + " = "
+ variable.evaluate().toParseableString());
updateWithCurrentVariableValue();
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
/**
* Get the update from the VariableEditorUIUpdateThread
*/
public void update(Observable o, Object arg) {
if (o == VariableEditorUIUpdateThread.getInstance())
// if this component is no longer usable, remove it as an Observer.
if (!isDisplayable())
VariableEditorUIUpdateThread.getInstance().deleteObserver(this);
else if (!variable.evaluate().toParseableString().equals(
displayedValue))
if (!getValueIsAdjusting())
updateWithCurrentVariableValue();
}
/**
* Sets up a VariableEditorComponent such that
* updateWithCurrentVariableValue() will be called in it whenever the
* slider's position changes.
*
* @param componentToUpdate
* the VariableEditorComponent to update when the slider's
* position changes.
*/
public void bindToVariableEditorComponent(
VariableEditorComponent componentToUpdate) {
final VariableEditorComponent componentToReallyUpdate = componentToUpdate;
addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
componentToReallyUpdate.updateWithCurrentVariableValue();
}
});
}
}