package ini.trakem2.utils; import java.awt.Color; import java.awt.Component; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.text.JTextComponent; public class OptionPanel extends JPanel { private static final long serialVersionUID = 1L; private GridBagLayout bl = new GridBagLayout(); private GridBagConstraints c = new GridBagConstraints(); private ActionListener tl = new ActionListener() { public void actionPerformed(ActionEvent ae) { Component source = (Component) ae.getSource(); try { Setter s = setters.get(source); if (null != s) s.setFrom(source); } catch (Exception e) { Utils.log2("Invalid value!"); } } }; private KeyListener kl = new KeyAdapter() { public void keyReleased(KeyEvent ke) { Component source = (Component) ke.getSource(); try { Setter s = setters.get(source); if (null != s) s.setFrom(source); } catch (Throwable t) { Utils.logAll("Invalid value " + ((JTextField)source).getText()); } } }; private MouseWheelListener mwl = new MouseWheelListener() { @Override public void mouseWheelMoved(MouseWheelEvent mwe) { Component source = (Component) mwe.getSource(); try { Setter s = setters.get(source); if (s instanceof NumericalSetter) { if (null != s) { // Update the value of the field and also of the JTextField ((JTextField)source).setText(((NumericalSetter)s).setFrom(source, mwe.getWheelRotation()).toString()); } } } catch (Throwable t) { Utils.logAll("Invalid value " + ((JTextField)source).getText()); } } }; public OptionPanel() { super(); setLayout(bl); c.ipady = 10; } private List<JTextField> numeric_fields = new ArrayList<JTextField>(); private List<JCheckBox> checkboxes = new ArrayList<JCheckBox>(); private List<JComboBox> choices = new ArrayList<JComboBox>(); private List<Component> all = new ArrayList<Component>(); private HashMap<Component,Setter> setters = new HashMap<Component,Setter>(); private int rows = 0; public void addMessage(String text) { JLabel label = new JLabel(text); c.gridy = rows++; c.gridx = 0; c.anchor = GridBagConstraints.NORTHWEST; c.weightx = 1; c.weighty = 0; c.gridwidth = 2; c.fill = GridBagConstraints.NONE; bl.setConstraints(label, c); add(label); } public void bottomPadding() { c.gridy = rows++; c.gridx = 0; c.anchor = GridBagConstraints.NORTHWEST; c.fill = GridBagConstraints.BOTH; c.weightx = 1; c.weighty = 1; JPanel space = new JPanel(); bl.setConstraints(space, c); add(space); } /** The left-side label of a field. */ private void addLabel(String label) { c.gridy = rows++; c.gridx = 0; JLabel l = new JLabel(label); c.anchor = GridBagConstraints.NORTHEAST; c.weightx = 1; c.weighty = 0; c.ipadx = 10; c.gridwidth = 1; c.fill = GridBagConstraints.NONE; bl.setConstraints(l, c); add(l); } /** The right-side field of a label. */ private void addField(Component comp, Setter setter) { c.gridx = 1; c.weightx = 0; c.anchor = GridBagConstraints.NORTHWEST; c.ipadx = 0; c.gridwidth = 1; c.fill = GridBagConstraints.HORIZONTAL; bl.setConstraints(comp, c); add(comp); setters.put(comp, setter); } public JTextField addNumericField(String label, double value, int digits) { return addNumericField(label, value, digits, null); } public JTextField addNumericField(String label, double value, int digits, Setter setter) { return addNumericField(label, Utils.cutNumber(value, digits), setter); } public JTextField addNumericField(String label, int value) { return addNumericField(label, value, null); } public JTextField addNumericField(String label, int value, Setter setter) { return addNumericField(label, Integer.toString(value), setter); } private JTextField addNumericField(String label, String value, Setter setter) { addLabel(label); JTextField tval = new JTextField(value, 7); numeric_fields.add(tval); addField(tval, setter); tval.addKeyListener(kl); tval.addMouseWheelListener(mwl); return tval; } public JCheckBox addCheckbox(String label, boolean selected) { return addCheckbox(label, selected, null); } public JCheckBox addCheckbox(String label, boolean selected, Setter setter) { addLabel(label); JCheckBox cb = new JCheckBox(); cb.setSelected(selected); checkboxes.add(cb); all.add(cb); addField(cb, setter); cb.addActionListener(tl); return cb; } public JComboBox addChoice(String label, String[] items, int selected) { return addChoice(label, items, selected, null); } public JComboBox addChoice(String label, String[] items, int selected, Setter setter) { addLabel(label); JComboBox choice = new JComboBox(items); choice.setBackground(Color.white); choice.setSelectedIndex(selected); choice.addActionListener(tl); all.add(choice); addField(choice, setter); return choice; } public List<JTextField> getNumericFields() { return new ArrayList<JTextField>(numeric_fields); } /** May throw IllegalArgumentException or NumberFormatException */ public double getNumber(int index) throws Exception { check(numeric_fields, index); return Double.parseDouble(numeric_fields.get(index).getText()); } public List<JCheckBox> getCheckBoxes() { return new ArrayList<JCheckBox>(checkboxes); } /** May throw IllegalArgumentException */ public boolean getCheckbox(int index) throws Exception { check(checkboxes, index); return checkboxes.get(index).isSelected(); } public List<JComboBox> getChoices() { return new ArrayList<JComboBox>(choices); } public int getChoiceIndex(int index) throws Exception { check(choices, index); return choices.get(index).getSelectedIndex(); } public String getChoiceString(int index) throws Exception { return getChoiceObject(index).toString(); } public Object getChoiceObject(int index) throws Exception { check(choices, index); return choices.get(index).getSelectedItem(); } private void check(List<?> list, int index) throws Exception { if (index < 0 || index > list.size() -1) throw new IllegalArgumentException("Index out of bounds: " + index); } static abstract public class Setter { protected final Object ob; protected final String field; protected Runnable reaction; public Setter(Object ob, String field) { this.ob = ob; this.field = field; } public Setter(Object ob, String field, Runnable reaction) { this(ob, field); this.reaction = reaction; } /** Will set the field if no exception is thrown when reading it. */ public void setFrom(Component source) throws Exception { Field f = ob.getClass().getDeclaredField(field); f.setAccessible(true); f.set(ob, getValue(source)); //Utils.log2("set value of " + field + " to " + f.get(ob)); if (null != reaction) reaction.run(); } abstract public Object getValue(Component source); } static abstract public class NumericalSetter extends Setter { protected int min = Integer.MIN_VALUE, max = Integer.MAX_VALUE; public NumericalSetter(Object ob, String field) { super(ob, field); } public NumericalSetter(Object ob, String field, Runnable reaction) { super(ob, field, reaction); } public NumericalSetter(Object ob, String field, Runnable reaction, int min, int max) { super(ob, field, reaction); this.min = min; this.max = max; } public Object setFrom(Component source, int inc) throws Exception { Field f = ob.getClass().getDeclaredField(field); f.setAccessible(true); Object val = getValue(source, inc); f.set(ob, val); Utils.log2("set value of " + field + " to " + f.get(ob)); if (null != reaction) reaction.run(); return val; } abstract protected Object getValue(Component source, int inc); } static public class IntSetter extends NumericalSetter { public IntSetter(Object ob, String field) { super(ob, field); } public IntSetter(Object ob, String field, Runnable reaction) { super(ob, field, reaction); } public IntSetter(Object ob, String field, Runnable reaction, int min, int max) { super(ob, field, reaction, min, max); } public Object getValue(Component source) { return (int) Double.parseDouble(((JTextField)source).getText()); } protected Object getValue(Component source, int inc) { int val = ((int) Double.parseDouble(((JTextField)source).getText())) + inc; if (val < this.min) return this.min; if (val > this.max) return this.max; return val; } } static public class DoubleSetter extends NumericalSetter { public DoubleSetter(Object ob, String field) { super(ob, field); } public DoubleSetter(Object ob, String field, Runnable reaction) { super(ob, field, reaction); } public DoubleSetter(Object ob, String field, Runnable reaction, int min, int max) { super(ob, field, reaction, min, max); } public Object getValue(Component source) { return Double.parseDouble(((JTextField)source).getText()); } protected Object getValue(Component source, int inc) { double val = Double.parseDouble(((JTextField)source).getText()) + inc; if (val < this.min) return (double)this.min; if (val > this.max) return (double)this.max; return val; } } static public class FloatSetter extends NumericalSetter { public FloatSetter(Object ob, String field) { super(ob, field); } public FloatSetter(Object ob, String field, Runnable reaction) { super(ob, field, reaction); } public FloatSetter(Object ob, String field, Runnable reaction, int min, int max) { super(ob, field, reaction, min, max); } public Object getValue(Component source) { return Float.parseFloat(((JTextField)source).getText()); } public Object getValue(Component source, int inc) { float val = Float.parseFloat(((JTextField)source).getText()) + inc; if (val < this.min) return (float)this.min; if (val > this.max) return (float)this.max; return val; } } static public class BooleanSetter extends Setter { public BooleanSetter(Object ob, String field) { super(ob, field); } public BooleanSetter(Object ob, String field, Runnable reaction) { super(ob, field, reaction); } public Object getValue(Component source) { return ((JCheckBox)source).isSelected(); } } static public class StringSetter extends Setter { public StringSetter(Object ob, String field) { super(ob, field); } public StringSetter(Object ob, String field, Runnable reaction) { super(ob, field, reaction); } public Object getValue(Component source) { return ((JTextComponent)source).getText(); } } static public class ChoiceIntSetter extends Setter { public ChoiceIntSetter(Object ob, String field) { super(ob, field); } public ChoiceIntSetter(Object ob, String field, Runnable reaction) { super(ob, field, reaction); } public Object getValue(Component source) { return ((JComboBox)source).getSelectedIndex(); } } static public class ChoiceStringSetter extends Setter { public ChoiceStringSetter(Object ob, String field) { super(ob, field); } public ChoiceStringSetter(Object ob, String field, Runnable reaction) { super(ob, field, reaction); } public Object getValue(Component source) { return ((JComboBox)source).getSelectedItem().toString(); } } static public class ChoiceObjectSetter extends Setter { public ChoiceObjectSetter(Object ob, String field) { super(ob, field); } public ChoiceObjectSetter(Object ob, String field, Runnable reaction) { super(ob, field, reaction); } public Object getValue(Component source) { return ((JComboBox)source).getSelectedItem(); } } }