added svn:ignores
[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 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         private void closeDialog() {
129                 this.setVisible(false);
130                 this.dispose();
131                 this.configurator.invalidateModels();
132         }
133         
134         
135         @Override
136         public void componentChanged(ComponentChangeEvent e) {
137                 if (e.isTreeChange() || e.isUndoChange()) {
138                         
139                         // Hide dialog in case of tree or undo change
140                         dialog.closeDialog();
141                         
142                 } else {
143                         /*
144                          * TODO: HIGH:  The line below has caused a NullPointerException (without null check)
145                          * How is this possible?  The null check was added to avoid this, but the
146                          * root cause should be analyzed.
147                          * [Openrocket-bugs] 2009-12-12 19:23:22 Automatic bug report for OpenRocket 0.9.5
148                          */
149                         if (configurator != null)
150                                 configurator.updateFields();
151                 }
152         }
153         
154         
155         /**
156          * Finds the Constructor of the given component's config dialog panel in 
157          * CONFIGDIALOGPACKAGE.
158          */
159         @SuppressWarnings("unchecked")
160         private static Constructor<? extends RocketComponentConfig> findDialogContentsConstructor(RocketComponent component) {
161                 Class<?> currentclass;
162                 String currentclassname;
163                 String configclassname;
164                 
165                 Class<?> configclass;
166                 Constructor<? extends RocketComponentConfig> c;
167                 
168                 currentclass = component.getClass();
169                 while ((currentclass != null) && (currentclass != Object.class)) {
170                         currentclassname = currentclass.getCanonicalName();
171                         int index = currentclassname.lastIndexOf('.');
172                         if (index >= 0)
173                                 currentclassname = currentclassname.substring(index + 1);
174                         configclassname = CONFIGDIALOGPACKAGE + "." + currentclassname +
175                                         CONFIGDIALOGPOSTFIX;
176                         
177                         try {
178                                 configclass = Class.forName(configclassname);
179                                 c = (Constructor<? extends RocketComponentConfig>)
180                                                 configclass.getConstructor(RocketComponent.class);
181                                 return c;
182                         } catch (Exception ignore) {
183                         }
184                         
185                         currentclass = currentclass.getSuperclass();
186                 }
187                 return null;
188         }
189         
190         
191
192
193         //////////  Static dialog  /////////
194         
195         /**
196          * A singleton configuration dialog.  Will create and show a new dialog if one has not 
197          * previously been used, or update the dialog and show it if a previous one exists.
198          * 
199          * @param document              the document to configure.
200          * @param component             the component to configure.
201          */
202         public static void showDialog(Window parent, OpenRocketDocument document,
203                         RocketComponent component) {
204                 if (dialog != null)
205                         dialog.dispose();
206                 
207                 dialog = new ComponentConfigDialog(parent, document, component);
208                 dialog.setVisible(true);
209                 
210                 document.addUndoPosition("Modify " + component.getComponentName());
211         }
212         
213         
214         /* package */
215         static void showDialog(RocketComponent component) {
216                 showDialog(dialog.parent, dialog.document, component);
217         }
218         
219         /**
220          * Hides the configuration dialog.  May be used even if not currently visible.
221          */
222         public static void hideDialog() {
223                 if (dialog != null) {
224                         dialog.closeDialog();
225                 }
226         }
227         
228         
229         /**
230          * Add an undo position for the current document.  This is intended for use only
231          * by the currently open dialog.
232          * 
233          * @param description  Description of the undoable action
234          */
235         /*package*/static void addUndoPosition(String description) {
236                 if (dialog == null) {
237                         throw new IllegalStateException("Dialog not open, report bug!");
238                 }
239                 dialog.document.addUndoPosition(description);
240         }
241         
242         /*package*/
243         static String getUndoDescription() {
244                 if (dialog == null) {
245                         throw new IllegalStateException("Dialog not open, report bug!");
246                 }
247                 return dialog.document.getUndoDescription();
248         }
249         
250         /**
251          * Returns whether the singleton configuration dialog is currently visible or not.
252          */
253         public static boolean isDialogVisible() {
254                 return (dialog != null) && (dialog.isVisible());
255         }
256         
257 }