001    package nl.cwi.sen1.gui.plugin;
002    
003    import java.awt.Color;
004    import java.awt.Dimension;
005    import java.awt.event.MouseAdapter;
006    import java.awt.event.MouseEvent;
007    
008    import javax.swing.BorderFactory;
009    import javax.swing.Box;
010    import javax.swing.BoxLayout;
011    import javax.swing.JLabel;
012    import javax.swing.JPanel;
013    import javax.swing.JSlider;
014    import javax.swing.event.ChangeEvent;
015    import javax.swing.event.ChangeListener;
016    
017    import nl.cwi.sen1.util.Preferences;
018    import prefuse.util.force.Force;
019    import prefuse.util.force.ForceSimulator;
020    
021    /* A panel with sliders to configure a ForceSimulator. Adapted from
022     * the prefuse implementation. 
023     *
024     */
025    public class GraphForcePanel extends JPanel {
026        private ForceSimulator fsim;
027    
028        public GraphForcePanel(ForceSimulator fsim, Preferences prefs) {
029            this.fsim = fsim;
030            this.setBackground(Color.WHITE);
031            initUI(prefs);
032        }
033    
034        private void initUI(Preferences prefs) {
035            this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
036            Force[] forces = fsim.getForces();
037            // create a bordered box for every force, containing a
038            // configurator for every parameter of that force
039            for (int i = 0; i < forces.length; i++) {
040                Force f = forces[i];
041                Box box = new Box(BoxLayout.Y_AXIS);
042    
043                // We use the class name and the parameter name to get
044                // properties from the preferences
045                String name = f.getClass().getName();
046                name = name.substring(name.lastIndexOf(".") + 1);
047    
048                String label = prefs.getString("force." + name + ".label");
049                box.setBorder(BorderFactory.createTitledBorder(label));
050    
051                for (int j = 0; j < f.getParameterCount(); j++) {
052                    String parName = f.getParameterName(j);
053                    final float min = prefs.getFloat("force." + name + "."
054                            + parName + ".min");
055                    final float max = prefs.getFloat("force." + name + "."
056                            + parName + ".max");
057                    final float def = prefs.getFloat("force." + name + "."
058                            + parName + ".default");
059    
060                    final JSlider slider = createSlider(f, j, min, max, def);
061    
062                    final String parLabel = prefs.getString("force." + name + "."
063                            + parName + ".label");
064                    JLabel tag = new JLabel(parLabel);
065                    tag.setPreferredSize(new Dimension(100, 20));
066                    tag.setMaximumSize(new Dimension(100, 20));
067                    tag.setToolTipText("click to return to default");
068                    tag.addMouseListener(new MouseAdapter() {
069                        public void mouseClicked(MouseEvent e) {
070                            slider.setValue(percentage(def, min, max));
071                        }
072                    });
073    
074                    Box paramBox = new Box(BoxLayout.X_AXIS);
075                    paramBox.add(tag);
076                    paramBox.add(Box.createHorizontalStrut(10));
077                    paramBox.add(Box.createHorizontalGlue());
078                    paramBox.add(slider);
079                    box.add(paramBox);
080                }
081                this.add(box);
082            }
083            this.add(Box.createVerticalGlue());
084        }
085    
086        private static int percentage(float value, float min, float max) {
087            // first normalize the range to start at zero
088            max = max - min;
089    
090            // then compute the percentage
091            value = value - min;
092            return (int) ((value / max) * 100);
093        }
094    
095        private static float value(int percentage, float min, float max) {
096            float value;
097            // first normalize the range to start at zero
098            max = max - min;
099    
100            // compute the value
101            value = (((float) percentage) / 100) * max;
102    
103            // shift range back
104            value += min;
105    
106            return value;
107        }
108    
109        private JSlider createSlider(Force f, int param, final float min,
110                final float max, final float def) {
111            final JSlider slider = new JSlider(0, 100);
112            slider.setBackground(Color.WHITE);
113    
114            // We store both a reference to the force and the index of the property
115            // in the widget
116            slider.putClientProperty("force", f);
117            slider.putClientProperty("param", new Integer(param));
118            slider.setToolTipText("" + def);
119            slider.setValue(percentage(def, min, max));
120    
121            slider.addChangeListener(new ChangeListener() {
122                public void stateChanged(ChangeEvent event) {
123                    JSlider s = (JSlider) event.getSource();
124    
125                    // retrieve the force object, and the index of the property we
126                    // want
127                    Force f = (Force) s.getClientProperty("force");
128                    Integer param = (Integer) s.getClientProperty("param");
129    
130                    // forward the new value to the force, and update the tooltip
131                    float val = value(s.getValue(), min, max);
132                    f.setParameter(param.intValue(), val);
133                    s.setToolTipText("" + val);
134                }
135            });
136    
137            return slider;
138        }
139    }