Better editor
authorBill Kuker <bkuker@billkuker.com>
Tue, 14 Jul 2009 01:04:13 +0000 (01:04 +0000)
committerBill Kuker <bkuker@billkuker.com>
Tue, 14 Jul 2009 01:04:13 +0000 (01:04 +0000)
Made grain panel layout overridable by subclass

src/com/billkuker/rocketry/motorsim/visual/BurnPanel.java
src/com/billkuker/rocketry/motorsim/visual/GrainPanel.java
src/com/billkuker/rocketry/motorsim/visual/MotorEditor.java
src/com/billkuker/rocketry/motorsim/visual/MotorWorkbench.java
src/com/billkuker/rocketry/motorsim/visual/workbench/WorkbenchTreeCellRenderer.java [new file with mode: 0644]
src/com/billkuker/rocketry/motorsim/visual/workbench/WorkbenchTreeModel.java [new file with mode: 0644]

index 4fb45af174548d24cf25a68a025a1c0b0ff5a139..4715257d194c033bb33cc71ee6d9cf1bb5dcd8b4 100644 (file)
@@ -71,7 +71,13 @@ public class BurnPanel extends JPanel {
                        tp.setDividerLocation(.5);\r
                        tp.setResizeWeight(.5);\r
                        \r
-                       grain = new GrainPanel(burn.getMotor().getGrain());\r
+                       grain = new GrainPanel(burn.getMotor().getGrain()){\r
+                               @Override protected void addComponents(java.awt.Component crossSection, java.awt.Component slider, java.awt.Component label, java.awt.Component area, java.awt.Component volume) {\r
+                                       JSplitPane h = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, crossSection, area); \r
+                                       add(h, BorderLayout.CENTER);\r
+                                       h.resetToPreferredSizes();\r
+                               };\r
+                       };\r
                        \r
                        JSplitPane grains = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, grain, burnRate);\r
                        grains.setDividerLocation(.5);\r
index 6fa3b34853ccbd95f942fa5b1ab04666759cb029..5ace339f5066b82810695ec398627e24582bb3d7 100644 (file)
@@ -2,6 +2,7 @@ package com.billkuker.rocketry.motorsim.visual;
 \r
 import java.awt.BorderLayout;\r
 import java.awt.Color;\r
+import java.awt.Component;\r
 import java.awt.Dimension;\r
 import java.awt.Graphics;\r
 import java.awt.Graphics2D;\r
@@ -82,20 +83,39 @@ public class GrainPanel extends JPanel {
                        e.printStackTrace();\r
                }\r
                \r
+               addComponents(\r
+                               xc = new XC(grain),\r
+                               new SL(),\r
+                               l,\r
+                               area,\r
+                               volume\r
+               );\r
+       }\r
+       \r
+       protected void addComponents(\r
+                       Component crossSection,\r
+                       Component slider,\r
+                       Component label,\r
+                       Component area,\r
+                       Component volume\r
+       ){\r
                \r
-               JSplitPane charts = new JSplitPane(JSplitPane.VERTICAL_SPLIT, area, volume);\r
-               charts.setDividerLocation(.5);\r
-               charts.setResizeWeight(.5);\r
-               \r
-               JPanel left = new JPanel(new BorderLayout());\r
+               JSplitPane v = new JSplitPane(JSplitPane.VERTICAL_SPLIT);\r
+               JSplitPane h = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);\r
                \r
-               add(xc = new XC(grain), BorderLayout.CENTER);\r
-               left.add(xc);\r
-\r
-               left.add(l, BorderLayout.NORTH);\r
-               left.add( new SL(), BorderLayout.SOUTH);\r
+               JPanel graphics = new JPanel(new BorderLayout());\r
+               graphics.add(crossSection, BorderLayout.CENTER);\r
+               graphics.add(label, BorderLayout.NORTH);\r
+               graphics.add(slider, BorderLayout.SOUTH);\r
        \r
-               add(new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, left, charts));\r
+               v.setTopComponent(h);\r
+               v.setBottomComponent(area);\r
+               h.setLeftComponent(graphics);\r
+               h.setRightComponent(volume);\r
+               add(v);\r
+               \r
+               h.resetToPreferredSizes();\r
+               v.resetToPreferredSizes();\r
 \r
        }\r
        \r
