updates for 0.9.4
[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.GUIUtil;
18 import net.sf.openrocket.util.Prefs;
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         
43         private ComponentConfigDialog(Window parent, OpenRocketDocument document, 
44                         RocketComponent component) {
45                 super(parent);
46                 this.parent = parent;
47                 
48                 setComponent(document, component);
49                 
50                 // Set window position according to preferences, and set prefs when moving
51                 Point position = Prefs.getWindowPosition(this.getClass());
52                 if (position == null)
53                         this.setLocationByPlatform(true);
54                 else
55                         this.setLocation(position);
56
57                 this.addComponentListener(new ComponentAdapter() {
58                         @Override
59                         public void componentMoved(ComponentEvent e) {
60                                 Prefs.setWindowPosition(ComponentConfigDialog.this.getClass(), 
61                                                 ComponentConfigDialog.this.getLocation());
62                         }
63                 });
64                 
65                 GUIUtil.setDisposableDialogOptions(this, null);
66         }
67         
68
69         /**
70          * Set the component being configured.  The listening connections of the old configurator
71          * will be removed and the new ones created.
72          * 
73          * @param component  Component to configure.
74          */
75         private void setComponent(OpenRocketDocument document, RocketComponent component) {
76                 if (this.document != null) {
77                         this.document.getRocket().removeComponentChangeListener(this);
78                 }
79
80                 if (configurator != null) {
81                         // Remove listeners by setting all applicable models to null
82                         GUIUtil.setNullModels(configurator);  // null-safe
83                 }
84                 
85                 this.document = document;
86                 this.component = component;
87                 this.document.getRocket().addComponentChangeListener(this);
88                 
89                 configurator = getDialogContents();
90                 this.setContentPane(configurator);
91                 configurator.updateFields();
92                 
93                 setTitle(component.getComponentName()+" configuration");
94
95 //              Dimension pref = getPreferredSize();
96 //              Dimension real = getSize();
97 //              if (pref.width > real.width || pref.height > real.height)
98                 this.pack();
99         }
100         
101         /**
102          * Return the configurator panel of the current component.
103          */
104         private RocketComponentConfig getDialogContents() {
105                 Constructor<? extends RocketComponentConfig> c = 
106                         findDialogContentsConstructor(component);
107                 if (c != null) {
108                         try {
109                                 return (RocketComponentConfig) c.newInstance(component);
110                         } catch (InstantiationException e) {
111                                 throw new RuntimeException("BUG in constructor reflection",e);
112                         } catch (IllegalAccessException e) {
113                                 throw new RuntimeException("BUG in constructor reflection",e);
114                         } catch (InvocationTargetException e) {
115                                 throw new RuntimeException("BUG in constructor reflection",e);
116                         }
117                 }
118                 
119                 // Should never be reached, since RocketComponentConfig should catch all
120                 // components without their own configurator.
121                 throw new RuntimeException("Unable to find any configurator for "+component);
122         }
123
124         /**
125          * Finds the Constructor of the given component's config dialog panel in 
126          * CONFIGDIALOGPACKAGE.
127          */
128         @SuppressWarnings("unchecked")
129         private static Constructor<? extends RocketComponentConfig> 
130                         findDialogContentsConstructor(RocketComponent component) {
131                 Class<?> currentclass;
132                 String currentclassname;
133                 String configclassname;
134                 
135                 Class<?> configclass;
136                 Constructor<? extends RocketComponentConfig> c;
137                 
138                 currentclass = component.getClass();
139                 while ((currentclass != null) && (currentclass != Object.class)) {
140                         currentclassname = currentclass.getCanonicalName();
141                         int index = currentclassname.lastIndexOf('.');
142                         if (index >= 0)
143                                 currentclassname = currentclassname.substring(index + 1);
144                         configclassname = CONFIGDIALOGPACKAGE + "." + currentclassname + 
145                                 CONFIGDIALOGPOSTFIX;
146                         
147                         try {
148                                 configclass = Class.forName(configclassname);
149                                 c = (Constructor<? extends RocketComponentConfig>)
150                                         configclass.getConstructor(RocketComponent.class);
151                                 return c;
152                         } catch (Exception ignore) { }
153
154                         currentclass = currentclass.getSuperclass();
155                 }
156                 return null;
157         }
158         
159         
160         
161
162         //////////  Static dialog  /////////
163         
164         /**
165          * A singleton configuration dialog.  Will create and show a new dialog if one has not 
166          * previously been used, or update the dialog and show it if a previous one exists.
167          * 
168          * @param document              the document to configure.
169          * @param component             the component to configure.
170          */
171         public static void showDialog(Window parent, OpenRocketDocument document, 
172                         RocketComponent component) {
173                 if (dialog != null)
174                         dialog.dispose();
175                 
176                 dialog = new ComponentConfigDialog(parent, document, component);
177                 dialog.setVisible(true);
178                 
179                 document.addUndoPosition("Modify "+component.getComponentName());
180         }
181         
182         
183         /* package */ 
184         static void showDialog(RocketComponent component) {
185                 showDialog(dialog.parent, dialog.document, component);
186         }
187         
188         /**
189          * Hides the configuration dialog.  May be used even if not currently visible.
190          */
191         public static void hideDialog() {
192                 if (dialog != null)
193                         dialog.setVisible(false);
194         }
195
196         
197         /**
198          * Add an undo position for the current document.  This is intended for use only
199          * by the currently open dialog.
200          * 
201          * @param description  Description of the undoable action
202          */
203         /*package*/ static void addUndoPosition(String description) {
204                 if (dialog == null) {
205                         throw new IllegalStateException("Dialog not open, report bug!");
206                 }
207                 dialog.document.addUndoPosition(description);
208         }
209         
210         /*package*/
211         static String getUndoDescription() {
212                 if (dialog == null) {
213                         throw new IllegalStateException("Dialog not open, report bug!");
214                 }
215                 return dialog.document.getUndoDescription();
216         }
217         
218         /**
219          * Returns whether the singleton configuration dialog is currently visible or not.
220          */
221         public static boolean isDialogVisible() {
222                 return (dialog!=null) && (dialog.isVisible());
223         }
224
225
226         public void componentChanged(ComponentChangeEvent e) {
227                 if (e.isTreeChange() || e.isUndoChange()) {
228                         
229                         // Hide dialog in case of tree or undo change
230                         dialog.setVisible(false);
231
232                 } else {
233                         configurator.updateFields();
234                 }
235         }
236         
237 }