Change toLowerCase() to toLowerCase(Locale.ENGLISH)
[debian/openrocket] / core / src / net / sf / openrocket / gui / configdialog / RocketComponentConfig.java
1 package net.sf.openrocket.gui.configdialog;
2
3
4 import java.awt.Color;
5 import java.awt.event.ActionEvent;
6 import java.awt.event.ActionListener;
7 import java.awt.event.FocusEvent;
8 import java.awt.event.FocusListener;
9 import java.util.ArrayList;
10 import java.util.Iterator;
11 import java.util.List;
12 import java.util.Locale;
13
14 import javax.swing.BorderFactory;
15 import javax.swing.JButton;
16 import javax.swing.JCheckBox;
17 import javax.swing.JColorChooser;
18 import javax.swing.JComboBox;
19 import javax.swing.JLabel;
20 import javax.swing.JPanel;
21 import javax.swing.JScrollPane;
22 import javax.swing.JSpinner;
23 import javax.swing.JTabbedPane;
24 import javax.swing.JTextArea;
25 import javax.swing.JTextField;
26
27 import net.miginfocom.swing.MigLayout;
28 import net.sf.openrocket.document.OpenRocketDocument;
29 import net.sf.openrocket.gui.SpinnerEditor;
30 import net.sf.openrocket.gui.adaptors.BooleanModel;
31 import net.sf.openrocket.gui.adaptors.DoubleModel;
32 import net.sf.openrocket.gui.adaptors.EnumModel;
33 import net.sf.openrocket.gui.adaptors.MaterialModel;
34 import net.sf.openrocket.gui.components.BasicSlider;
35 import net.sf.openrocket.gui.components.ColorIcon;
36 import net.sf.openrocket.gui.components.StyledLabel;
37 import net.sf.openrocket.gui.components.StyledLabel.Style;
38 import net.sf.openrocket.gui.components.UnitSelector;
39 import net.sf.openrocket.gui.util.ColorConversion;
40 import net.sf.openrocket.gui.util.GUIUtil;
41 import net.sf.openrocket.gui.util.SwingPreferences;
42 import net.sf.openrocket.l10n.Translator;
43 import net.sf.openrocket.material.Material;
44 import net.sf.openrocket.rocketcomponent.ComponentAssembly;
45 import net.sf.openrocket.rocketcomponent.ExternalComponent;
46 import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish;
47 import net.sf.openrocket.rocketcomponent.NoseCone;
48 import net.sf.openrocket.rocketcomponent.RocketComponent;
49 import net.sf.openrocket.startup.Application;
50 import net.sf.openrocket.unit.UnitGroup;
51 import net.sf.openrocket.util.Invalidatable;
52 import net.sf.openrocket.util.LineStyle;
53
54 public class RocketComponentConfig extends JPanel {
55         
56         private static final Translator trans = Application.getTranslator();
57         
58         protected final OpenRocketDocument document;
59         protected final RocketComponent component;
60         protected final JTabbedPane tabbedPane;
61         
62         private final List<Invalidatable> invalidatables = new ArrayList<Invalidatable>();
63         
64         
65         protected final JTextField componentNameField;
66         protected JTextArea commentTextArea;
67         private final TextFieldListener textFieldListener;
68         private JButton colorButton;
69         private JCheckBox colorDefault;
70         private JPanel buttonPanel;
71         
72         private JLabel massLabel;
73         
74         
75         public RocketComponentConfig(OpenRocketDocument document, RocketComponent component) {
76                 setLayout(new MigLayout("fill", "[grow, fill]"));
77                 this.document = document;
78                 this.component = component;
79                 
80                 //// Component name:
81                 JLabel label = new JLabel(trans.get("RocketCompCfg.lbl.Componentname"));
82                 //// The component name.
83                 label.setToolTipText(trans.get("RocketCompCfg.ttip.Thecomponentname"));
84                 this.add(label, "split, gapright 10");
85                 
86                 componentNameField = new JTextField(15);
87                 textFieldListener = new TextFieldListener();
88                 componentNameField.addActionListener(textFieldListener);
89                 componentNameField.addFocusListener(textFieldListener);
90                 //// The component name.
91                 componentNameField.setToolTipText(trans.get("RocketCompCfg.ttip.Thecomponentname"));
92                 this.add(componentNameField, "growx, growy 0, wrap");
93                 
94                 
95                 tabbedPane = new JTabbedPane();
96                 this.add(tabbedPane, "growx, growy 1, wrap");
97                 
98                 //// Override and Mass and CG override options
99                 tabbedPane.addTab(trans.get("RocketCompCfg.tab.Override"), null, overrideTab(),
100                                 trans.get("RocketCompCfg.tab.MassandCGoverride"));
101                 if (component.isMassive())
102                         //// Figure and Figure style options
103                         tabbedPane.addTab(trans.get("RocketCompCfg.tab.Figure"), null, figureTab(),
104                                         trans.get("RocketCompCfg.tab.Figstyleopt"));
105                 //// Comment and Specify a comment for the component
106                 tabbedPane.addTab(trans.get("RocketCompCfg.tab.Comment"), null, commentTab(),
107                                 trans.get("RocketCompCfg.tab.Specifyacomment"));
108                 
109                 addButtons();
110                 
111                 updateFields();
112         }
113         
114         
115         protected void addButtons(JButton... buttons) {
116                 if (buttonPanel != null) {
117                         this.remove(buttonPanel);
118                 }
119                 
120                 buttonPanel = new JPanel(new MigLayout("fill, ins 0"));
121                 
122                 //// Mass:
123                 massLabel = new StyledLabel(trans.get("RocketCompCfg.lbl.Mass") + " ", -1);
124                 buttonPanel.add(massLabel, "growx");
125                 
126                 for (JButton b : buttons) {
127                         buttonPanel.add(b, "right, gap para");
128                 }
129                 
130                 //// Close button
131                 JButton closeButton = new JButton(trans.get("dlg.but.close"));
132                 closeButton.addActionListener(new ActionListener() {
133                         @Override
134                         public void actionPerformed(ActionEvent arg0) {
135                                 ComponentConfigDialog.hideDialog();
136                         }
137                 });
138                 buttonPanel.add(closeButton, "right, gap 30lp");
139                 
140                 updateFields();
141                 
142                 this.add(buttonPanel, "spanx, growx");
143         }
144         
145         
146         /**
147          * Called when a change occurs, so that the fields can be updated if necessary.
148          * When overriding this method, the supermethod must always be called.
149          */
150         public void updateFields() {
151                 // Component name
152                 componentNameField.setText(component.getName());
153                 
154                 // Component color and "Use default color" checkbox
155                 if (colorButton != null && colorDefault != null) {
156                         colorButton.setIcon(new ColorIcon(getColor()));
157                         
158                         if ((component.getColor() == null) != colorDefault.isSelected())
159                                 colorDefault.setSelected(component.getColor() == null);
160                 }
161                 
162                 // Mass label
163                 if (component.isMassive()) {
164                         //// Component mass:
165                         String text = trans.get("RocketCompCfg.lbl.Componentmass") + " ";
166                         text += UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(
167                                         component.getComponentMass());
168                         
169                         String overridetext = null;
170                         if (component.isMassOverridden()) {
171                                 //// (overridden to 
172                                 overridetext = trans.get("RocketCompCfg.lbl.overriddento") + " " + UnitGroup.UNITS_MASS.getDefaultUnit().
173                                                 toStringUnit(component.getOverrideMass()) + ")";
174                         }
175                         
176                         for (RocketComponent c = component.getParent(); c != null; c = c.getParent()) {
177                                 if (c.isMassOverridden() && c.getOverrideSubcomponents()) {
178                                         ///// (overridden by
179                                         overridetext = trans.get("RocketCompCfg.lbl.overriddenby") + " " + c.getName() + ")";
180                                 }
181                         }
182                         
183                         if (overridetext != null)
184                                 text = text + " " + overridetext;
185                         
186                         massLabel.setText(text);
187                 } else {
188                         massLabel.setText("");
189                 }
190         }
191         
192         
193         protected JPanel materialPanel(JPanel panel, Material.Type type) {
194                 ////Component material: and Component finish:
195                 return materialPanel(panel, type, trans.get("RocketCompCfg.lbl.Componentmaterial"),
196                                 trans.get("RocketCompCfg.lbl.Componentfinish"));
197         }
198         
199         protected JPanel materialPanel(JPanel panel, Material.Type type,
200                         String materialString, String finishString) {
201                 JLabel label = new JLabel(materialString);
202                 //// The component material affects the weight of the component.
203                 label.setToolTipText(trans.get("RocketCompCfg.lbl.ttip.componentmaterialaffects"));
204                 panel.add(label, "spanx 4, wrap rel");
205                 
206                 JComboBox combo = new JComboBox(new MaterialModel(panel, component, type));
207                 //// The component material affects the weight of the component.
208                 combo.setToolTipText(trans.get("RocketCompCfg.combo.ttip.componentmaterialaffects"));
209                 panel.add(combo, "spanx 4, growx, wrap paragraph");
210                 
211                 
212                 if (component instanceof ExternalComponent) {
213                         label = new JLabel(finishString);
214                         ////<html>The component finish affects the aerodynamic drag of the component.<br>
215                         String tip = trans.get("RocketCompCfg.lbl.longA1")
216                                         //// The value indicated is the average roughness height of the surface.
217                                         + trans.get("RocketCompCfg.lbl.longA2");
218                         label.setToolTipText(tip);
219                         panel.add(label, "spanx 4, wmin 220lp, wrap rel");
220                         
221                         combo = new JComboBox(new EnumModel<ExternalComponent.Finish>(component, "Finish"));
222                         combo.setToolTipText(tip);
223                         panel.add(combo, "spanx 4, growx, split");
224                         
225                         //// Set for all
226                         JButton button = new JButton(trans.get("RocketCompCfg.but.Setforall"));
227                         //// Set this finish for all components of the rocket.
228                         button.setToolTipText(trans.get("RocketCompCfg.but.ttip.Setforall"));
229                         button.addActionListener(new ActionListener() {
230                                 @Override
231                                 public void actionPerformed(ActionEvent e) {
232                                         Finish f = ((ExternalComponent) component).getFinish();
233                                         try {
234                                                 document.startUndo("Set rocket finish");
235                                                 
236                                                 // Do changes
237                                                 Iterator<RocketComponent> iter = component.getRoot().iterator();
238                                                 while (iter.hasNext()) {
239                                                         RocketComponent c = iter.next();
240                                                         if (c instanceof ExternalComponent) {
241                                                                 ((ExternalComponent) c).setFinish(f);
242                                                         }
243                                                 }
244                                         } finally {
245                                                 document.stopUndo();
246                                         }
247                                 }
248                         });
249                         panel.add(button, "wrap paragraph");
250                 }
251                 
252                 return panel;
253         }
254         
255         
256         private JPanel overrideTab() {
257                 JPanel panel = new JPanel(new MigLayout("align 50% 20%, fillx, gap rel unrel",
258                                 "[][65lp::][30lp::][]", ""));
259                 //// Override the mass or center of gravity of the
260                 panel.add(new StyledLabel(trans.get("RocketCompCfg.lbl.Overridemassorcenter") + " " +
261                                 component.getComponentName() + ":", Style.BOLD), "spanx, wrap 20lp");
262                 
263                 JCheckBox check;
264                 BooleanModel bm;
265                 UnitSelector us;
266                 BasicSlider bs;
267                 
268                 ////  Mass
269                 bm = new BooleanModel(component, "MassOverridden");
270                 check = new JCheckBox(bm);
271                 //// Override mass:
272                 check.setText(trans.get("RocketCompCfg.checkbox.Overridemass"));
273                 panel.add(check, "growx 1, gapright 20lp");
274                 
275                 DoubleModel m = new DoubleModel(component, "OverrideMass", UnitGroup.UNITS_MASS, 0);
276                 
277                 JSpinner spin = new JSpinner(m.getSpinnerModel());
278                 spin.setEditor(new SpinnerEditor(spin));
279                 bm.addEnableComponent(spin, true);
280                 panel.add(spin, "growx 1");
281                 
282                 us = new UnitSelector(m);
283                 bm.addEnableComponent(us, true);
284                 panel.add(us, "growx 1");
285                 
286                 bs = new BasicSlider(m.getSliderModel(0, 0.03, 1.0));
287                 bm.addEnableComponent(bs);
288                 panel.add(bs, "growx 5, w 100lp, wrap");
289                 
290                 
291                 ////  CG override
292                 bm = new BooleanModel(component, "CGOverridden");
293                 check = new JCheckBox(bm);
294                 //// Override center of gravity:"
295                 check.setText(trans.get("RocketCompCfg.checkbox.Overridecenterofgrav"));
296                 panel.add(check, "growx 1, gapright 20lp");
297                 
298                 m = new DoubleModel(component, "OverrideCGX", UnitGroup.UNITS_LENGTH, 0);
299                 // Calculate suitable length for slider
300                 DoubleModel length;
301                 if (component instanceof ComponentAssembly) {
302                         double l = 0;
303                         
304                         Iterator<RocketComponent> iterator = component.iterator(false);
305                         while (iterator.hasNext()) {
306                                 RocketComponent c = iterator.next();
307                                 if (c.getRelativePosition() == RocketComponent.Position.AFTER)
308                                         l += c.getLength();
309                         }
310                         length = new DoubleModel(l);
311                 } else {
312                         length = new DoubleModel(component, "Length", UnitGroup.UNITS_LENGTH, 0);
313                 }
314                 
315                 spin = new JSpinner(m.getSpinnerModel());
316                 spin.setEditor(new SpinnerEditor(spin));
317                 bm.addEnableComponent(spin, true);
318                 panel.add(spin, "growx 1");
319                 
320                 us = new UnitSelector(m);
321                 bm.addEnableComponent(us, true);
322                 panel.add(us, "growx 1");
323                 
324                 bs = new BasicSlider(m.getSliderModel(new DoubleModel(0), length));
325                 bm.addEnableComponent(bs);
326                 panel.add(bs, "growx 5, w 100lp, wrap 35lp");
327                 
328                 
329                 // Override subcomponents checkbox
330                 bm = new BooleanModel(component, "OverrideSubcomponents");
331                 check = new JCheckBox(bm);
332                 //// Override mass and CG of all subcomponents
333                 check.setText(trans.get("RocketCompCfg.checkbox.OverridemassandCG"));
334                 panel.add(check, "gap para, spanx, wrap para");
335                 
336                 //// <html>The overridden mass does not include motors.<br>
337                 panel.add(new StyledLabel(trans.get("RocketCompCfg.lbl.longB1") +
338                                 //// The center of gravity is measured from the front end of the
339                                 trans.get("RocketCompCfg.lbl.longB2") + " " +
340                                 component.getComponentName().toLowerCase(Locale.getDefault()) + ".", -1),
341                                 "spanx, wrap, gap para, height 0::30lp");
342                 
343                 return panel;
344         }
345         
346         
347         private JPanel commentTab() {
348                 JPanel panel = new JPanel(new MigLayout("fill"));
349                 
350                 //// Comments on the
351                 panel.add(new StyledLabel(trans.get("RocketCompCfg.lbl.Commentsonthe") + " " + component.getComponentName() + ":",
352                                 Style.BOLD), "wrap");
353                 
354                 // TODO: LOW:  Changes in comment from other sources not reflected in component
355                 commentTextArea = new JTextArea(component.getComment());
356                 commentTextArea.setLineWrap(true);
357                 commentTextArea.setWrapStyleWord(true);
358                 commentTextArea.setEditable(true);
359                 GUIUtil.setTabToFocusing(commentTextArea);
360                 commentTextArea.addFocusListener(textFieldListener);
361                 
362                 panel.add(new JScrollPane(commentTextArea), "width 10px, height 10px, growx, growy");
363                 
364                 return panel;
365         }
366         
367         
368         
369         private JPanel figureTab() {
370                 JPanel panel = new JPanel(new MigLayout("align 20% 20%"));
371                 
372                 //// Figure style:
373                 panel.add(new StyledLabel(trans.get("RocketCompCfg.lbl.Figurestyle"), Style.BOLD), "wrap para");
374                 
375                 //// Component color:
376                 panel.add(new JLabel(trans.get("RocketCompCfg.lbl.Componentcolor")), "gapleft para, gapright 10lp");
377                 
378                 colorButton = new JButton(new ColorIcon(getColor()));
379                 colorButton.addActionListener(new ActionListener() {
380                         @Override
381                         public void actionPerformed(ActionEvent e) {
382                                 net.sf.openrocket.util.Color c = component.getColor();
383                                 if (c == null) {
384                                         c = Application.getPreferences().getDefaultColor(component.getClass());
385                                 }
386                                 
387                                 //// Choose color
388                                 Color awtColor = ColorConversion.toAwtColor(c);
389                                 awtColor = JColorChooser.showDialog(tabbedPane, trans.get("RocketCompCfg.lbl.Choosecolor"), awtColor);
390                                 c = ColorConversion.fromAwtColor(awtColor);
391                                 if (c != null) {
392                                         component.setColor(c);
393                                 }
394                         }
395                 });
396                 panel.add(colorButton, "gapright 10lp");
397                 
398                 //// Use default color
399                 colorDefault = new JCheckBox(trans.get("RocketCompCfg.checkbox.Usedefaultcolor"));
400                 if (component.getColor() == null)
401                         colorDefault.setSelected(true);
402                 colorDefault.addActionListener(new ActionListener() {
403                         @Override
404                         public void actionPerformed(ActionEvent e) {
405                                 if (colorDefault.isSelected())
406                                         component.setColor(null);
407                                 else
408                                         component.setColor(((SwingPreferences) Application.getPreferences()).getDefaultColor(component.getClass()));
409                         }
410                 });
411                 panel.add(colorDefault, "wrap para");
412                 
413                 //// Component line style:
414                 panel.add(new JLabel(trans.get("RocketCompCfg.lbl.Complinestyle")), "gapleft para, gapright 10lp");
415                 
416                 LineStyle[] list = new LineStyle[LineStyle.values().length + 1];
417                 System.arraycopy(LineStyle.values(), 0, list, 1, LineStyle.values().length);
418                 
419                 JComboBox combo = new JComboBox(new EnumModel<LineStyle>(component, "LineStyle",
420                                 //// Default style
421                                 list, trans.get("LineStyle.Defaultstyle")));
422                 panel.add(combo, "spanx 2, growx, wrap 50lp");
423                 
424                 //// Save as default style
425                 JButton button = new JButton(trans.get("RocketCompCfg.but.Saveasdefstyle"));
426                 button.addActionListener(new ActionListener() {
427                         @Override
428                         public void actionPerformed(ActionEvent e) {
429                                 if (component.getColor() != null) {
430                                         ((SwingPreferences) Application.getPreferences()).setDefaultColor(component.getClass(), component.getColor());
431                                         component.setColor(null);
432                                 }
433                                 if (component.getLineStyle() != null) {
434                                         Application.getPreferences().setDefaultLineStyle(component.getClass(), component.getLineStyle());
435                                         component.setLineStyle(null);
436                                 }
437                         }
438                 });
439                 panel.add(button, "gapleft para, spanx 3, growx, wrap");
440                 
441                 return panel;
442         }
443         
444         
445         private Color getColor() {
446                 net.sf.openrocket.util.Color c = component.getColor();
447                 if (c == null) {
448                         c = Application.getPreferences().getDefaultColor(component.getClass());
449                 }
450                 return ColorConversion.toAwtColor(c);
451         }
452         
453         
454         
455         protected JPanel shoulderTab() {
456                 JPanel panel = new JPanel(new MigLayout("fill"));
457                 JPanel sub;
458                 DoubleModel m, m2;
459                 DoubleModel m0 = new DoubleModel(0);
460                 BooleanModel bm;
461                 JCheckBox check;
462                 JSpinner spin;
463                 
464                 
465                 ////  Fore shoulder, not for NoseCone
466                 
467                 if (!(component instanceof NoseCone)) {
468                         sub = new JPanel(new MigLayout("gap rel unrel", "[][65lp::][30lp::]", ""));
469                         
470                         //// Fore shoulder
471                         sub.setBorder(BorderFactory.createTitledBorder(trans.get("RocketCompCfg.border.Foreshoulder")));
472                         
473                         
474                         ////  Radius
475                         //// Diameter:
476                         sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Diameter")));
477                         
478                         m = new DoubleModel(component, "ForeShoulderRadius", 2, UnitGroup.UNITS_LENGTH, 0);
479                         m2 = new DoubleModel(component, "ForeRadius", 2, UnitGroup.UNITS_LENGTH);
480                         
481                         spin = new JSpinner(m.getSpinnerModel());
482                         spin.setEditor(new SpinnerEditor(spin));
483                         sub.add(spin, "growx");
484                         
485                         sub.add(new UnitSelector(m), "growx");
486                         sub.add(new BasicSlider(m.getSliderModel(m0, m2)), "w 100lp, wrap");
487                         
488                         
489                         ////  Length:
490                         sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Length")));
491                         
492                         m = new DoubleModel(component, "ForeShoulderLength", UnitGroup.UNITS_LENGTH, 0);
493                         
494                         spin = new JSpinner(m.getSpinnerModel());
495                         spin.setEditor(new SpinnerEditor(spin));
496                         sub.add(spin, "growx");
497                         
498                         sub.add(new UnitSelector(m), "growx");
499                         sub.add(new BasicSlider(m.getSliderModel(0, 0.02, 0.2)), "w 100lp, wrap");
500                         
501                         
502                         ////  Thickness:
503                         sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Thickness")));
504                         
505                         m = new DoubleModel(component, "ForeShoulderThickness", UnitGroup.UNITS_LENGTH, 0);
506                         m2 = new DoubleModel(component, "ForeShoulderRadius", UnitGroup.UNITS_LENGTH);
507                         
508                         spin = new JSpinner(m.getSpinnerModel());
509                         spin.setEditor(new SpinnerEditor(spin));
510                         sub.add(spin, "growx");
511                         
512                         sub.add(new UnitSelector(m), "growx");
513                         sub.add(new BasicSlider(m.getSliderModel(m0, m2)), "w 100lp, wrap");
514                         
515                         
516                         ////  Capped
517                         bm = new BooleanModel(component, "ForeShoulderCapped");
518                         check = new JCheckBox(bm);
519                         //// End capped
520                         check.setText(trans.get("RocketCompCfg.checkbox.Endcapped"));
521                         //// Whether the end of the shoulder is capped.
522                         check.setToolTipText(trans.get("RocketCompCfg.ttip.Endcapped"));
523                         sub.add(check, "spanx");
524                         
525                         
526                         panel.add(sub);
527                 }
528                 
529                 
530                 ////  Aft shoulder
531                 sub = new JPanel(new MigLayout("gap rel unrel", "[][65lp::][30lp::]", ""));
532                 
533                 if (component instanceof NoseCone)
534                         //// Nose cone shoulder
535                         sub.setBorder(BorderFactory.createTitledBorder(trans.get("RocketCompCfg.title.Noseconeshoulder")));
536                 else
537                         //// Aft shoulder
538                         sub.setBorder(BorderFactory.createTitledBorder(trans.get("RocketCompCfg.title.Aftshoulder")));
539                 
540                 
541                 ////  Radius
542                 //// Diameter:
543                 sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Diameter")));
544                 
545                 m = new DoubleModel(component, "AftShoulderRadius", 2, UnitGroup.UNITS_LENGTH, 0);
546                 m2 = new DoubleModel(component, "AftRadius", 2, UnitGroup.UNITS_LENGTH);
547                 
548                 spin = new JSpinner(m.getSpinnerModel());
549                 spin.setEditor(new SpinnerEditor(spin));
550                 sub.add(spin, "growx");
551                 
552                 sub.add(new UnitSelector(m), "growx");
553                 sub.add(new BasicSlider(m.getSliderModel(m0, m2)), "w 100lp, wrap");
554                 
555                 
556                 ////  Length:
557                 sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Length")));
558                 
559                 m = new DoubleModel(component, "AftShoulderLength", UnitGroup.UNITS_LENGTH, 0);
560                 
561                 spin = new JSpinner(m.getSpinnerModel());
562                 spin.setEditor(new SpinnerEditor(spin));
563                 sub.add(spin, "growx");
564                 
565                 sub.add(new UnitSelector(m), "growx");
566                 sub.add(new BasicSlider(m.getSliderModel(0, 0.02, 0.2)), "w 100lp, wrap");
567                 
568                 
569                 ////  Thickness:
570                 sub.add(new JLabel(trans.get("RocketCompCfg.lbl.Thickness")));
571                 
572                 m = new DoubleModel(component, "AftShoulderThickness", UnitGroup.UNITS_LENGTH, 0);
573                 m2 = new DoubleModel(component, "AftShoulderRadius", UnitGroup.UNITS_LENGTH);
574                 
575                 spin = new JSpinner(m.getSpinnerModel());
576                 spin.setEditor(new SpinnerEditor(spin));
577                 sub.add(spin, "growx");
578                 
579                 sub.add(new UnitSelector(m), "growx");
580                 sub.add(new BasicSlider(m.getSliderModel(m0, m2)), "w 100lp, wrap");
581                 
582                 
583                 ////  Capped
584                 bm = new BooleanModel(component, "AftShoulderCapped");
585                 check = new JCheckBox(bm);
586                 //// End capped
587                 check.setText(trans.get("RocketCompCfg.checkbox.Endcapped"));
588                 //// Whether the end of the shoulder is capped.
589                 check.setToolTipText(trans.get("RocketCompCfg.ttip.Endcapped"));
590                 sub.add(check, "spanx");
591                 
592                 
593                 panel.add(sub);
594                 
595                 
596                 return panel;
597         }
598         
599         
600         
601         
602         /*
603          * Private inner class to handle events in componentNameField.
604          */
605         private class TextFieldListener implements ActionListener, FocusListener {
606                 @Override
607                 public void actionPerformed(ActionEvent e) {
608                         setName();
609                 }
610                 
611                 @Override
612                 public void focusGained(FocusEvent e) {
613                 }
614                 
615                 @Override
616                 public void focusLost(FocusEvent e) {
617                         setName();
618                 }
619                 
620                 private void setName() {
621                         if (!component.getName().equals(componentNameField.getText())) {
622                                 component.setName(componentNameField.getText());
623                         }
624                         if (!component.getComment().equals(commentTextArea.getText())) {
625                                 component.setComment(commentTextArea.getText());
626                         }
627                 }
628         }
629         
630         
631         protected void register(Invalidatable model) {
632                 this.invalidatables.add(model);
633         }
634         
635         public void invalidateModels() {
636                 for (Invalidatable i : invalidatables) {
637                         i.invalidate();
638                 }
639         }
640         
641 }