From 260f1baaab10ab9b1db67ab587bc36adcb34494e Mon Sep 17 00:00:00 2001
From: Eduardo Pedroni <ep625@york.ac.uk>
Date: Tue, 8 Apr 2014 20:04:12 +0100
Subject: GUIParameters all refactored and commented.

---
 .../settings/parameters/GUIBooleanParameter.java   | 106 +++++-----
 .../settings/parameters/GUIDoubleParameter.java    | 168 +++++++--------
 .../settings/parameters/GUIIntegerParameter.java   | 167 +++++++--------
 src/jcgp/gui/settings/parameters/GUIParameter.java | 229 +++++++++++++++++----
 4 files changed, 401 insertions(+), 269 deletions(-)

(limited to 'src/jcgp/gui/settings/parameters')

diff --git a/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java b/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java
index 4339562..bada5d4 100644
--- a/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java
+++ b/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java
@@ -3,83 +3,79 @@ package jcgp.gui.settings.parameters;
 import javafx.beans.value.ChangeListener;
 import javafx.beans.value.ObservableValue;
 import javafx.scene.control.CheckBox;
+import javafx.scene.control.Control;
 import jcgp.backend.resources.parameters.BooleanParameter;
 import jcgp.backend.resources.parameters.ParameterStatus;
+import jcgp.gui.GUI;
 import jcgp.gui.settings.SettingsPane;
 
