diff --git a/projects/samskivert/src/java/com/samskivert/util/RuntimeAdjust.java b/projects/samskivert/src/java/com/samskivert/util/RuntimeAdjust.java new file mode 100644 index 00000000..43630db6 --- /dev/null +++ b/projects/samskivert/src/java/com/samskivert/util/RuntimeAdjust.java @@ -0,0 +1,363 @@ +// +// $Id: RuntimeAdjust.java,v 1.1 2003/01/15 00:46:49 mdb Exp $ + +package com.samskivert.util; + +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.Iterator; + +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSeparator; +import javax.swing.JTextField; + +import com.samskivert.Log; +import com.samskivert.swing.CollapsiblePanel; +import com.samskivert.swing.GroupLayout; +import com.samskivert.swing.MultiLineLabel; +import com.samskivert.swing.ScrollablePanel; +import com.samskivert.swing.VGroupLayout; + +/** + * Provides a service where named variables can be registered as + * adjustable by the developer at runtime. Generally a special key is + * configured (via {@link DebugChords}) to pop up the adjustable variables + * interface which can then be used to toggle booleans, change integer + * values and generally adjust debugging and tuning parameters. This is + * not meant to be an interface for end-user configurable parameters, but + * to allow the developer to tweak runtime parameters more easily. + * + *

Adjustments are bound to a {@link Config} property which can be + * changed through the config interface or through the bound runtime + * adjust. + * + *

