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();
+}