package org.esa.snap.rcp.statistics;
import com.bc.ceres.binding.Property;
import com.bc.ceres.binding.PropertyContainer;
import com.bc.ceres.binding.PropertySet;
import com.bc.ceres.binding.ValidationException;
import com.bc.ceres.binding.Validator;
import com.bc.ceres.swing.binding.BindingContext;
import org.esa.snap.core.util.math.MathUtils;
import org.esa.snap.ui.GridBagUtils;
import org.jfree.chart.axis.ValueAxis;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import java.awt.GridBagConstraints;
/**
* @author Norman Fomferra
*/
public class AxisRangeControl {
public final static String PROPERTY_NAME_AUTO_MIN_MAX = "autoMinMax";
public final static String PROPERTY_NAME_MIN = "min";
public final static String PROPERTY_NAME_MAX = "max";
private final BindingContext bindingContext;
private final String axisName;
private JPanel panel;
private TitledSeparator titledSeparator;
public AxisRangeControl(String axisName) {
this.axisName = axisName;
PropertySet propertyContainer = PropertyContainer.createObjectBacked(new Model());
bindingContext = new BindingContext(propertyContainer);
}
public JPanel getPanel() {
if (panel == null) {
panel = createPanel();
panel.setName(axisName);
}
return panel;
}
private JPanel createPanel() {
final JCheckBox autoMinMaxBox = new JCheckBox("Auto min/max");
final JLabel minLabel = new JLabel("Min:");
final JLabel maxLabel = new JLabel("Max:");
final JTextField minTextField = new JTextField();
final JTextField maxTextField = new JTextField();
final JPanel panel = GridBagUtils.createPanel();
final GridBagConstraints gbc = GridBagUtils.createConstraints("anchor=WEST,fill=HORIZONTAL");
GridBagUtils.setAttributes(gbc, "gridwidth=2,insets.top=2,weightx=1");
titledSeparator = new TitledSeparator(axisName);
GridBagUtils.addToPanel(panel, titledSeparator, gbc, "gridy=0,insets.right=-2");
GridBagUtils.addToPanel(panel, autoMinMaxBox, gbc, "gridy=1");
GridBagUtils.setAttributes(gbc, "gridwidth=1");
GridBagUtils.addToPanel(panel, minLabel, gbc, "insets.left=22,gridx=0,gridy=2,weightx=0");
GridBagUtils.addToPanel(panel, minTextField, gbc, "insets=2,gridx=1,gridy=2,weightx=1");
GridBagUtils.addToPanel(panel, maxLabel, gbc, "insets.left=22,gridx=0,gridy=3,weightx=0");
GridBagUtils.addToPanel(panel, maxTextField, gbc, "insets=2,gridx=1,gridy=3,weightx=1");
bindingContext.bind("autoMinMax", autoMinMaxBox);
bindingContext.bind("min", minTextField);
bindingContext.bind("max", maxTextField);
bindingContext.getPropertySet().getDescriptor("min").setDescription("Minimum display value for " + axisName);
bindingContext.getPropertySet().getDescriptor("max").setDescription("Maximum display value for " + axisName);
bindingContext.getPropertySet().getDescriptor("min").setValidator(new Validator() {
@Override
public void validateValue(Property property, Object value) throws ValidationException {
final Double max = bindingContext.getPropertySet().getValue("max");
if ((Double) value >= max) {
throw new ValidationException("min value has to be less than " + max);
}
}
});
bindingContext.getPropertySet().getDescriptor("max").setValidator(new Validator() {
@Override
public void validateValue(Property property, Object value) throws ValidationException {
final Double min = bindingContext.getPropertySet().getValue("min");
if ((Double) value <= min) {
throw new ValidationException("max value has to be greater than " + min);
}
}
});
bindingContext.getBinding("min").addComponent(minLabel);
bindingContext.getBinding("max").addComponent(maxLabel);
bindingContext.bindEnabledState("min", true, "autoMinMax", false);
bindingContext.bindEnabledState("max", true, "autoMinMax", false);
return panel;
}
void addValidators() {
}
public void setTitleSuffix(String suffix) {
final JLabel label = titledSeparator.getLabelComponent();
if (suffix == null || suffix.trim().length() == 0) {
label.setText(axisName);
} else {
label.setText(axisName + " (" + suffix.trim() + ")");
}
titledSeparator.repaint();
}
public BindingContext getBindingContext() {
return bindingContext;
}
public void setComponentsEnabled(boolean enabled) {
if (!enabled) {
for (Property property : bindingContext.getPropertySet().getProperties()) {
bindingContext.setComponentsEnabled(property.getName(), enabled);
}
} else {
for (Property property : bindingContext.getPropertySet().getProperties()) {
if (property.getName().equals("min") || property.getName().equals("max")) {
bindingContext.setComponentsEnabled(property.getName(), !isAutoMinMax());
} else {
bindingContext.setComponentsEnabled(property.getName(), enabled);
}
}
}
}
public boolean isAutoMinMax() {
return (Boolean) bindingContext.getBinding("autoMinMax").getPropertyValue();
}
public void adjustComponents(ValueAxis axis, int numDecimalPlaces) {
adjustComponents(axis.getLowerBound(), axis.getUpperBound(), numDecimalPlaces);
}
public void adjustComponents(double min, double max, int numDecimalPlaces) {
final Double oldMax = getMax();
double newMax = MathUtils.round(max, roundFactor(numDecimalPlaces));
double newMin = MathUtils.round(min, roundFactor(numDecimalPlaces));
if(newMin == newMax) {
newMax += Math.pow(10, -numDecimalPlaces);
}
if (newMin >= oldMax) {
setMax(newMax);
setMin(newMin);
} else {
setMin(newMin);
setMax(newMax);
}
}
public void adjustAxis(ValueAxis axis, int numDecimalPlaces) {
final double lowerRange = MathUtils.round((Double) getBindingContext().getBinding("min").getPropertyValue(), roundFactor(numDecimalPlaces));
final double upperRange = MathUtils.round((Double) getBindingContext().getBinding("max").getPropertyValue(), roundFactor(numDecimalPlaces));
axis.setRange(lowerRange, upperRange);
}
private double roundFactor(int n) {
return Math.pow(10.0, n);
}
public Double getMin() {
return (Double) getBindingContext().getPropertySet().getValue("min");
}
public Double getMax() {
return (Double) getBindingContext().getPropertySet().getValue("max");
}
private void setMin(double min) {
getBindingContext().getPropertySet().setValue("min", min);
}
private void setMax(double max) {
getBindingContext().getPropertySet().setValue("max", max);
}
private static class Model {
private boolean autoMinMax = true;
private double min = 0.0;
private double max = 100.0;
}
}