package com.framsticks.gui.controls; import java.awt.BorderLayout; import java.awt.Dimension; import java.util.Hashtable; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.JTextField; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.apache.log4j.Logger; import com.framsticks.params.Flags; import com.framsticks.params.types.DecimalParam; import com.framsticks.params.types.FloatParam; import com.framsticks.params.types.NumberParam; import com.framsticks.util.lang.Numbers; import com.framsticks.util.swing.Layout; @SuppressWarnings("serial") public class SliderControl extends TextControl { private static final Logger log = Logger.getLogger(SliderControl.class.getName()); protected final JSlider slider; protected final JTextField text; /** * Division factor used to implement float value slider. */ private final int div = 10; private JComponent changing = null; private Class valueType; public SliderControl(NumberParam param) { super(param); text = new JTextField(); //TODO: that factor should be done as a constant this.setMaximumSize(new Dimension(Integer.MAX_VALUE, (int)(LINE_HEIGHT * 1.2))); // ComponentUI ui = UIManager.getUI(slider); // assert ui instanceof SliderUI; // SliderUI sui = (SliderUI) ui; // slider.setUI(sui); slider = new JSlider(); slider.setEnabled(!isReadOnly()); slider.setPaintLabels(false); if (param instanceof DecimalParam) { valueType = Integer.class; int min = param.getMin(Integer.class); int max = param.getMax(Integer.class); slider.setMinimum(min); slider.setMaximum(max); if (param.getDef(Integer.class) != null) { slider.setValue(param.getDef(Integer.class)); } else { slider.setValue(min); } slider.setMajorTickSpacing((int) ((max - min) / 5)); slider.setMinorTickSpacing((int) ((max - min) / 10)); } else if (param instanceof FloatParam) { valueType = Double.class; double min = param.getMin(Double.class) * div; slider.setMinimum((int) min); double max = param.getMax(Double.class) * div; double diff = max - min; slider.setMaximum((int) max); Hashtable labels = new Hashtable(); int ticks = 6; for (int i = 0; i <= ticks; i++) { double val = (diff / ticks) * i + min; String label = String.format("%1$.1f", (val / 10)).replace(",", "."); labels.put((int) val, new JLabel(label, JLabel.CENTER)); } slider.setLabelTable(labels); slider.setMajorTickSpacing((int) (diff / 5)); slider.setMinorTickSpacing((int) (diff / 10)); if (param.getDef(Double.class) != null) { double defaultValue = param.getDef(Double.class) * div; slider.setValue((int) defaultValue); } } slider.setPaintLabels(true); slider.setPaintTicks(true); this.setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS)); this.setAlignmentX(Box.CENTER_ALIGNMENT); this.setAlignmentY(Box.CENTER_ALIGNMENT); JPanel sliderPanel = new JPanel(); sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.LINE_AXIS)); Layout.fixComponentSize(text, new Dimension(90, Control.LINE_HEIGHT)); text.setHorizontalAlignment(JSlider.CENTER); text.setEnabled(!param.hasFlag(Flags.READONLY)); slider.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { if (changing != null) { return; } log.trace("changing " + getParam() + " with slider: " + slider.getValue()); changing = slider; text.setText(convertFromSliderDomain(slider.getValue()).toString()); changing = null; } }); text.getDocument().addDocumentListener(createDocumentListener(new Runnable() { @Override public void run() { if (changing != null) { return; } log.trace("changing " + getParam() + " with text: " + text.getText()); changing = text; Integer i = convertToSliderDomain(convertTextToNumber()); if (i != null) { slider.setValue(i); } changing = null; notifyOfChange(); } })); JPanel sVPanel = new JPanel(); sVPanel.setLayout(new BoxLayout(sVPanel, BoxLayout.LINE_AXIS)); sVPanel.add(text); Layout.copyComponentDimensions(sVPanel, text); JPanel sPanel = new JPanel(); sPanel.setLayout(new BoxLayout(sPanel, BoxLayout.LINE_AXIS)); sliderPanel.setLayout(new BorderLayout()); sliderPanel.add(slider, BorderLayout.CENTER); sliderPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE)); sliderPanel.setMinimumSize(new Dimension(0, 60)); sPanel.add(sVPanel); sPanel.add(sliderPanel); this.add(sPanel); } @Override public NumberParam getParam() { return (NumberParam) param; } private Number convertFromSliderDomain(int value) { if (param instanceof DecimalParam) { return value; } if (param instanceof FloatParam) { return (double) value / (double) div; } return null; } private Integer convertToSliderDomain(Number value) { if (value == null) { return null; } if (param instanceof DecimalParam) { return (Integer) value; } if (param instanceof FloatParam) { return (int) ((Double) value * div); } return null; } private Number convertTextToNumber() { return Numbers.cast(text.getText(), valueType); } @Override public void pushValueToUserInterfaceImpl(Object value) { if (value == null) { return; } text.setText(value.toString()); } @Override public Number pullValueFromUserInterface() { return convertTextToNumber(); } }