-public class GUIBooleanParameter extends GUIParameter {
-	
-	private boolean originalValue;
+/**
+ * This extension of GUIParameter uses a CheckBox to display
+ * the value of a BooleanParameter. It cannot be constructed
+ * directly - instead, use GUIParameter.create().
+ * <br><br>
+ * See {@link GUIParameter} for more information.
+ * 
+ * @author Eduardo Pedroni
+ */
+public class GUIBooleanParameter extends GUIParameter<Boolean> {
+
+	private CheckBox checkBox;
 	
+	/**
+	 * This default-visibility constructor is intended for use
+	 * by the factory method only.
+	 * 
+	 */
 	GUIBooleanParameter(final BooleanParameter parameter, final SettingsPane sp) {
-		super();
-		
-		this.parameter = parameter;
-		
-		originalValue = parameter.get();
-		
-		valueControl = new CheckBox(parameter.getName());
-		((CheckBox) valueControl).setSelected(parameter.get());
-		
-		valueControl.setDisable(parameter.isMonitor());
-		
-		if (parameter.isMonitor()) {
-			makeLightBinding();
-		} else {
-			((CheckBox) valueControl).selectedProperty().addListener(new ChangeListener<Boolean>() {
-				@Override
-				public void changed(
-						ObservableValue<? extends Boolean> observable,
-						Boolean oldValue, Boolean newValue) {
-					parameter.set(newValue);
-					setValidityStyle();
-					sp.revalidateParameters();
-				}
-			});
-		}
-		
-		getChildren().add(valueControl);
-	}
-
-	@Override
-	public void refreshValue() {
-		if (!((CheckBox) valueControl).selectedProperty().isBound()) {
-			((CheckBox) valueControl).setSelected(((BooleanParameter) parameter).get());
-		}
+		super(parameter, sp);
 	}
 	
 	@Override
-	public boolean requiresReset() {
-		return (parameter.isCritical() && ((BooleanParameter) parameter).get() != originalValue)
-				|| parameter.getStatus() == ParameterStatus.WARNING_RESET;
+	protected Control makeControl() {
+		checkBox = new CheckBox();
+		checkBox.setSelected(parameter.get());
+		
+		return checkBox;
 	}
 	
 	@Override
-	public void applyValue() {
-		originalValue = ((BooleanParameter) parameter).get();
+	protected void setControlListeners(final SettingsPane sp) {
+		/* pass the CheckBox value back to the parameter whenever it gets
+		 * modified, provided the experiment isn't running */
+		checkBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
+			@Override
+			public void changed(
+					ObservableValue<? extends Boolean> observable,
+					Boolean oldValue, Boolean newValue) {
+				if (!GUI.isWorking()) {
+					parameter.set(newValue);
+					sp.revalidateParameters();
+				}
+			}
+		});
 	}
 	
-	/**
-	 * @param parameter
-	 */
-	private void setValidityStyle() {
+	@Override
+	protected void setValidityStyle() {
+		// update the Control's style and tooltip based on the status of the parameter
 		if (parameter.getStatus() == ParameterStatus.INVALID) {
-			valueControl.setStyle(BASE_CHECKBOX_STYLE + INVALID_PARAMETER_STYLE);
-			valueControl.setTooltip(tooltip);
+			checkBox.setStyle(BASE_CHECKBOX_STYLE + INVALID_PARAMETER_STYLE);
+			checkBox.setTooltip(tooltip);
 			tooltip.setText(parameter.getStatus().getDetails());
 		} else if (parameter.getStatus() == ParameterStatus.WARNING || parameter.getStatus() ==  ParameterStatus.WARNING_RESET) {
-			valueControl.setStyle(BASE_CHECKBOX_STYLE + WARNING_PARAMETER_STYLE);
-			valueControl.setTooltip(tooltip);
+			checkBox.setStyle(BASE_CHECKBOX_STYLE + WARNING_PARAMETER_STYLE);
+			checkBox.setTooltip(tooltip);
 			tooltip.setText(parameter.getStatus().getDetails());
 		} else {
-			valueControl.setStyle(BASE_CHECKBOX_STYLE + VALID_PARAMETER_STYLE);
-			valueControl.setTooltip(null);
+			checkBox.setStyle(BASE_CHECKBOX_STYLE + VALID_PARAMETER_STYLE);
+			checkBox.setTooltip(null);
 		}
 	}
 
 	@Override
-	public void validate() {
-		((BooleanParameter) parameter).validate(((BooleanParameter) parameter).get());
-		setValidityStyle();
+	public void refreshValue() {
+		checkBox.setSelected(parameter.get());
 	}
+	
 }
diff --git a/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java b/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java
index 4a8e0e1..87e7b69 100644
--- a/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java
+++ b/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java
@@ -4,115 +4,105 @@ import javafx.beans.value.ChangeListener;
 import javafx.beans.value.ObservableValue;
 import javafx.event.EventHandler;
 import javafx.geometry.Pos;
+import javafx.scene.control.Control;
 import javafx.scene.control.TextField;
 import javafx.scene.input.KeyEvent;
-import javafx.scene.layout.Priority;
-import javafx.scene.text.Text;
-import jcgp.backend.resources.parameters.DoubleParameter;
+import jcgp.backend.resources.parameters.Parameter;
 import jcgp.backend.resources.parameters.ParameterStatus;
+import jcgp.gui.GUI;
 import jcgp.gui.settings.SettingsPane;
 
-public class GUIDoubleParameter extends GUIParameter {
+/**
+ * This extension of GUIParameter uses a TextField to display
+ * the value of a DoubleParameter. It cannot be constructed
+ * directly - instead, use GUIParameter.create().
+ * <br><br>
+ * See {@link GUIParameter} for more information.
+ * 
+ * @author Eduardo Pedroni
+ */
+public class GUIDoubleParameter extends GUIParameter<Number> {
+
+	private TextField textField;
 	
-	private double originalValue;
+	/**
+	 * This default-visibility constructor is intended for use
+	 * by the factory method only.
+	 * 
+	 */
+	GUIDoubleParameter(Parameter<Number> parameter, SettingsPane sp) {
+		super(parameter, sp);
+	}
 	
-	GUIDoubleParameter(final DoubleParameter parameter, final SettingsPane sp) {
-		super();
-		
-		this.parameter = parameter;
-		
-		originalValue = parameter.get();
-		
-		name = new Text(parameter.getName());
-		valueControl = new TextField(String.valueOf(parameter.get()));
-		valueControl.setStyle(VALID_PARAMETER_STYLE);
-		
-		((TextField) valueControl).setAlignment(Pos.CENTER_RIGHT);
-		
-		setHgrow(valueControl, Priority.ALWAYS);
-		setHgrow(name, Priority.ALWAYS);
-		
-		name.wrappingWidthProperty().bind(widthProperty().divide(2));
-		
-		((TextField) valueControl).setDisable(parameter.isMonitor());
+	@Override
+	protected Control makeControl() {
+		textField = new TextField(String.valueOf(parameter.get()));
+		textField.setStyle(VALID_PARAMETER_STYLE);
+		textField.setAlignment(Pos.CENTER_RIGHT);
 		
-		// bind if monitor, else set changelistener
-		if (parameter.isMonitor()) {
-			makeLightBinding();
-		} else {
-			valueControl.addEventFilter(KeyEvent.KEY_TYPED, new EventHandler<KeyEvent>() {
-				@Override
-				public void handle( KeyEvent t ) {
-					char ch = t.getCharacter().toCharArray()[t.getCharacter().toCharArray().length - 1];
-					if (!((ch >= '0' && ch <= '9') || (ch == '.' && !((TextField) valueControl).getText().contains(".")))) {
-						t.consume();
-					}
-				}
-			});
-			((TextField) valueControl).textProperty().addListener(new ChangeListener<String>() {
-				@Override
-				public void changed(
-						ObservableValue<? extends String> observable,
-						String oldValue, String newValue) {
-					if (!newValue.isEmpty()) {
-						parameter.set(Double.parseDouble(newValue));
-						setValidityStyle();
-						sp.revalidateParameters();
-					}
+		return textField;
+	}
 
+	@Override
+	protected void setControlListeners(final SettingsPane sp) {
+		/* filter keypresses and ignore anything that is not a number
+		 * and any decimal point beyond the first ones */
+		textField.addEventFilter(KeyEvent.KEY_TYPED, new EventHandler<KeyEvent>() {
+			@Override
+			public void handle( KeyEvent t ) {
+				char ch = t.getCharacter().toCharArray()[t.getCharacter().toCharArray().length - 1];
+				if (!((ch >= '0' && ch <= '9') || (ch == '.' && !textField.getText().contains(".")))) {
+					t.consume();
 				}
-			});
-			valueControl.focusedProperty().addListener(new ChangeListener<Boolean>() {
-				@Override
-				public void changed(
-						ObservableValue<? extends Boolean> observable,
-						Boolean oldValue, Boolean newValue) {
-					if (!newValue && ((TextField) valueControl).getText().isEmpty()) {
-						((TextField) valueControl).setText(String.valueOf(parameter.get()));
-					}
+			}
+		});
+		/* pass the TextField value back to the parameter whenever it gets
+		 * modified, provided it is not empty and the experiment isn't running */
+		textField.textProperty().addListener(new ChangeListener<String>() {
+			@Override
+			public void changed(
+					ObservableValue<? extends String> observable,
+					String oldValue, String newValue) {
+				if (!newValue.isEmpty() && !GUI.isWorking()) {
+					parameter.set(Double.parseDouble(newValue));
+					sp.revalidateParameters();
 				}
-			});
-		}
-		
-		
-		getChildren().addAll(name, valueControl);
-	}
 
-	@Override
-	public void refreshValue() {
-		((TextField) valueControl).setText(String.valueOf(((DoubleParameter) parameter).get()));
-	}
-	
-	@Override
-	public boolean requiresReset() {
-		return (parameter.isCritical() && ((DoubleParameter) parameter).get() != originalValue)
-				|| parameter.getStatus() == ParameterStatus.WARNING_RESET;
-	}
-	
-	@Override
-	public void applyValue() {
-		originalValue = ((DoubleParameter) parameter).get();
+			}
+		});
+		/* if the TextField loses focus and is empty, set it to the current
+		 * value of the parameter */
+		textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
+			@Override
+			public void changed(
+					ObservableValue<? extends Boolean> observable,
+					Boolean oldValue, Boolean newValue) {
+				if (!newValue && textField.getText().isEmpty()) {
+					textField.setText(String.valueOf(parameter.get()));
+				}
+			}
+		});
 	}
 
 	@Override
-	public void validate() {
-		((DoubleParameter) parameter).validate(((DoubleParameter) parameter).get());
-		setValidityStyle();
-	}
-	
-	private void setValidityStyle() {
+	protected void setValidityStyle() {
+		// update the Control's style and tooltip based on the status of the parameter
 		if (parameter.getStatus() == ParameterStatus.INVALID) {
-			valueControl.setStyle(BASE_TEXT_STYLE + INVALID_PARAMETER_STYLE);
-			valueControl.setTooltip(tooltip);
+			textField.setStyle(BASE_TEXT_STYLE + INVALID_PARAMETER_STYLE);
+			textField.setTooltip(tooltip);
 			tooltip.setText(parameter.getStatus().getDetails());
 		} else if (parameter.getStatus() == ParameterStatus.WARNING || parameter.getStatus() ==  ParameterStatus.WARNING_RESET) {
-			valueControl.setStyle(BASE_TEXT_STYLE + WARNING_PARAMETER_STYLE);
-			valueControl.setTooltip(tooltip);
+			textField.setStyle(BASE_TEXT_STYLE + WARNING_PARAMETER_STYLE);
+			textField.setTooltip(tooltip);
 			tooltip.setText(parameter.getStatus().getDetails());
 		} else {
-			valueControl.setStyle(BASE_TEXT_STYLE + VALID_PARAMETER_STYLE);
-			valueControl.setTooltip(null);
+			textField.setStyle(BASE_TEXT_STYLE + VALID_PARAMETER_STYLE);
+			textField.setTooltip(null);
 		}
 	}
-
+	
+	@Override
+	public void refreshValue() {
+		textField.setText(String.valueOf((parameter).get()));
+	}
 }
diff --git a/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java b/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java
index 204e7a2..6927f26 100644
--- a/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java
+++ b/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java
@@ -4,115 +4,104 @@ import javafx.beans.value.ChangeListener;
 import javafx.beans.value.ObservableValue;
 import javafx.event.EventHandler;
 import javafx.geometry.Pos;
+import javafx.scene.control.Control;
 import javafx.scene.control.TextField;
 import javafx.scene.input.KeyEvent;
-import javafx.scene.layout.Priority;
-import javafx.scene.text.Text;
-import jcgp.backend.resources.parameters.IntegerParameter;
+import jcgp.backend.resources.parameters.Parameter;
 import jcgp.backend.resources.parameters.ParameterStatus;
+import jcgp.gui.GUI;
 import jcgp.gui.settings.SettingsPane;
 
-public class GUIIntegerParameter extends GUIParameter {
-
-	private int originalValue;
+/**
+ * This extension of GUIParameter uses a TextField to display
+ * the value of a IntegerParameter. It cannot be constructed
+ * directly - instead, use GUIParameter.create().
+ * <br><br>
+ * See {@link GUIParameter} for more information.
+ * 
+ * @author Eduardo Pedroni
+ */
+public class GUIIntegerParameter extends GUIParameter<Number> {
 	
-	GUIIntegerParameter(final IntegerParameter parameter, final SettingsPane sp) {	
-		super();
-		
-		this.parameter = parameter;
-		
-		originalValue = parameter.get();
-		
-		name = new Text(parameter.getName());
-		valueControl = new TextField(String.valueOf(parameter.get()));
-		valueControl.setStyle(VALID_PARAMETER_STYLE);
-		
-		((TextField) valueControl).setAlignment(Pos.CENTER_RIGHT);
-		
-		setHgrow(valueControl, Priority.ALWAYS);
-		setHgrow(name, Priority.ALWAYS);
-		
-		name.wrappingWidthProperty().bind(widthProperty().divide(2));
-		
-		((TextField) valueControl).setDisable(parameter.isMonitor());
-		
-		// bind if monitor, else set listeners
-		if (parameter.isMonitor()) {
-			makeLightBinding();
-		} else {
-			valueControl.addEventFilter(KeyEvent.KEY_TYPED, new EventHandler<KeyEvent>() {
-				@Override
-				public void handle( KeyEvent t ) {
-					char ch = t.getCharacter().toCharArray()[t.getCharacter().toCharArray().length - 1];
-					if (!(ch >= '0' && ch <= '9')) {
-						t.consume();
-					}
-				}
-			});
-			((TextField) valueControl).textProperty().addListener(new ChangeListener<String>() {
-				@Override
-				public void changed(
-						ObservableValue<? extends String> observable,
-						String oldValue, String newValue) {
-					if (!newValue.isEmpty()) {
-						parameter.set(Integer.parseInt(newValue));
-						setValidityStyle();
-						sp.revalidateParameters();
-					}
-
-				}
-			});
-			valueControl.focusedProperty().addListener(new ChangeListener<Boolean>() {
-				@Override
-				public void changed(
-						ObservableValue<? extends Boolean> observable,
-						Boolean oldValue, Boolean newValue) {
-					if (!newValue && ((TextField) valueControl).getText().isEmpty()) {
-						((TextField) valueControl).setText(String.valueOf(parameter.get()));
-					}
-				}
-			});
-		}
-				
-		getChildren().addAll(name, valueControl);
+	private TextField textField;
+	
+	/**
+	 * This default-visibility constructor is intended for use
+	 * by the factory method only.
+	 * 
+	 */
+	GUIIntegerParameter(Parameter<Number> parameter, SettingsPane sp) {
+		super(parameter, sp);
 	}
 
 	@Override
-	public void refreshValue() {
-		((TextField) valueControl).setText(String.valueOf(((IntegerParameter) parameter).get()));
+	protected Control makeControl() {
+		textField = new TextField(String.valueOf(parameter.get()));
+		textField.setStyle(VALID_PARAMETER_STYLE);
+		textField.setAlignment(Pos.CENTER_RIGHT);
+		
+		return textField;
 	}
 
 	@Override
-	public boolean requiresReset() {
-		// true if critical and the value has changed, or it requires a reset anyway
-		return (parameter.isCritical() && ((IntegerParameter) parameter).get() != originalValue)
-				|| parameter.getStatus() == ParameterStatus.WARNING_RESET;
+	protected void setControlListeners(final SettingsPane settingsPane) {
+		/* filter keypresses and ignore anything that is not a number */
+		textField.addEventFilter(KeyEvent.KEY_TYPED, new EventHandler<KeyEvent>() {
+			@Override
+			public void handle( KeyEvent t ) {
+				char ch = t.getCharacter().toCharArray()[t.getCharacter().toCharArray().length - 1];
+				if (!(ch >= '0' && ch <= '9')) {
+					t.consume();
+				}
+			}
+		});
+		/* pass the TextField value back to the parameter whenever it gets
+		 * modified, provided it is not empty and the experiment isn't running */
+		textField.textProperty().addListener(new ChangeListener<String>() {
+			@Override
+			public void changed(
+					ObservableValue<? extends String> observable,
+					String oldValue, String newValue) {
+				if (!newValue.isEmpty() && !GUI.isWorking()) {
+					parameter.set(Double.parseDouble(newValue));
+					settingsPane.revalidateParameters();
+				}
+			}
+		});
+		/* if the TextField loses focus and is empty, set it to the current
+		 * value of the parameter */
+		textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
+			@Override
+			public void changed(
+					ObservableValue<? extends Boolean> observable,
+					Boolean oldValue, Boolean newValue) {
+				if (!newValue && textField.getText().isEmpty()) {
+					textField.setText(String.valueOf(parameter.get()));
+				}
+			}
+		});
 	}
 	
 	@Override
-	public void applyValue() {
-		originalValue = ((IntegerParameter) parameter).get();
-	}
-
-	@Override
-	public void validate() {
-		((IntegerParameter) parameter).validate(((IntegerParameter) parameter).get());
-		setValidityStyle();
-	}
-	
-	private void setValidityStyle() {
+	protected void setValidityStyle() {
+		// update the Control's style and tooltip based on the status of the parameter
 		if (parameter.getStatus() == ParameterStatus.INVALID) {
-			valueControl.setStyle(BASE_TEXT_STYLE + INVALID_PARAMETER_STYLE);
-			valueControl.setTooltip(tooltip);
+			textField.setStyle(BASE_TEXT_STYLE + INVALID_PARAMETER_STYLE);
+			textField.setTooltip(tooltip);
 			tooltip.setText(parameter.getStatus().getDetails());
-		} else if (parameter.getStatus() == ParameterStatus.WARNING || parameter.getStatus() ==  ParameterStatus.WARNING_RESET) {
-			valueControl.setStyle(BASE_TEXT_STYLE + WARNING_PARAMETER_STYLE);
-			valueControl.setTooltip(tooltip);
+		} else if (parameter.getStatus() == ParameterStatus.WARNING 
+				|| parameter.getStatus() ==  ParameterStatus.WARNING_RESET) {
+			textField.setStyle(BASE_TEXT_STYLE + WARNING_PARAMETER_STYLE);
+			textField.setTooltip(tooltip);
 			tooltip.setText(parameter.getStatus().getDetails());
 		} else {
-			valueControl.setStyle(BASE_TEXT_STYLE + VALID_PARAMETER_STYLE);
-			valueControl.setTooltip(null);
+			textField.setStyle(BASE_TEXT_STYLE + VALID_PARAMETER_STYLE);
+			textField.setTooltip(null);
 		}
 	}
 
+	@Override
+	public void refreshValue() {
+		textField.setText(String.valueOf((parameter).get()));
+	}
 }
diff --git a/src/jcgp/gui/settings/parameters/GUIParameter.java b/src/jcgp/gui/settings/parameters/GUIParameter.java
index 6bfdf30..b7afb74 100644
--- a/src/jcgp/gui/settings/parameters/GUIParameter.java
+++ b/src/jcgp/gui/settings/parameters/GUIParameter.java
@@ -9,6 +9,7 @@ import javafx.geometry.Pos;
 import javafx.scene.control.Control;
 import javafx.scene.control.Tooltip;
 import javafx.scene.layout.HBox;
+import javafx.scene.layout.Priority;
 import javafx.scene.text.Text;
 import jcgp.backend.resources.parameters.BooleanParameter;
 import jcgp.backend.resources.parameters.DoubleParameter;
@@ -18,7 +19,25 @@ import jcgp.backend.resources.parameters.ParameterStatus;
 import jcgp.gui.GUI;
 import jcgp.gui.settings.SettingsPane;
 
-public abstract class GUIParameter extends HBox {
+/**
+ * 
+ * This is the base class for all GUIParameters. Using the factory method GUIParameter.create()
+ * generates an appropriate instance of this class for the specified parameter.
+ * <br><br>
+ * GUIParameter is an HBox containing a Text for the parameter name and a Control for interaction.
+ * It stores an instance of its associated Parameter object and also contains a Tooltip for 
+ * displaying status information.
+ * <br><br>
+ * Monitor parameters have their Control disabled so that no changed can be made via the GUI.
+ * Non-monitor parameters are updated automatically as well, but may be changed by the user
+ * if the program is not evolving.
+ * <br><br>
+ * For more information on how parameters work in JCGP, see {@link Parameter}.
+ * 
+ * @author Eduardo Pedroni
+ * @param <T> the parameter data type
+ */
+public abstract class GUIParameter<T> extends HBox {
 	
 	public static final String BASE_TEXT_STYLE = "-fx-border-color: #C9C9C9; -fx-border-radius: 2; -fx-padding: 0; ";
 	public static final String BASE_CHECKBOX_STYLE = "-fx-padding: 0; ";
@@ -26,66 +45,204 @@ public abstract class GUIParameter extends HBox {
 	public static final String INVALID_PARAMETER_STYLE = "-fx-background-color: " + GUI.BAD_SELECTION_COLOUR;
 	public static final String WARNING_PARAMETER_STYLE = "-fx-background-color: " + GUI.NEUTRAL_SELECTION_COLOUR;
 	public static final String VALID_PARAMETER_STYLE = "-fx-background-color: " + GUI.NEUTRAL_COLOUR;	
-		
-	protected Parameter parameter;
-	protected Text name;
-	protected Control valueControl;
+	
+	private Text name;
+	private Control valueControl;
 	
 	protected Tooltip tooltip;
+	protected Parameter<T> parameter;
+	
+	/** This is the lock used to prevent more than one update task to be scheduled
+	 *  at the same time on the same GUIParameter. */
+	private AtomicBoolean updateLock = new AtomicBoolean(false);
 	
-	protected AtomicBoolean updateCheck = new AtomicBoolean(true);
+	/**
+	 * This value is used to assert whether the control has changed values since 
+	 * the program last ran. Therefore, it is updated whenever a generation occurs
+	 * or the experiment is reset.
+	 */
+	private T referenceValue;
 	
-	protected GUIParameter() {
+	/**
+	 * This protected constructor contains the common elements to all GUIParameters
+	 * and should be invoked by any subclasses using super().
+	 * 
+	 * @param parameter a Parameter for which to generate a GUIParameter
+	 * @param sp a reference to the SettingsPane
+	 */
+	protected GUIParameter(Parameter<T> parameter, final SettingsPane sp) {
+		this.parameter = parameter;
+		this.referenceValue = parameter.get();
+		
 		setAlignment(Pos.CENTER_LEFT);
 		setSpacing(5);
 		
+		name = new Text(parameter.getName());
+		// set text width to half of the total width of the GUIParameter
+		name.wrappingWidthProperty().bind(widthProperty().divide(2));
+		// allow the text to grow to always fill half of the GUIParameter
+		
+		// the tooltip is the hover-over label containing status information, when appropriate
 		tooltip = new Tooltip();
 		tooltip.setStyle("-fx-background-color: white; -fx-border-color: black; .page-corner {-fx-background-color: transparent;}");
 		tooltip.setSkin(null);
+		
+		
+		valueControl = makeControl();
+		setHgrow(valueControl, Priority.ALWAYS);
+		
+		// if the parameter is a monitor, it should be permanently disabled
+		valueControl.setDisable(parameter.isMonitor());
+		
+		// bind to parameter value property in a thread-safe way
+		makeThreadSafeBinding();
+		
+		// if parameter is not a monitor, make sure the control is constrained appropriately
+		if (!parameter.isMonitor()) {
+			setControlListeners(sp);
+		}
+		
+		getChildren().addAll(name, valueControl);
 	}
 	
-	protected final void makeLightBinding() {
+	/**
+	 * Factory method to create GUIParameters from Parameters. 
+	 * Use this to create an appropriate GUIParameter from any instance of Parameter,
+	 * rather than manually downcasting the Parameter object every time.
+	 * 
+	 * @param parameter a Parameter for which to generate a GUIParameter
+	 * @param sp a reference to the SettingsPane
+	 * @return an appropriate instance of GUIParameter
+	 */
+	public static GUIParameter<?> create(Parameter<?> parameter, SettingsPane sp) {
+		if (parameter instanceof IntegerParameter) {
+			return new GUIIntegerParameter((IntegerParameter) parameter, sp);
+		} else if (parameter instanceof DoubleParameter) {
+			return new GUIDoubleParameter((DoubleParameter) parameter, sp);
+		} else if (parameter instanceof BooleanParameter) {
+			return new GUIBooleanParameter((BooleanParameter) parameter, sp);
+		} else {
+			throw new ClassCastException("No GUIParameter subclass exists for argument of type " + parameter.getClass());
+		}
+	}
+
+	/**
+	 * Parameters are intended to communicate information from the experiment
+	 * to the GUI. Since the experiment runs on a separate threads and it is illegal
+	 * to modify JavaFX objects from outside the JavaFX Application thread, this
+	 * special ChangeListener updates the GUIParameter in a safe way.
+	 * <br><br>
+	 * Note that this is applied to all parameters regardless of whether they are 
+	 * monitors or not; the only difference between monitor and non-monitor parameters
+	 * is that monitor parameters cannot be modified from the GUI.
+	 */
+	private void makeThreadSafeBinding() {
 		parameter.valueProperty().addListener(new ChangeListener<Object>() {
 			@Override
 			public void changed(
 					ObservableValue<? extends Object> observable,
 					Object oldValue, Object newValue) {
-				if (updateCheck.getAndSet(false)) {
-					Platform.runLater(new Runnable() {
-						@Override
-						public void run() {
-							updateCheck.set(true);
-							refreshValue();
-						}
-					});
+				// only do this if the experiment is running
+				if (GUI.isWorking()) {
+					/* here's the catch - atomically get the lock state and set it to true
+					 * the lock will only be false again when the runnable is finished executing, 
+					 * preventing multiple runnables to concurrently update the same GUIParameter
+					 */
+					if (!updateLock.getAndSet(true)) {
+						Platform.runLater(new Runnable() {
+							@Override
+							public void run() {
+								refreshValue();
+								updateLock.set(false);
+							}
+						});
+					}
 				}
 			}
 		});
 	}
 	
-	public abstract void refreshValue();
-	
-	public abstract boolean requiresReset();
-	
-	public abstract void applyValue();
-	
-	public abstract void validate();
+	/**
+	 * @return true if the current value of the parameter does not prevent the 
+	 * experiment from running.
+	 */
+	public boolean isValid() {
+		return parameter.getStatus() != ParameterStatus.INVALID;
+	}
 	
-	public static GUIParameter create(Parameter parameter, SettingsPane sp) {
-		if (parameter instanceof IntegerParameter) {
-			return new GUIIntegerParameter((IntegerParameter) parameter, sp);
-		} else if (parameter instanceof DoubleParameter) {
-			return new GUIDoubleParameter((DoubleParameter) parameter, sp);
-		} else if (parameter instanceof BooleanParameter) {
-			return new GUIBooleanParameter((BooleanParameter) parameter, sp);
-		} else {
-			throw new ClassCastException("No GUIParameter subclass exists for argument of type " + parameter.getClass());
-		}
+	/**
+	 * Force the parameter to validate its current value, and apply the associated
+	 * style to the GUIParameter.
+	 */
+	public void validate() {
+		parameter.validate(parameter.get());
+		refreshValue();
+		setValidityStyle();
 	}
 	
-	public boolean isValid() {
-		return parameter.getStatus() == ParameterStatus.VALID
-				|| parameter.getStatus() == ParameterStatus.WARNING
+	/**
+	 * Certain parameter changes might require the experiment to be reset, either
+	 * because the parameter is critical or because its status requires a reset.
+	 * 
+	 * @return true if an experiment reset is required due to this parameter changing.
+	 */
+	public boolean requiresReset() {
+		return (parameter.isCritical() && !parameter.get().equals(referenceValue))
 				|| parameter.getStatus() == ParameterStatus.WARNING_RESET;
 	}
+	
+	/**
+	 * Set the current parameter value as the reference value of the GUIParameter.
+	 * The new reference value will be used to determine the validity of the parameter, 
+	 * should its value change.
+	 */
+	public void applyValue() {
+		referenceValue = parameter.get();
+	}
+	
+	/* 
+	 * The following prototypes are instance-dependent and are called from
+	 * GUIParameter() as necessary.
+	 */
+	/**
+	 * This method returns the Control object used to control the parameter.
+	 * <br><br>
+	 * Implementations of GUIParameter must override this method and return 
+	 * a control appropriate to the type of parameter. This will typically be
+	 * done by referencing the protected field GUIParameter.parameter.
+	 * 
+	 * @return the Control object to be added to the GUIParameter.
+	 */
+	protected abstract Control makeControl();
+	
+	/**
+	 * Adds the necessary handlers to the Control object in order to modify
+	 * the underlying parameter. This will typically consist of filtering key
+	 * presses to ensure no invalid characters are inserted, applying the new 
+	 * value to the underlying parameter and revalidating the parameters to
+	 * reflect the changes made.
+	 * 
+	 * @param settingsPane the parent Pane on which revalidateParameters() should
+	 * be called.
+	 */
+	protected abstract void setControlListeners(SettingsPane settingsPane);
+	
+	/**
+	 * This method is called to style the GUIParameter according to the status of
+	 * the parameter, which can be obtained with parameter.getStatus(). While the
+	 * subclass is free to style itself in any way, the CSS strings defined here 
+	 * (INVALID_PARAMETER_STYLE, WARNING_PARAMETER_STYLE, VALID_PARAMETER_STYLE)
+	 * provide a way to keep the GUI consistent.
+	 * 
+	 * TODO update this if the strings are externalised
+	 * 
+	 * @see ParameterStatus
+	 */
+	protected abstract void setValidityStyle();
+	
+	/**
+	 * Update the control so it shows the correct value of the parameter. This method
+	 * is used exclusively by the thread-safe binding created if the module is a monitor.
+	 */
+	protected abstract void refreshValue();
 }
-- 
cgit v1.2.3