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