create changelog entry
[debian/openrocket] / core / src / net / sf / openrocket / gui / configdialog / ComponentConfigDialog.java
1 package net.sf.openrocket.gui.configdialog;
2
3
4 import java.awt.Window;
5 import java.lang.reflect.Constructor;
6 import java.lang.reflect.InvocationTargetException;
7
8 import javax.swing.JDialog;
9
10 import net.sf.openrocket.document.OpenRocketDocument;
11 import net.sf.openrocket.gui.util.GUIUtil;
12 import net.sf.openrocket.l10n.Translator;
13 import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
14 import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
15 import net.sf.openrocket.rocketcomponent.RocketComponent;
16 import net.sf.openrocket.startup.Application;
17 import net.sf.openrocket.util.BugException;
18 import net.sf.openrocket.util.Reflection;
19
20 /**
21  * A dialog that contains the configuration elements of one component.
22  * The contents of the dialog are instantiated from CONFIGDIALOGPACKAGE according
23  * to the current component.
24  * 
25  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
26  */
27
28 public class ComponentConfigDialog extends JDialog implements ComponentChangeListener {
29         private static final long serialVersionUID = 1L;
30         private static final String CONFIGDIALOGPACKAGE = "net.sf.openrocket.gui.configdialog";
31         private static final String CONFIGDIALOGPOSTFIX = "Config";
32         
33
34         private static ComponentConfigDialog dialog = null;
35         
36
37         private OpenRocketDocument document = null;
38         private RocketComponent component = null;
39         private RocketComponentConfig configurator = null;
40         
41         private final Window parent;
42         private static final Translator trans = Application.getTranslator();
43         
44         private ComponentConfigDialog(Window parent, OpenRocketDocument document,
45                         RocketComponent component) {
46                 super(parent);
47                 this.parent = parent;
48                 
49                 setComponent(document, component);
50                 
51                 GUIUtil.setDisposableDialogOptions(this, null);
52                 GUIUtil.rememberWindowPosition(this);
53         }
54         
55         
56         /**
57          * Set the component being configured.  The listening connections of the old configurator
58          * will be removed and the new ones created.
59          * 
60          * @param component  Component to configure.
61          */
62         private void setComponent(OpenRocketDocument document, RocketComponent component) {
63                 if (this.document != null) {
64                         this.document.getRocket().removeComponentChangeListener(this);
65                 }
66                 
67                 if (configurator != null) {
68                         // Remove listeners by setting all applicable models to null
69                         GUIUtil.setNullModels(configurator); // null-safe
70                 }
71                 
72                 this.document = document;
73                 this.component = component;
74                 this.document.getRocket().addComponentChangeListener(this);
75                 
76                 configurator = getDialogContents();
77                 this.setContentPane(configurator);
78                 configurator.updateFields();
79                 
80                 //// configuration
81                 setTitle(trans.get("ComponentCfgDlg.configuration1") + " " + component.getComponentName() + " " + trans.get("ComponentCfgDlg.configuration"));
82                 
83                 this.pack();
84         }
85         
86         /**
87          * Return the configurator panel of the current component.
88          */
89         private RocketComponentConfig getDialogContents() {
90                 Constructor<? extends RocketComponentConfig> c =
91                                 findDialogContentsConstructor(component);
92                 if (c != null) {
93                         try {
94                                 return c.newInstance(document, component);
95                         } catch (InstantiationException e) {
96                                 throw new BugException("BUG in constructor reflection", e);
97                         } catch (IllegalAccessException e) {
98                                 throw new BugException("BUG in constructor reflection", e);
99                         } catch (InvocationTargetException e) {
100                                 throw Reflection.handleWrappedException(e);
101                         }
102                 }
103                 
104                 // Should never be reached, since RocketComponentConfig should catch all
105                 // components without their own configurator.
106                 throw new BugException("Unable to find any configurator for " + component);
107         }
108         
109         
110         private void closeDialog() {
111                 this.setVisible(false);
112                 this.dispose();
113                 this.configurator.invalidateModels();
114         }
115         
116         
117         @Override
118         public void componentChanged(ComponentChangeEvent e) {
119                 if (e.isTreeChange() || e.isUndoChange()) {
120                         
121                         // Hide dialog in case of tree or undo change
122                         dialog.closeDialog();
123                         
124                 } else {
125                         /*
126                          * TODO: HIGH:  The line below has caused a NullPointerException (without null check)
127                          * How is this possible?  The null check was added to avoid this, but the
128                          * root cause should be analyzed.
129                          * [Openrocket-bugs] 2009-12-12 19:23:22 Automatic bug report for OpenRocket 0.9.5
130                          */
131                         if (configurator != null)
132                                 configurator.updateFields();
133                 }
134         }
135         
136         
137         /**
138          * Finds the Constructor of the given component's config dialog panel in 
139          * CONFIGDIALOGPACKAGE.
140          */
141         @SuppressWarnings("unchecked")
142         private static Constructor<? extends RocketComponentConfig> findDialogContentsConstructor(RocketComponent component) {
143                 Class<?> currentclass;
144                 String currentclassname;
145                 String configclassname;
146                 
147                 Class<?> configclass;
148                 Constructor<? extends RocketComponentConfig> c;
149                 
150                 currentclass = component.getClass();
151                 while ((currentclass != null) && (currentclass != Object.class)) {
152                         currentclassname = currentclass.getCanonicalName();
153                         int index = currentclassname.lastIndexOf('.');
154                         if (index >= 0)
155                                 currentclassname = currentclassname.substring(index + 1);
156                         configclassname = CONFIGDIALOGPACKAGE + "." + currentclassname +
157                                         CONFIGDIALOGPOSTFIX;
158                         
159                         try {
160                                 configclass = Class.forName(configclassname);
161                                 c = (Constructor<? extends RocketComponentConfig>)
162                                                 configclass.getConstructor(OpenRocketDocument.class, RocketComponent.class);
163                                 return c;
164                         } catch (Exception ignore) {
165                         }
166                         
167                         currentclass = currentclass.getSuperclass();
168                 }
169                 return null;
170         }
171         
172         
173
174
175         //////////  Static dialog  /////////
176         
177         /**
178          * A singleton configuration dialog.  Will create and show a new dialog if one has not 
179          * previously been used, or update the dialog and show it if a previous one exists.
180          * 
181          * @param document              the document to configure.
182          * @param component             the component to configure.
183          */
184         public static void showDialog(Window parent, OpenRocketDocument document,
185                         RocketComponent component) {
186                 if (dialog != null)
187                         dialog.dispose();
188                 
189                 dialog = new ComponentConfigDialog(parent, document, component);
190                 dialog.setVisible(true);
191                 
192                 ////Modify
193                 document.addUndoPosition(trans.get("ComponentCfgDlg.Modify") + " " + component.getComponentName());
194         }
195         
196         
197         /* package */
198         static void showDialog(RocketComponent component) {
199                 showDialog(dialog.parent, dialog.document, component);
200         }
201         
202         /**
203          * Hides the configuration dialog.  May be used even if not currently visible.
204          */
205         public static void hideDialog() {
206                 if (dialog != null) {
207                         dialog.closeDialog();
208                 }
209         }
210         
211         
212         /**
213          * Returns whether the singleton configuration dialog is currently visible or not.
214          */
215         public static boolean isDialogVisible() {
216                 return (dialog != null) && (dialog.isVisible());
217         }
218         
219 }