Note: adjusts are meant to be arranged in a two level + * hierarchy. An adjust's name, therefore, should be of the form: + * library.package.adjustment. The package + * component can consist of multiple words joined with a period, but the + * library will always be the first word before the first + * period and the adjustment always the last word after the + * final period. This is mainly only important for organizing the runtime + * adjustment editing interface. + */ +public class RuntimeAdjust +{ + /** + * Creates a Swing user interface that can be used to adjust all + * registered runtime adjustments. + */ + public static JPanel createAdjustEditor () + { + VGroupLayout layout = new VGroupLayout( + VGroupLayout.NONE, VGroupLayout.STRETCH, + 3, VGroupLayout.TOP); + layout.setOffAxisJustification(VGroupLayout.LEFT); + ScrollablePanel panel = new ScrollablePanel(layout); + panel.setTracksViewportWidth(true); + + Font font = panel.getFont(); + Font smaller = font.deriveFont(font.getSize()-1f); + Font bigger = font.deriveFont(Font.BOLD, font.getSize()+2f); + + String library = null; + String pkgname = null; + CollapsiblePanel pkgpanel = null; + + int acount = _adjusts.size(); + for (int ii = 0; ii < acount; ii++) { + Adjust adjust = (Adjust)_adjusts.get(ii); + + // create a new library label if necessary + if (!adjust.getLibrary().equals(library)) { + library = adjust.getLibrary(); + pkgname = null; + JLabel label = new JLabel(library, JLabel.CENTER); + label.setFont(bigger); + panel.add(label); + } + + // create a new package panel if necessary + if (!adjust.getPackage().equals(pkgname)) { + pkgname = adjust.getPackage(); + pkgpanel = new CollapsiblePanel(); + JCheckBox pkgcheck = new JCheckBox(pkgname); + pkgcheck.setSelected(true); + pkgpanel.setTrigger(pkgcheck, null, null); + pkgpanel.setTriggerContainer(pkgcheck); + pkgpanel.getContent().setLayout(layout); + pkgpanel.setCollapsed(false); + panel.add(pkgpanel); + } + + // add an entry for this adjustment + pkgpanel.getContent().add(new JSeparator()); + JPanel editor = adjust.getEditor(); + editor.setFont(smaller); + pkgpanel.getContent().add(editor); + } + + return panel; + } + + public static class BooleanAdjust extends Adjust + implements ActionListener + { + public BooleanAdjust (String descrip, String name, + Config config, boolean defval) + { + super(descrip, name, config); + _value = _config.getValue(_name, defval); + } + + public boolean getValue () + { + return _value; + } + + public void setValue (boolean value) + { + _config.setValue(_name, value); + } + + protected void populateEditor (JPanel editor) + { + editor.add(_valbox = new JCheckBox(), GroupLayout.FIXED); + _valbox.setSelected(getValue()); + _valbox.addActionListener(this); + } + + public void actionPerformed (ActionEvent e) + { + setValue(_valbox.isSelected()); + } + + public void propertyChange (PropertyChangeEvent evt) + { + _value = ((Boolean)evt.getNewValue()).booleanValue(); + adjusted(_value); + if (_valbox != null) { + _valbox.setSelected(_value); + } + } + + protected void adjusted (boolean newValue) + { +// Log.info(_name + " => " + newValue); + } + + protected boolean _value; + protected JCheckBox _valbox; + } + + public static class IntAdjust extends Adjust + implements ActionListener + { + public IntAdjust (String descrip, String name, + Config config, int defval) + { + super(descrip, name, config); + _value = _config.getValue(_name, defval); + } + + public int getValue () + { + return _value; + } + + public void setValue (int value) + { + _config.setValue(_name, value); + } + + protected void populateEditor (JPanel editor) + { + editor.add(_valbox = new JTextField(), GroupLayout.FIXED); + _valbox.addActionListener(this); + _valbox.setText("" + getValue()); + } + + public void actionPerformed (ActionEvent e) + { + try { + setValue(Integer.parseInt(_valbox.getText())); + } catch (NumberFormatException nfe) { + _valbox.setText("" + getValue()); + } + } + + public void propertyChange (PropertyChangeEvent evt) + { + _value = ((Integer)evt.getNewValue()).intValue(); + adjusted(_value, ((Integer)evt.getOldValue()).intValue()); + if (_valbox != null) { + _valbox.setText("" + _value); + } + } + + protected void adjusted (int newValue, int oldValue) + { +// Log.info(_name + " => " + newValue); + } + + protected int _value; + protected JTextField _valbox; + } + + public static class EnumAdjust extends Adjust + implements ActionListener + { + public EnumAdjust (String descrip, String name, + Config config, String[] values, String defval) + { + super(descrip, name, config); + _values = values; + _value = _config.getValue(_name, defval); + } + + public String getValue () + { + return _value; + } + + public void setValue (String value) + { + if (!ListUtil.containsEqual(_values, value)) { + Log.warning("Refusing invalid adjustment [name=" + _name + + ", values=" + StringUtil.toString(_values) + + ", value=" + value + "]."); + } else { + _config.setValue(_name, value); + } + } + + protected void populateEditor (JPanel editor) + { + editor.add(_valbox = new JComboBox(_values), GroupLayout.FIXED); + _valbox.addActionListener(this); + _valbox.setSelectedItem(getValue()); + } + + public void actionPerformed (ActionEvent e) + { + setValue((String)_valbox.getSelectedItem()); + } + + public void propertyChange (PropertyChangeEvent evt) + { + _value = (String)evt.getNewValue(); + adjusted(_value, (String)evt.getOldValue()); + if (_valbox != null) { + _valbox.setSelectedItem(_value); + } + } + + protected void adjusted (String newValue, String oldValue) + { +// Log.info(_name + " => " + newValue); + } + + protected String _value; + protected String[] _values; + protected JComboBox _valbox; + } + + protected abstract static class Adjust + implements PropertyChangeListener, Comparable + { + public Adjust (String descrip, String name, Config config) + { + _name = name; + _descrip = descrip; + _config = config; + _config.addPropertyChangeListener(_name, this); + + // validate the structure of the name + int fdidx = _name.indexOf("."), ldidx = _name.lastIndexOf("."); + if (fdidx == -1 || ldidx == -1) { + Log.warning("Invalid adjustment name '" + _name + + "', must be of the form " + + "'library.package.adjustment'."); + return; + } + + // make sure there isn't another with the same name + int idx = _adjusts.binarySearch(this); + if (idx >= 0) { + Log.warning("Error: duplicate adjust registration " + + "[new=" + this + + ", old=" + _adjusts.get(idx) + "]."); + return; + } + + _adjusts.insertSorted(this); // keep 'em sorted + } + + public boolean equals (Object other) + { + return _name.equals(((Adjust)other)._name); + } + + public int compareTo (Object other) + { + return _name.compareTo(((Adjust)other)._name); + } + + public String getName () + { + return _name; + } + + public String getDescription () + { + return _descrip; + } + + public String getLibrary () + { + return _name.substring(0, _name.indexOf(".")); + } + + public String getPackage () + { + return _name.substring(_name.indexOf(".")+1, + _name.lastIndexOf(".")); + } + + public String getAdjustment () + { + return _name.substring(_name.lastIndexOf(".")+1); + } + + public JPanel getEditor () + { + if (_editor == null) { + _editor = GroupLayout.makeHBox(GroupLayout.STRETCH); + _editor.add(new MultiLineLabel(_descrip, MultiLineLabel.LEFT, + MultiLineLabel.VERTICAL, 25)); + populateEditor(_editor); + } + return _editor; + } + + protected abstract void populateEditor (JPanel editor); + + public String toString () + { + return StringUtil.shortClassName(this) + + "[name=" + _name + ", desc=" + _descrip + "]"; + } + + protected String _name, _descrip; + protected Config _config; + protected JPanel _editor; + } + + protected static SortableArrayList _adjusts = new SortableArrayList(); +}