menu icons, window sizing, compass direction selector
[debian/openrocket] / 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.l10n.Translator;
12 import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
13 import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
14 import net.sf.openrocket.rocketcomponent.RocketComponent;
15 import net.sf.openrocket.startup.Application;
16 import net.sf.openrocket.util.BugException;
17 import net.sf.openrocket.util.GUIUtil;
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                 //              Dimension pref = getPreferredSize();
84                 //              Dimension real = getSize();
85                 //              if (pref.width > real.width || pref.height > real.height)
86                 this.pack();
87         }
88         
89         /**
90          * Return the configurator panel of the current component.
91          */
92         private RocketComponentConfig getDialogContents() {
93                 Constructor<? extends RocketComponentConfig> c =
94                                 findDialogContentsConstructor(component);
95                 if (c != null) {
96                         try {
97                                 return c.newInstance(component);
98                         } catch (InstantiationException e) {
99                                 throw new BugException("BUG in constructor reflection", e);
100                         } catch (IllegalAccessException e) {
101                                 throw new BugException("BUG in constructor reflection", e);
102                         } catch (InvocationTargetException e) {
103                                 throw Reflection.handleWrappedException(e);
104                         }
105                 }
106                 
107                 // Should never be reached, since RocketComponentConfig should catch all
108                 // components without their own configurator.
109                 throw new BugException("Unable to find any configurator for " + component);
110         }
111         
112         
113         private void closeDialog() {
114                 this.setVisible(false);
115                 this.dispose();
116                 this.configurator.invalidateModels();
117         }
118         
119         
120         @Override
121         public void componentChanged(ComponentChangeEvent e) {
122                 if (e.isTreeChange() || e.isUndoChange()) {
123                         
124                         // Hide dialog in case of tree or undo change
125                         dialog.closeDialog();
126                         
127                 } else {
128                         /*
129                          * TODO: HIGH:  The line below has caused a NullPointerException (without null check)
130                          * How is this possible?  The null check was added to avoid this, but the
131                          * root cause should be analyzed.
132                          * [Openrocket-bugs] 2009-12-12 19:23:22 Automatic bug report for OpenRocket 0.9.5
133                          */
134                         if (configurator != null)
135                                 configurator.updateFields();
136                 }
137         }
138         
139         
140         /**
141          * Finds the Constructor of the given component's config dialog panel in 
142          * CONFIGDIALOGPACKAGE.
143          */
144         @SuppressWarnings("unchecked")
145         private static Constructor<? extends RocketComponentConfig> findDialogContentsConstructor(RocketComponent component) {
146                 Class<?> currentclass;
147                 String currentclassname;
148                 String configclassname;
149                 
150                 Class<?> configclass;
151                 Constructor<? extends RocketComponentConfig> c;
152                 
153                 currentclass = component.getClass();
154                 while ((currentclass != null) && (currentclass != Object.class)) {
155                         currentclassname = currentclass.getCanonicalName();
156                         int index = currentclassname.lastIndexOf('.');
157                         if (index >= 0)
158                                 currentclassname = currentclassname.substring(index + 1);
159                         configclassname = CONFIGDIALOGPACKAGE + "." + currentclassname +
160                                         CONFIGDIALOGPOSTFIX;
161                         
162                         try {
163                                 configclass = Class.forName(configclassname);
164                                 c = (Constructor<? extends RocketComponentConfig>)
165                                                 configclass.getConstructor(RocketComponent.class);
166                                 return c;
167                         } catch (Exception ignore) {
168                         }
169                         
170                         currentclass = currentclass.getSuperclass();
171                 }
172                 return null;
173         }
174         
175         
176
177
178         //////////  Static dialog  /////////
179         
180         /**
181          * A singleton configuration dialog.  Will create and show a new dialog if one has not 
182          * previously been used, or update the dialog and show it if a previous one exists.
183          * 
184          * @param document              the document to configure.
185          * @param component             the component to configure.
186          */
187         public static void showDialog(Window parent, OpenRocketDocument document,
188                         RocketComponent component) {
189                 if (dialog != null)
190                         dialog.dispose();
191                 
192                 dialog = new ComponentConfigDialog(parent, document, component);
193                 dialog.setVisible(true);
194                 
195                 ////Modify
196                 document.addUndoPosition(trans.get("ComponentCfgDlg.Modify") + " " + component.getComponentName());
197         }
198         
199         
200         /* package */
201         static void showDialog(RocketComponent component) {
202                 showDialog(dialog.parent, dialog.document, component);
203         }
204         
205         /**
206          * Hides the configuration dialog.  May be used even if not currently visible.
207          */
208         public static void hideDialog() {
209                 if (dialog != null) {
210                         dialog.closeDialog();
211                 }
212         }
213         
214         
215         /**
216          * Add an undo position for the current document.  This is intended for use only
217          * by the currently open dialog.
218          * 
219          * @param description  Description of the undoable action
220          */
221         /*package*/static void addUndoPosition(String description) {
222                 if (dialog == null) {
223                         throw new IllegalStateException("Dialog not open, report bug!");
224                 }
225                 dialog.document.addUndoPosition(description);
226         }
227         
228         /*package*/
229         static String getUndoDescription() {
230                 if (dialog == null) {
231                         throw new IllegalStateException("Dialog not open, report bug!");
232                 }
233                 return dialog.document.getUndoDescription();
234         }
235         
236         /**
237          * Returns whether the singleton configuration dialog is currently visible or not.
238          */
239         public static boolean isDialogVisible() {
240                 return (dialog != null) && (dialog.isVisible());
241         }
242         
243 }