1 package net.sf.openrocket.gui.configdialog;
4 import java.awt.Component;
5 import java.awt.Container;
7 import java.awt.Window;
8 import java.awt.event.ComponentAdapter;
9 import java.awt.event.ComponentEvent;
10 import java.lang.reflect.Constructor;
11 import java.lang.reflect.InvocationTargetException;
13 import javax.swing.DefaultBoundedRangeModel;
14 import javax.swing.JDialog;
15 import javax.swing.JSlider;
16 import javax.swing.JSpinner;
17 import javax.swing.SpinnerNumberModel;
19 import net.sf.openrocket.document.OpenRocketDocument;
20 import net.sf.openrocket.gui.Resettable;
21 import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
22 import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
23 import net.sf.openrocket.rocketcomponent.RocketComponent;
24 import net.sf.openrocket.util.GUIUtil;
25 import net.sf.openrocket.util.Prefs;
28 * A JFrame dialog that contains the configuration elements of one component.
29 * The contents of the dialog are instantiated from CONFIGDIALOGPACKAGE according
30 * to the current component.
32 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
35 public class ComponentConfigDialog extends JDialog implements ComponentChangeListener {
36 private static final long serialVersionUID = 1L;
37 private static final String CONFIGDIALOGPACKAGE = "net.sf.openrocket.gui.configdialog";
38 private static final String CONFIGDIALOGPOSTFIX = "Config";
41 private static ComponentConfigDialog dialog = null;
44 private OpenRocketDocument document = null;
45 private RocketComponent component = null;
46 private RocketComponentConfig configurator = null;
48 private final Window parent;
50 private ComponentConfigDialog(Window parent, OpenRocketDocument document,
51 RocketComponent component) {
55 setComponent(document, component);
57 // Set window position according to preferences, and set prefs when moving
58 Point position = Prefs.getWindowPosition(this.getClass());
60 this.setLocationByPlatform(true);
62 this.setLocation(position);
64 this.addComponentListener(new ComponentAdapter() {
66 public void componentMoved(ComponentEvent e) {
67 Prefs.setWindowPosition(ComponentConfigDialog.this.getClass(),
68 ComponentConfigDialog.this.getLocation());
73 // Install ESC listener
74 GUIUtil.installEscapeCloseOperation(this);
79 * Set the component being configured. The listening connections of the old configurator
80 * will be removed and the new ones created.
82 * @param component Component to configure.
84 private void setComponent(OpenRocketDocument document, RocketComponent component) {
85 if (this.document != null) {
86 this.document.getRocket().removeComponentChangeListener(this);
89 if (configurator != null) {
90 // Remove listeners by setting all applicable models to null
91 setNullModels(configurator); // null-safe
93 // mainPanel.remove(configurator);
96 this.document = document;
97 this.component = component;
98 this.document.getRocket().addComponentChangeListener(this);
100 configurator = getDialogContents();
101 this.setContentPane(configurator);
102 configurator.updateFields();
103 // mainPanel.add(configurator,"cell 0 0, growx, growy");
105 setTitle(component.getComponentName()+" configuration");
107 // Dimension pref = getPreferredSize();
108 // Dimension real = getSize();
109 // if (pref.width > real.width || pref.height > real.height)
114 * Traverses recursively the component tree, and sets all applicable component
115 * models to null, so as to remove the listener connections.
117 * NOTE: All components in the configuration dialogs that use custom models must be added
120 private void setNullModels(Component c) {
124 // Remove models for known components
125 // Why the FSCK must this be so hard?!?!?
127 if (c instanceof JSpinner) {
128 ((JSpinner)c).setModel(new SpinnerNumberModel());
129 } else if (c instanceof JSlider) {
130 ((JSlider)c).setModel(new DefaultBoundedRangeModel());
131 } else if (c instanceof Resettable) {
132 ((Resettable)c).resetModel();
136 if (c instanceof Container) {
137 Component[] cs = ((Container)c).getComponents();
138 for (Component sub: cs)
146 * Return the configurator panel of the current component.
148 private RocketComponentConfig getDialogContents() {
149 Constructor<? extends RocketComponentConfig> c =
150 findDialogContentsConstructor(component);
153 return (RocketComponentConfig) c.newInstance(component);
154 } catch (InstantiationException e) {
155 throw new RuntimeException("BUG in constructor reflection",e);
156 } catch (IllegalAccessException e) {
157 throw new RuntimeException("BUG in constructor reflection",e);
158 } catch (InvocationTargetException e) {
159 throw new RuntimeException("BUG in constructor reflection",e);
163 // Should never be reached, since RocketComponentConfig should catch all
164 // components without their own configurator.
165 throw new RuntimeException("Unable to find any configurator for "+component);
169 * Finds the Constructor of the given component's config dialog panel in
170 * CONFIGDIALOGPACKAGE.
172 @SuppressWarnings("unchecked")
173 private static Constructor<? extends RocketComponentConfig>
174 findDialogContentsConstructor(RocketComponent component) {
175 Class<?> currentclass;
176 String currentclassname;
177 String configclassname;
179 Class<?> configclass;
180 Constructor<? extends RocketComponentConfig> c;
182 currentclass = component.getClass();
183 while ((currentclass != null) && (currentclass != Object.class)) {
184 currentclassname = currentclass.getCanonicalName();
185 int index = currentclassname.lastIndexOf('.');
187 currentclassname = currentclassname.substring(index + 1);
188 configclassname = CONFIGDIALOGPACKAGE + "." + currentclassname +
192 configclass = Class.forName(configclassname);
193 c = (Constructor<? extends RocketComponentConfig>)
194 configclass.getConstructor(RocketComponent.class);
196 } catch (Exception ignore) { }
198 currentclass = currentclass.getSuperclass();
206 ////////// Static dialog /////////
209 * A singleton configuration dialog. Will create and show a new dialog if one has not
210 * previously been used, or update the dialog and show it if a previous one exists.
212 * @param document the document to configure.
213 * @param component the component to configure.
215 public static void showDialog(Window parent, OpenRocketDocument document,
216 RocketComponent component) {
220 dialog = new ComponentConfigDialog(parent, document, component);
221 dialog.setVisible(true);
223 document.addUndoPosition("Modify "+component.getComponentName());
228 static void showDialog(RocketComponent component) {
229 showDialog(dialog.parent, dialog.document, component);
233 * Hides the configuration dialog. May be used even if not currently visible.
235 public static void hideDialog() {
237 dialog.setVisible(false);
242 * Add an undo position for the current document. This is intended for use only
243 * by the currently open dialog.
245 * @param description Description of the undoable action
247 /*package*/ static void addUndoPosition(String description) {
248 if (dialog == null) {
249 throw new IllegalStateException("Dialog not open, report bug!");
251 dialog.document.addUndoPosition(description);
255 static String getUndoDescription() {
256 if (dialog == null) {
257 throw new IllegalStateException("Dialog not open, report bug!");
259 return dialog.document.getUndoDescription();
263 * Returns whether the singleton configuration dialog is currently visible or not.
265 public static boolean isDialogVisible() {
266 return (dialog!=null) && (dialog.isVisible());
270 public void componentChanged(ComponentChangeEvent e) {
271 if (e.isTreeChange() || e.isUndoChange()) {
273 // Hide dialog in case of tree or undo change
274 dialog.setVisible(false);
277 configurator.updateFields();