import net.sf.openrocket.unit.UnitGroup;
import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.ChangeSource;
+import net.sf.openrocket.util.Invalidatable;
+import net.sf.openrocket.util.Invalidator;
import net.sf.openrocket.util.MathUtil;
+import net.sf.openrocket.util.MemoryManagement;
import net.sf.openrocket.util.Reflection;
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
-public class DoubleModel implements ChangeListener, ChangeSource {
+public class DoubleModel implements ChangeListener, ChangeSource, Invalidatable {
private static final LogHelper log = Application.getLogger();
* Model suitable for JSpinner using JSpinner.NumberEditor. It extends SpinnerNumberModel
* to be compatible with the NumberEditor, but only has the necessary methods defined.
*/
- private class ValueSpinnerModel extends SpinnerNumberModel {
+ private class ValueSpinnerModel extends SpinnerNumberModel implements Invalidatable {
@Override
public Object getValue() {
public void removeChangeListener(ChangeListener l) {
DoubleModel.this.removeChangeListener(l);
}
+
+ @Override
+ public void invalidate() {
+ DoubleModel.this.invalidate();
+ }
}
/**
//////////// JSlider model ////////////
- private class ValueSliderModel implements BoundedRangeModel, ChangeListener {
+ private class ValueSliderModel implements BoundedRangeModel, ChangeListener, Invalidatable {
private static final int MAX = 1000;
/*
return x * x;
}
+ @Override
public int getValue() {
double value = DoubleModel.this.getValue();
if (value <= min.getValue())
// Use quadratic scale
// Further solution of the quadratic equation
// a*x^2 + b*x + c-value == 0
- x = (Math.sqrt(quad1 * quad1 - 4 * quad2 * (quad0 - value)) - quad1) / (2 * quad2);
+ x = (MathUtil.safeSqrt(quad1 * quad1 - 4 * quad2 * (quad0 - value)) - quad1) / (2 * quad2);
}
return (int) (x * MAX);
}
+ @Override
public void setValue(int newValue) {
if (firing > 0) {
// Ignore loops
// Static get-methods
private boolean isAdjusting;
+ @Override
public int getExtent() {
return 0;
}
+ @Override
public int getMaximum() {
return MAX;
}
+ @Override
public int getMinimum() {
return 0;
}
+ @Override
public boolean getValueIsAdjusting() {
return isAdjusting;
}
// Ignore set-values
+ @Override
public void setExtent(int newExtent) {
}
+ @Override
public void setMaximum(int newMaximum) {
}
+ @Override
public void setMinimum(int newMinimum) {
}
+ @Override
public void setValueIsAdjusting(boolean b) {
isAdjusting = b;
}
+ @Override
public void setRangeProperties(int value, int extent, int min, int max, boolean adjusting) {
setValueIsAdjusting(adjusting);
setValue(value);
}
// Pass change listeners to the underlying model
+ @Override
public void addChangeListener(ChangeListener l) {
DoubleModel.this.addChangeListener(l);
}
+ @Override
public void removeChangeListener(ChangeListener l) {
DoubleModel.this.removeChangeListener(l);
}
+ @Override
+ public void invalidate() {
+ DoubleModel.this.invalidate();
+ }
-
+ @Override
public void stateChanged(ChangeEvent e) {
// Min or max range has changed.
// Fire if not already firing
//////////// Action model ////////////
- private class AutomaticActionModel extends AbstractAction implements ChangeListener {
+ private class AutomaticActionModel extends AbstractAction implements ChangeListener, Invalidatable {
private boolean oldValue = false;
public AutomaticActionModel() {
}
// If the value has changed, generate an event to the listeners
+ @Override
public void stateChanged(ChangeEvent e) {
boolean newValue = isAutomatic();
if (oldValue == newValue)
}
}
+ @Override
public void actionPerformed(ActionEvent e) {
// Setting performed in putValue
}
+ @Override
+ public void invalidate() {
+ DoubleModel.this.invalidate();
+ }
}
/**
private double lastValue = 0;
private boolean lastAutomatic = false;
+ private Invalidator invalidator = new Invalidator(this);
+
+ /**
+ * Generate a DoubleModel that contains an internal double value.
+ *
+ * @param value the initial value.
+ */
public DoubleModel(double value) {
this(value, UnitGroup.UNITS_NONE, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
}
+ /**
+ * Generate a DoubleModel that contains an internal double value.
+ *
+ * @param value the initial value.
+ * @param unit the unit for the value.
+ */
public DoubleModel(double value, UnitGroup unit) {
this(value, unit, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
}
+ /**
+ * Generate a DoubleModel that contains an internal double value.
+ *
+ * @param value the initial value.
+ * @param unit the unit for the value.
+ * @param min minimum value.
+ */
public DoubleModel(double value, UnitGroup unit, double min) {
this(value, unit, min, Double.POSITIVE_INFINITY);
}
+ /**
+ * Generate a DoubleModel that contains an internal double value.
+ *
+ * @param value the initial value.
+ * @param unit the unit for the value.
+ * @param min minimum value.
+ * @param max maximum value.
+ */
public DoubleModel(double value, UnitGroup unit, double min, double max) {
this.lastValue = value;
this.minValue = min;
* The double value is read and written using the methods "get"/"set" + valueName.
*
* @param source Component whose parameter to use.
- * @param valueName Name of metods used to get/set the parameter.
+ * @param valueName Name of methods used to get/set the parameter.
* @param multiplier Value shown by the model is the value from component.getXXX * multiplier
* @param min Minimum value allowed (in SI units)
* @param max Maximum value allowed (in SI units)
* @param v New value for parameter in SI units.
*/
public void setValue(double v) {
+ checkState(true);
+
log.debug("Setting value " + v + " for " + this);
if (setMethod == null) {
if (getMethod != null) {
* state change event if automatic setting is not available.
*/
public void setAutomatic(boolean auto) {
+ checkState(true);
+
if (setAutoMethod == null) {
log.debug("Setting automatic to " + auto + " for " + this + ", automatic not available");
fireStateChanged(); // in case something is out-of-sync
* @param u The unit to set active.
*/
public void setCurrentUnit(Unit u) {
+ checkState(true);
if (currentUnit == u)
return;
log.debug("Setting unit for " + this + " to '" + u + "'");
* is the first listener.
* @param l Listener to add.
*/
+ @Override
public void addChangeListener(ChangeListener l) {
+ checkState(true);
+
if (listeners.isEmpty()) {
if (source != null) {
source.addChangeListener(this);
* if this was the last listener of the model.
* @param l Listener to remove.
*/
+ @Override
public void removeChangeListener(ChangeListener l) {
+ checkState(false);
+
listeners.remove(l);
if (listeners.isEmpty() && source != null) {
source.removeChangeListener(this);
}
+ /**
+ * Invalidates this model by removing all listeners and removing this from
+ * listening to the source. After invalidation no listeners can be added to this
+ * model and the value cannot be set.
+ */
+ @Override
+ public void invalidate() {
+ log.verbose("Invalidating " + this);
+ invalidator.invalidate();
+
+ if (!listeners.isEmpty()) {
+ log.warn("Invalidating " + this + " while still having listeners " + listeners);
+ }
+ listeners.clear();
+ if (source != null) {
+ source.removeChangeListener(this);
+ }
+ MemoryManagement.collectable(this);
+ }
+
+
+ private void checkState(boolean error) {
+ invalidator.check(error);
+ }
+
+
@Override
protected void finalize() throws Throwable {
super.finalize();
* Fire a ChangeEvent to all listeners.
*/
protected void fireStateChanged() {
+ checkState(true);
+
Object[] l = listeners.toArray();
ChangeEvent event = new ChangeEvent(this);
firing++;
* Called when the component changes. Checks whether the modeled value has changed, and if
* it has, updates lastValue and generates ChangeEvents for all listeners of the model.
*/
+ @Override
public void stateChanged(ChangeEvent e) {
+ checkState(true);
+
double v = getValue();
boolean b = isAutomatic();
if (lastValue == v && lastAutomatic == b)