Added validating and some validation
[sw/motorsim] / src / com / billkuker / rocketry / motorsim / visual / workbench / MotorEditor.java
1 package com.billkuker.rocketry.motorsim.visual.workbench;\r
2 \r
3 import java.awt.BorderLayout;\r
4 import java.awt.event.ActionEvent;\r
5 import java.awt.event.ActionListener;\r
6 import java.awt.event.FocusEvent;\r
7 import java.awt.event.FocusListener;\r
8 import java.beans.PropertyChangeEvent;\r
9 import java.beans.PropertyChangeListener;\r
10 import java.beans.PropertyVetoException;\r
11 import java.io.IOException;\r
12 \r
13 import javax.measure.quantity.Pressure;\r
14 import javax.measure.quantity.Velocity;\r
15 import javax.measure.unit.SI;\r
16 import javax.swing.BoxLayout;\r
17 import javax.swing.JButton;\r
18 import javax.swing.JFrame;\r
19 import javax.swing.JPanel;\r
20 import javax.swing.JProgressBar;\r
21 import javax.swing.JSplitPane;\r
22 import javax.swing.JTabbedPane;\r
23 import javax.swing.JTextArea;\r
24 import javax.swing.JTextField;\r
25 import javax.swing.SwingUtilities;\r
26 import javax.swing.UIManager;\r
27 import javax.swing.WindowConstants;\r
28 \r
29 import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;\r
30 import org.fife.ui.rsyntaxtextarea.SyntaxConstants;\r
31 import org.jscience.physics.amount.Amount;\r
32 \r
33 import com.billkuker.rocketry.motorsim.Burn;\r
34 import com.billkuker.rocketry.motorsim.Chamber;\r
35 import com.billkuker.rocketry.motorsim.ChangeListening;\r
36 import com.billkuker.rocketry.motorsim.ConvergentDivergentNozzle;\r
37 import com.billkuker.rocketry.motorsim.CylindricalChamber;\r
38 import com.billkuker.rocketry.motorsim.Fuel;\r
39 import com.billkuker.rocketry.motorsim.Grain;\r
40 import com.billkuker.rocketry.motorsim.Motor;\r
41 import com.billkuker.rocketry.motorsim.Nozzle;\r
42 import com.billkuker.rocketry.motorsim.fuel.EditableFuel;\r
43 import com.billkuker.rocketry.motorsim.fuel.KNDX;\r
44 import com.billkuker.rocketry.motorsim.fuel.KNER;\r
45 import com.billkuker.rocketry.motorsim.fuel.KNSB;\r
46 import com.billkuker.rocketry.motorsim.fuel.KNSU;\r
47 import com.billkuker.rocketry.motorsim.grain.CoredCylindricalGrain;\r
48 import com.billkuker.rocketry.motorsim.grain.Finocyl;\r
49 import com.billkuker.rocketry.motorsim.grain.Moonburner;\r
50 import com.billkuker.rocketry.motorsim.grain.MultiGrain;\r
51 import com.billkuker.rocketry.motorsim.grain.RodAndTubeGrain;\r
52 import com.billkuker.rocketry.motorsim.io.MotorIO;\r
53 import com.billkuker.rocketry.motorsim.visual.BurnPanel;\r
54 import com.billkuker.rocketry.motorsim.visual.Chart;\r
55 import com.billkuker.rocketry.motorsim.visual.Editor;\r
56 import com.billkuker.rocketry.motorsim.visual.GrainPanel;\r
57 import com.billkuker.rocketry.motorsim.visual.NozzlePanel;\r
58 \r
59 public class MotorEditor extends JTabbedPane implements PropertyChangeListener {\r
60         private static final long serialVersionUID = 1L;\r
61         RSyntaxTextArea text = new RSyntaxTextArea();\r
62         Motor motor;\r
63         GrainEditor grainEditor;\r
64         BurnTab bt;\r
65 \r
66         private static final int XML_TAB = 0;\r
67         private static final int CASING_TAB = 1;\r
68         private static final int GRAIN_TAB = 2;\r
69         private static final int FUEL_TAB = 3;\r
70         private static final int BURN_TAB = 4;\r
71 \r
72         @SuppressWarnings("unchecked")\r
73         private Class[] grainTypes = { CoredCylindricalGrain.class, Finocyl.class,\r
74                         Moonburner.class, RodAndTubeGrain.class };\r
75 \r
76         @SuppressWarnings("unchecked")\r
77         private Class[] fuelTypes = { KNSB.class, KNSU.class, KNER.class,\r
78                         KNDX.class, EditableFuel.class };\r
79 \r
80         private abstract class Chooser<T> extends JPanel {\r
81                 private static final long serialVersionUID = 1L;\r
82                 private Class<? extends T>[] types;\r
83 \r
84                 public Chooser(Class<? extends T>... ts) {\r
85                         types = ts;\r
86                         setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));\r
87                         for (final Class<? extends T> c : types) {\r
88                                 JButton b = new JButton(c.getSimpleName());\r
89                                 add(b);\r
90                                 b.addActionListener(new ActionListener() {\r
91                                         public void actionPerformed(ActionEvent e) {\r
92                                                 try {\r
93                                                         choiceMade(c.newInstance());\r
94                                                 } catch (InstantiationException e1) {\r
95                                                         e1.printStackTrace();\r
96                                                 } catch (IllegalAccessException e1) {\r
97                                                         e1.printStackTrace();\r
98                                                 }\r
99                                         }\r
100                                 });\r
101                         }\r
102                 }\r
103 \r
104                 protected abstract void choiceMade(T o);\r
105         }\r
106 \r
107         private class BurnTab extends JPanel {\r
108                 private static final long serialVersionUID = 1L;\r
109                 public BurnTab() {\r
110                         setLayout(new BorderLayout());\r
111                         setName("Burn");\r
112                         reBurn();\r
113                 }\r
114 \r
115                 public void reBurn() {\r
116                         removeAll();\r
117                         new Thread() {\r
118                                 public void run() {\r
119                                         final JProgressBar bar = new JProgressBar(0,100);\r
120                                         add(bar);\r
121                                         try{\r
122                                         final Burn b = new Burn(motor, new Burn.BurnProgressListener(){\r
123                                                 @Override\r
124                                                 public void setProgress(float f){\r
125                                                         bar.setValue((int)(f*100));\r
126                                                 }\r
127                                         });\r
128                                 \r
129                                         final BurnPanel bp = new BurnPanel(b);\r
130                                         SwingUtilities.invokeLater(new Thread() {\r
131                                                 public void run() {\r
132                                                         remove(bar);\r
133                                                         add(bp, BorderLayout.CENTER);\r
134                                                         \r
135                                                         revalidate();\r
136                                                 }\r
137                                         });\r
138                                         } catch ( Exception e ){\r
139                                                 remove(bar);\r
140                                                 JTextArea t = new JTextArea(e.getMessage());\r
141                                                 t.setEditable(false);\r
142                                                 add(t);\r
143                                         }\r
144                                 }\r
145                         }.start();\r
146                 }\r
147         }\r
148 \r
149         private class GrainEditor extends JSplitPane {\r
150                 private static final long serialVersionUID = 1L;\r
151 \r
152                 public GrainEditor(final Grain g) {\r
153                         super(JSplitPane.HORIZONTAL_SPLIT);\r
154                         setName("Grain");\r
155                         setRightComponent(new GrainPanel(g));\r
156                         if (g instanceof Grain.Composite) {\r
157                                 final JPanel p = new JPanel();\r
158                                 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));\r
159                                 p.add(new Editor(g));\r
160                                 for (Grain gg : ((Grain.Composite) g).getGrains()) {\r
161                                         final int grainEditorIndex = p.getComponentCount() + 1;\r
162                                         p.add(new Chooser<Grain>(grainTypes) {\r
163                                                 private static final long serialVersionUID = 1L;\r
164 \r
165                                                 @Override\r
166                                                 protected void choiceMade(Grain ng) {\r
167                                                         if (g instanceof MultiGrain) {\r
168                                                                 ((MultiGrain) g).setGrain(ng);\r
169                                                                 p.remove(grainEditorIndex);\r
170                                                                 p.add(new Editor(ng), grainEditorIndex);\r
171                                                                 p.remove(0);\r
172                                                                 p.add(new Editor(g), 0);\r
173                                                                 // System.out.println("Chose new grain");\r
174                                                         }\r
175                                                 }\r
176                                         });\r
177                                         p.add(new Editor(gg));\r
178                                         if (gg instanceof ChangeListening.Subject) {\r
179                                                 ((ChangeListening.Subject) gg)\r
180                                                                 .addPropertyChangeListener(MotorEditor.this);\r
181                                         }\r
182                                 }\r
183                                 setLeftComponent(p);\r
184                         } else {\r
185                                 setLeftComponent(new Editor(g));\r
186                         }\r
187                         // setDividerLocation(.25);\r
188                         // setResizeWeight(.25);\r
189                         if (g instanceof ChangeListening.Subject) {\r
190                                 ((ChangeListening.Subject) g).addPropertyChangeListener(MotorEditor.this);\r
191                         }\r
192                 }\r
193         }\r
194 \r
195         private class FuelEditor extends JSplitPane {\r
196                 private static final long serialVersionUID = 1L;\r
197 \r
198                 public FuelEditor(Fuel f) {\r
199                         super(JSplitPane.HORIZONTAL_SPLIT);\r
200                         setName("Fuel");\r
201                         Chart<Pressure, Velocity> burnRate;\r
202                         try {\r
203                                 burnRate = new Chart<Pressure, Velocity>(SI.MEGA(SI.PASCAL),\r
204                                                 SI.METERS_PER_SECOND, f, "burnRate");\r
205                         } catch (NoSuchMethodException e) {\r
206                                 throw new Error(e);\r
207                         }\r
208                         burnRate.setDomain(burnRate.new IntervalDomain(Amount.valueOf(0, SI\r
209                                         .MEGA(SI.PASCAL)), Amount.valueOf(11, SI.MEGA(SI.PASCAL)),\r
210                                         20));\r
211 \r
212                         final JPanel p = new JPanel();\r
213                         p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));\r
214 \r
215                         p.add(new Chooser<Fuel>(fuelTypes) {\r
216                                 private static final long serialVersionUID = 1L;\r
217                                 @Override\r
218                                 protected void choiceMade(Fuel o) {\r
219                                         motor.setFuel(o);\r
220                                         removeTabAt(FUEL_TAB);\r
221                                         MotorEditor.this.add(new FuelEditor(motor.getFuel()),\r
222                                                         FUEL_TAB);\r
223                                         setSelectedIndex(FUEL_TAB);\r
224                                 }\r
225                         });\r
226                         p.add(new Editor(f));\r
227                         try {\r
228                                 p.add(new Editor(f.getCombustionProduct()));\r
229                         } catch (Exception e) {\r
230 \r
231                         }\r
232 \r
233                         setLeftComponent(p);\r
234                         setRightComponent(burnRate);\r
235                         // setDividerLocation(.25);\r
236                         // setResizeWeight(.25);\r
237                         if (f instanceof ChangeListening.Subject) {\r
238                                 ((ChangeListening.Subject) f).addPropertyChangeListener(MotorEditor.this);\r
239                         }\r
240                 }\r
241         }\r
242 \r
243         private class CaseEditor extends JSplitPane {\r
244                 private static final long serialVersionUID = 1L;\r
245 \r
246                 public CaseEditor(Nozzle n, Chamber c) {\r
247                         super(JSplitPane.HORIZONTAL_SPLIT);\r
248                         setName("Casing");\r
249                         JPanel parts = new JPanel();\r
250                         parts.setLayout(new BoxLayout(parts, BoxLayout.Y_AXIS));\r
251                         setLeftComponent(parts);\r
252                         setRightComponent(new NozzlePanel(n));\r
253 \r
254                         parts.add(new JTextField(motor.getName()){\r
255                                 private static final long serialVersionUID = 1L;\r
256                                 {\r
257                                         final JTextField t = this;\r
258                                         addFocusListener(new FocusListener() {\r
259                                                 \r
260                                                 @Override\r
261                                                 public void focusLost(FocusEvent e) {\r
262                                                         String n = t.getText();\r
263                                                         if ( !"".equals(n) && !n.equals(motor.getName()) ){\r
264                                                                 System.out.println("Name Changed");\r
265                                                                 motor.setName(n);\r
266                                                         } else {\r
267                                                                 t.setText(motor.getName());\r
268                                                         }\r
269                                                 }\r
270                                                 \r
271                                                 @Override\r
272                                                 public void focusGained(FocusEvent e) {\r
273                                                 \r
274                                                 }\r
275                                         });\r
276                                         \r
277                                 }\r
278                         });\r
279                         \r
280                         parts.add(new Editor(c));\r
281                         parts.add(new Editor(n));\r
282 \r
283                         if (n instanceof ChangeListening.Subject) {\r
284                                 ((ChangeListening.Subject) n).addPropertyChangeListener(MotorEditor.this);\r
285                         }\r
286                         if (c instanceof ChangeListening.Subject) {\r
287                                 ((ChangeListening.Subject) c).addPropertyChangeListener(MotorEditor.this);\r
288                         }\r
289                 }\r
290         }\r
291 \r
292         {\r
293                 text.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_XML);\r
294 \r
295         }\r
296 \r
297         public MotorEditor(Motor m) {\r
298                 super(JTabbedPane.BOTTOM);\r
299                 text.setName("XML");\r
300                 text.setEditable(false);\r
301                 add(text, XML_TAB);\r
302                 setMotor(m, true);\r
303         }\r
304         \r
305         public Motor getMotor(){\r
306                 return motor;\r
307         }\r
308 \r
309         private void reText() {\r
310                 try {\r
311                         text.setText(MotorIO.writeMotor(motor));\r
312                 } catch (IOException e) {\r
313                         throw new Error(e);\r
314                 }\r
315         }\r
316 \r
317         private void setMotor(Motor m, boolean retext) {\r
318                 if ( motor != null )\r
319                         motor.removePropertyChangeListener(this);\r
320                 motor = m;\r
321                 motor.addPropertyChangeListener(this);\r
322                 if (retext)\r
323                         reText();\r
324                 if (grainEditor != null)\r
325                         remove(grainEditor);\r
326                 while (getTabCount() > 1)\r
327                         removeTabAt(1);\r
328                 add(new CaseEditor(motor.getNozzle(), motor.getChamber()), CASING_TAB);\r
329                 add(new GrainEditor(motor.getGrain()), GRAIN_TAB);\r
330                 add(new FuelEditor(motor.getFuel()), FUEL_TAB);\r
331                 add(bt = new BurnTab(), BURN_TAB);\r
332         }\r
333 \r
334         @Deprecated\r
335         public static Motor defaultMotor() {\r
336                 Motor m = new Motor();\r
337                 m.setName("Example Motor");\r
338                 m.setFuel(new KNSU());\r
339 \r
340                 CylindricalChamber c = new CylindricalChamber();\r
341                 c.setLength(Amount.valueOf(200, SI.MILLIMETER));\r
342                 c.setID(Amount.valueOf(25, SI.MILLIMETER));\r
343                 m.setChamber(c);\r
344 \r
345                 CoredCylindricalGrain g = new CoredCylindricalGrain();\r
346                 try {\r
347                         g.setLength(Amount.valueOf(70, SI.MILLIMETER));\r
348                         g.setOD(Amount.valueOf(23.5, SI.MILLIMETER));\r
349                         g.setID(Amount.valueOf(7.9375, SI.MILLIMETER));\r
350                 } catch (PropertyVetoException v) {\r
351                         throw new Error(v);\r
352                 }\r
353 \r
354                 m.setGrain(new MultiGrain(g, 2));\r
355 \r
356                 ConvergentDivergentNozzle n = new ConvergentDivergentNozzle();\r
357                 n.setThroatDiameter(Amount.valueOf(7.962, SI.MILLIMETER));\r
358                 n.setExitDiameter(Amount.valueOf(13.79, SI.MILLIMETER));\r
359                 n.setEfficiency(.85);\r
360                 m.setNozzle(n);\r
361 \r
362                 return m;\r
363         }\r
364 \r
365         public void focusOnObject(Object o) {\r
366                 if (o instanceof Grain)\r
367                         setSelectedIndex(GRAIN_TAB);\r
368                 if (o instanceof Chamber || o instanceof Nozzle)\r
369                         setSelectedIndex(CASING_TAB);\r
370                 if (o instanceof Fuel || o instanceof Fuel.CombustionProduct)\r
371                         setSelectedIndex(FUEL_TAB);\r
372         }\r
373 \r
374         @Deprecated\r
375         public void showAsWindow() {\r
376                 JFrame f = new JFrame();\r
377                 f.setSize(1024, 768);\r
378                 f.setContentPane(this);\r
379                 f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);\r
380                 f.setVisible(true);\r
381         }\r
382 \r
383         public static void main(String args[]) throws Exception {\r
384                 try {\r
385                         UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());\r
386                 } catch (Exception e1) {\r
387                         e1.printStackTrace();\r
388                 }\r
389                 new MotorEditor(defaultMotor()).showAsWindow();\r
390         }\r
391 \r
392         public void propertyChange(PropertyChangeEvent evt) {\r
393                 reText();\r
394                 //Dont re-burn for a name change!\r
395                 if ( !evt.getPropertyName().equals("Name") )\r
396                         bt.reBurn();\r
397         }\r
398 \r
399 }\r