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