@@ -127,8 +147,10 @@ public class GrainPanel extends JPanel {
                        if ( w < 40 )\r
                                w = 40;\r
                        \r
-                       setMinimumSize(new Dimension(240+w,250));\r
-\r
+                       Dimension sz = new Dimension(240+w, 250);\r
+                       setMinimumSize(sz);\r
+                       setPreferredSize(sz);\r
+                       setMaximumSize(sz);\r
                }\r
                public void paint(Graphics g){\r
                        super.paint(g);\r
index 7c75b240dc6c8bd5df83aea0e55fdadfcdd39996..5044bf5b40ac7d75f0ce517fd6d1b24bcda20131 100644 (file)
@@ -1,5 +1,6 @@
 package com.billkuker.rocketry.motorsim.visual;\r
 \r
+import java.awt.BorderLayout;\r
 import java.awt.event.ActionEvent;\r
 import java.awt.event.ActionListener;\r
 import java.beans.PropertyChangeEvent;\r
@@ -26,6 +27,7 @@ import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
 import org.fife.ui.rsyntaxtextarea.SyntaxConstants;\r
 import org.jscience.physics.amount.Amount;\r
 \r
+import com.billkuker.rocketry.motorsim.Burn;\r
 import com.billkuker.rocketry.motorsim.Chamber;\r
 import com.billkuker.rocketry.motorsim.ConvergentDivergentNozzle;\r
 import com.billkuker.rocketry.motorsim.CylindricalChamber;\r
@@ -34,6 +36,10 @@ import com.billkuker.rocketry.motorsim.Grain;
 import com.billkuker.rocketry.motorsim.Motor;\r
 import com.billkuker.rocketry.motorsim.MotorPart;\r
 import com.billkuker.rocketry.motorsim.Nozzle;\r
+import com.billkuker.rocketry.motorsim.fuel.EditableFuel;\r
+import com.billkuker.rocketry.motorsim.fuel.KNDX;\r
+import com.billkuker.rocketry.motorsim.fuel.KNER;\r
+import com.billkuker.rocketry.motorsim.fuel.KNSB;\r
 import com.billkuker.rocketry.motorsim.fuel.KNSU;\r
 import com.billkuker.rocketry.motorsim.grain.CoredCylindricalGrain;\r
 import com.billkuker.rocketry.motorsim.grain.Finocyl;\r
@@ -42,31 +48,40 @@ import com.billkuker.rocketry.motorsim.grain.MultiGrain;
 import com.billkuker.rocketry.motorsim.grain.RodAndTubeGrain;\r
 import com.billkuker.rocketry.motorsim.io.MotorIO;\r
 \r
-public class MotorEditor extends JTabbedPane implements PropertyChangeListener, DocumentListener{\r
+public class MotorEditor extends JTabbedPane implements PropertyChangeListener,\r
+               DocumentListener {\r
        private static final long serialVersionUID = 1L;\r
        RSyntaxTextArea text = new RSyntaxTextArea();\r
        Motor motor;\r
        GrainEditor grainEditor;\r
-       \r
-       private class GrainChooser extends JPanel{\r
+       BurnTab bt;\r
+\r
+       private static final int XML_TAB = 0;\r
+       private static final int CASING_TAB = 1;\r
+       private static final int GRAIN_TAB = 2;\r
+       private static final int FUEL_TAB = 3;\r
+       private static final int BURN_TAB = 4;\r
+\r
+       private Class[] grainTypes = { CoredCylindricalGrain.class, Finocyl.class,\r
+                       Moonburner.class, RodAndTubeGrain.class };\r
+\r
+       private Class[] fuelTypes = { KNSB.class, KNSU.class, KNER.class,\r
+                       KNDX.class, EditableFuel.class };\r
+\r
+       private abstract class Chooser<T> extends JPanel {\r
                private static final long serialVersionUID = 1L;\r
-               @SuppressWarnings("unchecked")\r
-               private Class[] types = {\r
-                                       CoredCylindricalGrain.class,\r
-                                       Finocyl.class,\r
-                                       Moonburner.class,\r
-                                       RodAndTubeGrain.class\r
-                               };\r
-               @SuppressWarnings("unchecked")\r
-               public GrainChooser(){\r
+               private Class<? extends T>[] types;\r
+\r
+               public Chooser(Class<? extends T>... ts) {\r
+                       types = ts;\r
                        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));\r
-                       for ( final Class c : types ){\r
+                       for (final Class<? extends T> c : types) {\r
                                JButton b = new JButton(c.getSimpleName());\r
                                add(b);\r
-                               b.addActionListener(new ActionListener(){\r
+                               b.addActionListener(new ActionListener() {\r
                                        public void actionPerformed(ActionEvent e) {\r
                                                try {\r
-                                                       grainChosen((Grain)c.newInstance());\r
+                                                       choiceMade(c.newInstance());\r
                                                } catch (InstantiationException e1) {\r
                                                        e1.printStackTrace();\r
                                                } catch (IllegalAccessException e1) {\r
@@ -76,152 +91,184 @@ public class MotorEditor extends JTabbedPane implements PropertyChangeListener,
                                });\r
                        }\r
                }\r
-               protected void grainChosen(Grain g){\r
-                       System.out.println(g);\r
+\r
+               protected abstract void choiceMade(T o);\r
+       }\r
+\r
+       private class BurnTab extends JPanel {\r
+               public BurnTab() {\r
+                       setLayout(new BorderLayout());\r
+                       setName("Burn");\r
+                       reBurn();\r
+               }\r
+\r
+               public void reBurn() {\r
+                       removeAll();\r
+                       new Thread() {\r
+                               public void run() {\r
+                                       final Burn b = new Burn(motor);\r
+                                       final BurnPanel bp = new BurnPanel(b);\r
+                                       SwingUtilities.invokeLater(new Thread() {\r
+                                               public void run() {\r
+                                                       add(bp, BorderLayout.CENTER);\r
+                                               }\r
+                                       });\r
+                               }\r
+                       }.start();\r
                }\r
        }\r
-       \r
-       private class GrainEditor extends JSplitPane{\r
+\r
+       private class GrainEditor extends JSplitPane {\r
                private static final long serialVersionUID = 1L;\r
 \r
-               public GrainEditor(final Grain g){\r
+               public GrainEditor(final Grain g) {\r
                        super(JSplitPane.HORIZONTAL_SPLIT);\r
+                       setName("Grain");\r
                        setRightComponent(new GrainPanel(g));\r
-                       if ( g instanceof Grain.Composite ){\r
+                       if (g instanceof Grain.Composite) {\r
                                final JPanel p = new JPanel();\r
                                p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));\r
                                p.add(new Editor(g));\r
-                               for ( Grain gg : ((Grain.Composite)g).getGrains() ){\r
+                               for (Grain gg : ((Grain.Composite) g).getGrains()) {\r
                                        final int grainEditorIndex = p.getComponentCount() + 1;\r
-                                       p.add(new GrainChooser(){\r
-                                               /**\r
-                                                * \r
-                                                */\r
+                                       p.add(new Chooser<Grain>(grainTypes) {\r
                                                private static final long serialVersionUID = 1L;\r
 \r
                                                @Override\r
-                                               protected void grainChosen(Grain ng){\r
-                                                       if ( g instanceof MultiGrain ){\r
+                                               protected void choiceMade(Grain ng) {\r
+                                                       if (g instanceof MultiGrain) {\r
                                                                ((MultiGrain) g).setGrain(ng);\r
                                                                p.remove(grainEditorIndex);\r
                                                                p.add(new Editor(ng), grainEditorIndex);\r
                                                                p.remove(0);\r
                                                                p.add(new Editor(g), 0);\r
-                                                               //System.out.println("Chose new grain");\r
+                                                               // System.out.println("Chose new grain");\r
                                                        }\r
                                                }\r
                                        });\r
                                        p.add(new Editor(gg));\r
-                                       if ( gg instanceof MotorPart ){\r
-                                               ((MotorPart)gg).addPropertyChangeListener(MotorEditor.this);\r
+                                       if (gg instanceof MotorPart) {\r
+                                               ((MotorPart) gg)\r
+                                                               .addPropertyChangeListener(MotorEditor.this);\r
                                        }\r
                                }\r
                                setLeftComponent(p);\r
                        } else {\r
                                setLeftComponent(new Editor(g));\r
                        }\r
-                       setDividerLocation(.25);\r
-                       setResizeWeight(.25);\r
-                       if ( g instanceof MotorPart ){\r
-                               ((MotorPart)g).addPropertyChangeListener(MotorEditor.this);\r
+                       // setDividerLocation(.25);\r
+                       // setResizeWeight(.25);\r
+                       if (g instanceof MotorPart) {\r
+                               ((MotorPart) g).addPropertyChangeListener(MotorEditor.this);\r
                        }\r
                }\r
        }\r
-       \r
-       private class FuelEditor extends JSplitPane{\r
-               /**\r
-                * \r
-                */\r
+\r
+       private class FuelEditor extends JSplitPane {\r
                private static final long serialVersionUID = 1L;\r
 \r
-               public FuelEditor(Fuel f){\r
+               public FuelEditor(Fuel f) {\r
                        super(JSplitPane.HORIZONTAL_SPLIT);\r
+                       setName("Fuel");\r
                        Chart<Pressure, Velocity> burnRate;\r
                        try {\r
-                               burnRate = new Chart<Pressure, Velocity>(\r
-                                               SI.MEGA(SI.PASCAL),\r
-                                               SI.METERS_PER_SECOND,\r
-                                               f,\r
-                                               "burnRate");\r
+                               burnRate = new Chart<Pressure, Velocity>(SI.MEGA(SI.PASCAL),\r
+                                               SI.METERS_PER_SECOND, f, "burnRate");\r
                        } catch (NoSuchMethodException e) {\r
                                throw new Error(e);\r
                        }\r
-                       burnRate.setDomain(\r
-                                       burnRate.new IntervalDomain(\r
-                                                       Amount.valueOf(0, SI.MEGA(SI.PASCAL)),\r
-                                                       Amount.valueOf(11, SI.MEGA(SI.PASCAL)),\r
-                                                       20\r
-                                                       ));\r
-\r
-                       setLeftComponent(new Editor(f));\r
+                       burnRate.setDomain(burnRate.new IntervalDomain(Amount.valueOf(0, SI\r
+                                       .MEGA(SI.PASCAL)), Amount.valueOf(11, SI.MEGA(SI.PASCAL)),\r
+                                       20));\r
+\r
+                       final JPanel p = new JPanel();\r
+                       p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));\r
+\r
+                       p.add(new Chooser<Fuel>(fuelTypes) {\r
+                               @Override\r
+                               protected void choiceMade(Fuel o) {\r
+                                       motor.setFuel(o);\r
+                                       removeTabAt(FUEL_TAB);\r
+                                       MotorEditor.this.add(new FuelEditor(motor.getFuel()),\r
+                                                       FUEL_TAB);\r
+                                       setSelectedIndex(FUEL_TAB);\r
+                               }\r
+                       });\r
+                       p.add(new Editor(f));\r
+                       try {\r
+                               p.add(new Editor(f.getCombustionProduct()));\r
+                       } catch (Exception e) {\r
+\r
+                       }\r
+\r
+                       setLeftComponent(p);\r
                        setRightComponent(burnRate);\r
-                       setDividerLocation(.25);\r
-                       setResizeWeight(.25);\r
-                       if ( f instanceof MotorPart ){\r
-                               ((MotorPart)f).addPropertyChangeListener(MotorEditor.this);\r
+                       // setDividerLocation(.25);\r
+                       // setResizeWeight(.25);\r
+                       if (f instanceof MotorPart) {\r
+                               ((MotorPart) f).addPropertyChangeListener(MotorEditor.this);\r
                        }\r
                }\r
        }\r
-       \r
-       private class CaseEditor extends JSplitPane{\r
 \r
-               /**\r
-                * \r
-                */\r
+       private class CaseEditor extends JSplitPane {\r
                private static final long serialVersionUID = 1L;\r
 \r
-               public CaseEditor(Nozzle n, Chamber c){\r
-                       super(JSplitPane.HORIZONTAL_SPLIT, \r
-                                       new JSplitPane(JSplitPane.VERTICAL_SPLIT,\r
-                                                       new Editor(n), new Editor(c)), new NozzlePanel(n));\r
-                       JSplitPane parts = (JSplitPane)getLeftComponent();\r
-                       parts.setDividerLocation(.5);\r
-                       parts.setResizeWeight(.5);\r
-                       setDividerLocation(.25);\r
-                       setResizeWeight(.25);\r
-\r
-                       if ( n instanceof MotorPart ){\r
-                               ((MotorPart)n).addPropertyChangeListener(MotorEditor.this);\r
+               public CaseEditor(Nozzle n, Chamber c) {\r
+                       super(JSplitPane.HORIZONTAL_SPLIT);\r
+                       setName("Casing");\r
+                       JPanel parts = new JPanel();\r
+                       parts.setLayout(new BoxLayout(parts, BoxLayout.Y_AXIS));\r
+                       setLeftComponent(parts);\r
+                       setRightComponent(new NozzlePanel(n));\r
+\r
+                       parts.add(new Editor(c));\r
+                       parts.add(new Editor(n));\r
+\r
+                       if (n instanceof MotorPart) {\r
+                               ((MotorPart) n).addPropertyChangeListener(MotorEditor.this);\r
                        }\r
-                       if ( c instanceof MotorPart ){\r
-                               ((MotorPart)c).addPropertyChangeListener(MotorEditor.this);\r
+                       if (c instanceof MotorPart) {\r
+                               ((MotorPart) c).addPropertyChangeListener(MotorEditor.this);\r
                        }\r
                }\r
        }\r
-       \r
+\r
        {\r
                text.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_XML);\r
 \r
        }\r
-       \r
-       public MotorEditor(Motor m){\r
+\r
+       public MotorEditor(Motor m) {\r
                super(JTabbedPane.BOTTOM);\r
                text.getDocument().addDocumentListener(this);\r
-               addTab("XML", text);\r
+               text.setName("XML");\r
+               add(text, XML_TAB);\r
                setMotor(m, true);\r
        }\r
-       \r
-       private void reText(){\r
+\r
+       private void reText() {\r
                try {\r
                        text.setText(MotorIO.writeMotor(motor));\r
                } catch (IOException e) {\r
                        throw new Error(e);\r
                }\r
        }\r
-       \r
-       private void setMotor(Motor m, boolean retext){\r
+\r
+       private void setMotor(Motor m, boolean retext) {\r
                motor = m;\r
-               if ( retext )\r
+               if (retext)\r
                        reText();\r
-               if ( grainEditor != null)\r
+               if (grainEditor != null)\r
                        remove(grainEditor);\r
-       while ( getTabCount() > 1       )\r
-               removeTabAt(1);\r
-               add("Casing", new CaseEditor(motor.getNozzle(), motor.getChamber()));\r
-               add("Grain", new GrainEditor(motor.getGrain()));\r
-               add("Fuel", new FuelEditor(motor.getFuel()));\r
+               while (getTabCount() > 1)\r
+                       removeTabAt(1);\r
+               add(new CaseEditor(motor.getNozzle(), motor.getChamber()), CASING_TAB);\r
+               add(new GrainEditor(motor.getGrain()), GRAIN_TAB);\r
+               add(new FuelEditor(motor.getFuel()), FUEL_TAB);\r
+               add(bt = new BurnTab(), BURN_TAB);\r
        }\r
-       \r
+\r
        @Deprecated\r
        public static Motor defaultMotor() {\r
                Motor m = new Motor();\r
@@ -242,8 +289,7 @@ public class MotorEditor extends JTabbedPane implements PropertyChangeListener,
                        throw new Error(v);\r
                }\r
 \r
-               m.setGrain(new MultiGrain(g,2));\r
-\r
+               m.setGrain(new MultiGrain(g, 2));\r
 \r
                ConvergentDivergentNozzle n = new ConvergentDivergentNozzle();\r
                n.setThroatDiameter(Amount.valueOf(7.962, SI.MILLIMETER));\r
@@ -253,17 +299,26 @@ public class MotorEditor extends JTabbedPane implements PropertyChangeListener,
 \r
                return m;\r
        }\r
-       \r
+\r
+       public void focusOnObject(Object o) {\r
+               if (o instanceof Grain)\r
+                       setSelectedIndex(GRAIN_TAB);\r
+               if (o instanceof Chamber || o instanceof Nozzle)\r
+                       setSelectedIndex(CASING_TAB);\r
+               if (o instanceof Fuel || o instanceof Fuel.CombustionProduct)\r
+                       setSelectedIndex(FUEL_TAB);\r
+       }\r
+\r
        @Deprecated\r
-       public void showAsWindow(){\r
+       public void showAsWindow() {\r
                JFrame f = new JFrame();\r
-               f.setSize(1024,768);\r
+               f.setSize(1024, 768);\r
                f.setContentPane(this);\r
                f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);\r
                f.setVisible(true);\r
        }\r
-       \r
-       public static void main(String args[]) throws Exception{\r
+\r
+       public static void main(String args[]) throws Exception {\r
                try {\r
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());\r
                } catch (Exception e1) {\r
@@ -274,19 +329,20 @@ public class MotorEditor extends JTabbedPane implements PropertyChangeListener,
 \r
        public void propertyChange(PropertyChangeEvent evt) {\r
                reText();\r
+               bt.reBurn();\r
        }\r
 \r
-\r
        public void changedUpdate(DocumentEvent e) {\r
                try {\r
                        final Motor m = MotorIO.readMotor(text.getText());\r
-                       SwingUtilities.invokeLater(new Runnable(){\r
+                       SwingUtilities.invokeLater(new Runnable() {\r
                                public void run() {\r
                                        setMotor(m, false);\r
-                               }});\r
+                               }\r
+                       });\r
                        System.out.println("Motor Updated");\r
                } catch (Throwable e1) {\r
-                       //Leave blank, motor might be broken\r
+                       // Leave blank, motor might be broken\r
                }\r
        }\r
 \r
index 0158dcdc064e49d16cbfa960dd59f93cfc3490a0..d497e2dd5ae74be2da231228493cb635ea7fd378 100644 (file)
@@ -1,49 +1,83 @@
 package com.billkuker.rocketry.motorsim.visual;\r
 \r
 import java.awt.BorderLayout;\r
+import java.awt.Dimension;\r
+import java.util.HashMap;\r
 \r
-import javax.swing.BoxLayout;\r
 import javax.swing.JButton;\r
 import javax.swing.JFrame;\r
 import javax.swing.JPanel;\r
+import javax.swing.JScrollPane;\r
 import javax.swing.JSplitPane;\r
 import javax.swing.JTabbedPane;\r
 import javax.swing.JToolBar;\r
 import javax.swing.JTree;\r
 import javax.swing.UIManager;\r
 import javax.swing.WindowConstants;\r
+import javax.swing.event.TreeSelectionEvent;\r
+import javax.swing.event.TreeSelectionListener;\r
+import javax.swing.tree.DefaultMutableTreeNode;\r
+import javax.swing.tree.TreePath;\r
+import javax.swing.tree.TreeSelectionModel;\r
 \r
-public class MotorWorkbench extends JFrame {\r
+import com.billkuker.rocketry.motorsim.Motor;\r
+import com.billkuker.rocketry.motorsim.visual.workbench.WorkbenchTreeCellRenderer;\r
+import com.billkuker.rocketry.motorsim.visual.workbench.WorkbenchTreeModel;\r
+\r
+public class MotorWorkbench extends JFrame implements TreeSelectionListener {\r
+       private static final long serialVersionUID = 1L;\r
        private JPanel top;\r
        private JSplitPane split;\r
        private JTree tree;\r
        private JTabbedPane motors;\r
        private JToolBar bar;\r
-       public MotorWorkbench(){\r
-               setSize(1024,768);\r
-               top = new JPanel( new BorderLayout());\r
+       private WorkbenchTreeModel tm;\r
+\r
+       private HashMap<Motor, MotorEditor> m2e = new HashMap<Motor, MotorEditor>();\r
+\r
+       public MotorWorkbench() {\r
+               setSize(1024, 768);\r
+               top = new JPanel(new BorderLayout());\r
                setContentPane(top);\r
-               \r
+\r
                bar = new JToolBar();\r
                bar.add(new JButton("Burn"));\r
                top.add(bar, BorderLayout.PAGE_START);\r
-               \r
+\r
                motors = new JTabbedPane();\r
-               motors.addTab("Motor 1", new MotorEditor(MotorEditor.defaultMotor()));\r
-               motors.addTab("Motor 2", new MotorEditor(MotorEditor.defaultMotor()));\r
-               \r
-               tree = new JTree();\r
-               \r
-               split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, tree, motors);\r
-               split.setDividerLocation(.25);\r
-               split.setResizeWeight(.25);\r
+\r
+               tree = new JTree(tm = new WorkbenchTreeModel());\r
+               tree.setCellRenderer(new WorkbenchTreeCellRenderer());\r
+               tree.getSelectionModel().setSelectionMode(\r
+                               TreeSelectionModel.SINGLE_TREE_SELECTION);\r
+               tree.setPreferredSize(new Dimension(200, 100));\r
+\r
+               // Listen for when the selection changes.\r
+               tree.addTreeSelectionListener(this);\r
+\r
+               split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JScrollPane(\r
+                               tree), motors);\r
+               //split.setDividerLocation(.25);\r
+               //split.setResizeWeight(.25);\r
                top.add(split, BorderLayout.CENTER);\r
-               \r
+\r
                setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);\r
                setVisible(true);\r
+\r
+               Motor mot = MotorEditor.defaultMotor();\r
+               addMotor(mot);\r
+               //mot = MotorEditor.defaultMotor();\r
+               //addMotor(mot);\r
+       }\r
+\r
+       public void addMotor(Motor m) {\r
+               tm.addMotor(m);\r
+               MotorEditor e = new MotorEditor(m);\r
+               m2e.put(m, e);\r
+               motors.addTab("Motor", e);\r
        }\r
-       \r
-       public static void main(String args[]) throws Exception{\r
+\r
+       public static void main(String args[]) throws Exception {\r
                try {\r
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());\r
                } catch (Exception e1) {\r
@@ -51,4 +85,25 @@ public class MotorWorkbench extends JFrame {
                }\r
                new MotorWorkbench().show();\r
        }\r
+\r
+       @Override\r
+       public void valueChanged(TreeSelectionEvent e) {\r
+               Motor m = getMotor(e.getPath());\r
+\r
+               motors.setSelectedComponent(m2e.get(m));\r
+               \r
+               if ( e.getPath().getLastPathComponent() instanceof DefaultMutableTreeNode ){\r
+                       Object o = ((DefaultMutableTreeNode)e.getPath().getLastPathComponent()).getUserObject();\r
+                       m2e.get(m).focusOnObject(o);\r
+               }\r
+       }\r
+\r
+       private Motor getMotor(TreePath p) {\r
+               if (p.getLastPathComponent() instanceof WorkbenchTreeModel.MotorNode) {\r
+                       return ((WorkbenchTreeModel.MotorNode) p.getLastPathComponent())\r
+                                       .getUserObject();\r
+               } else if (p.getPath().length > 1)\r
+                       return getMotor(p.getParentPath());\r
+               return null;\r
+       }\r
 }\r
diff --git a/src/com/billkuker/rocketry/motorsim/visual/workbench/WorkbenchTreeCellRenderer.java b/src/com/billkuker/rocketry/motorsim/visual/workbench/WorkbenchTreeCellRenderer.java
new file mode 100644 (file)
index 0000000..4157d1b
--- /dev/null
@@ -0,0 +1,33 @@
+package com.billkuker.rocketry.motorsim.visual.workbench;
+
+import java.awt.Component;
+
+import javax.swing.JTree;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeCellRenderer;
+
+import com.billkuker.rocketry.motorsim.Motor;
+
+public class WorkbenchTreeCellRenderer extends DefaultTreeCellRenderer {
+       private static final long serialVersionUID = 1L;
+
+       @Override
+       public Component getTreeCellRendererComponent(JTree tree, Object value,
+                       boolean sel, boolean expanded, boolean leaf, int row,
+                       boolean hasFocus) {
+               super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf,
+                               row, hasFocus);
+               if (value instanceof DefaultMutableTreeNode) {
+                       value = ((DefaultMutableTreeNode) value).getUserObject();
+               }
+               if (value instanceof Motor) {
+                       setText(((Motor) value).getName());
+               } else if ( value == null ) {
+                       setText("");
+               } else {
+                       setText(value.getClass().getSimpleName());
+               }
+
+               return this;
+       }
+}
diff --git a/src/com/billkuker/rocketry/motorsim/visual/workbench/WorkbenchTreeModel.java b/src/com/billkuker/rocketry/motorsim/visual/workbench/WorkbenchTreeModel.java
new file mode 100644 (file)
index 0000000..1bb769e
--- /dev/null
@@ -0,0 +1,102 @@
+package com.billkuker.rocketry.motorsim.visual.workbench;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeModel;
+
+import com.billkuker.rocketry.motorsim.Motor;
+import com.billkuker.rocketry.motorsim.MotorPart;
+import com.billkuker.rocketry.motorsim.grain.MultiGrain;
+
+public class WorkbenchTreeModel extends DefaultTreeModel {
+
+       private static final long serialVersionUID = 1L;
+
+       public class MultiGrainNode extends PartNode{
+               private static final long serialVersionUID = 1L;
+               public MultiGrainNode(MultiGrain part) {
+                       super(part);
+                       setAllowsChildren(true);
+                       add(new PartNode(part.getGrain()));
+               }
+               @Override
+               public void propertyChange(PropertyChangeEvent e) {
+                       if ( e.getPropertyName().equals("Grain")){
+                               remove(0);
+                               add(new PartNode(((MultiGrain)getUserObject()).getGrain()));
+                               nodesChanged(this, new int[]{0});
+                       }
+                       super.propertyChange(e);
+               }
+       }
+
+       public class PartNode extends DefaultMutableTreeNode implements PropertyChangeListener {
+               private static final long serialVersionUID = 1L;
+
+               public PartNode(Object part) {
+                       super(part, false);
+                       if (part instanceof MotorPart) {
+                               ((MotorPart) part).addPropertyChangeListener(this);
+                       }
+               }
+       
+               @Override
+               public void propertyChange(PropertyChangeEvent e) {
+                       nodeChanged(this);
+               }
+       
+       }
+       
+       public class MotorNode extends DefaultMutableTreeNode implements PropertyChangeListener {
+               private static final long serialVersionUID = 1L;
+               Motor motor;
+               PartNode cn, nn, gn, fn;
+
+               public MotorNode(Motor m) {
+                       super(m);
+                       motor = m;
+                       add( cn = new PartNode(m.getChamber()));
+                       add( nn = new PartNode(m.getNozzle()));
+                       if ( m.getGrain() instanceof MultiGrain ){
+                               gn = new MultiGrainNode(((MultiGrain)m.getGrain()));
+                       } else {
+                               gn = new PartNode(m.getGrain());
+                       }
+                       add(gn);
+                       add( fn = new PartNode(m.getFuel()));
+                       if (m instanceof MotorPart) {
+                               ((MotorPart) m).addPropertyChangeListener(this);
+                       }
+               }
+               
+               @Override
+               public Motor getUserObject(){
+                       return (Motor)super.getUserObject();
+               }
+
+               @Override
+               public void propertyChange(PropertyChangeEvent e) {
+                       if ( e.getPropertyName().equals("Fuel")){
+                               fn = new PartNode(motor.getFuel());
+                               remove(3);
+                               add(fn);
+                               nodesChanged(this, new int[]{3});
+                       }
+               }
+
+       }
+
+       public WorkbenchTreeModel() {
+               super(new DefaultMutableTreeNode(), true);
+       }
+       
+       public void addMotor(Motor m){
+               DefaultMutableTreeNode root = (DefaultMutableTreeNode)getRoot();
+               root.add(new MotorNode(m));
+               nodesWereInserted(root, new int[]{root.getChildCount()-1});
+               
+       }
+
+}