1 package net.sf.openrocket.gui.configdialog;
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;
11 import javax.swing.JDialog;
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;
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.
29 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
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";
38 private static ComponentConfigDialog dialog = null;
41 private OpenRocketDocument document = null;
42 private RocketComponent component = null;
43 private RocketComponentConfig configurator = null;
45 private final Window parent;
46 private static final Translator trans = Application.getTranslator();
48 private ComponentConfigDialog(Window parent, OpenRocketDocument document,
49 RocketComponent component) {
53 setComponent(document, component);
55 GUIUtil.setDisposableDialogOptions(this, null);
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);
64 this.addComponentListener(new ComponentAdapter() {
66 public void componentMoved(ComponentEvent e) {
67 Prefs.setWindowPosition(ComponentConfigDialog.this.getClass(),
68 ComponentConfigDialog.this.getLocation());
76 * Set the component being configured. The listening connections of the old configurator
77 * will be removed and the new ones created.
79 * @param component Component to configure.
81 private void setComponent(OpenRocketDocument document, RocketComponent component) {
82 if (this.document != null) {
83 this.document.getRocket().removeComponentChangeListener(this);
86 if (configurator != null) {
87 // Remove listeners by setting all applicable models to null
88 GUIUtil.setNullModels(configurator); // null-safe
91 this.document = document;
92 this.component = component;
93 this.document.getRocket().addComponentChangeListener(this);
95 configurator = getDialogContents();
96 this.setContentPane(configurator);
97 configurator.updateFields();
100 setTitle(component.getComponentName() + " " + trans.get("ComponentCfgDlg.configuration"));
102 // Dimension pref = getPreferredSize();
103 // Dimension real = getSize();
104 // if (pref.width > real.width || pref.height > real.height)
109 * Return the configurator panel of the current component.
111 private RocketComponentConfig getDialogContents() {
112 Constructor<? extends RocketComponentConfig> c =
113 findDialogContentsConstructor(component);
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);
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);
132 private void closeDialog() {
133 this.setVisible(false);
135 this.configurator.invalidateModels();
140 public void componentChanged(ComponentChangeEvent e) {
141 if (e.isTreeChange() || e.isUndoChange()) {
143 // Hide dialog in case of tree or undo change
144 dialog.closeDialog();
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
153 if (configurator != null)
154 configurator.updateFields();
160 * Finds the Constructor of the given component's config dialog panel in
161 * CONFIGDIALOGPACKAGE.
163 @SuppressWarnings("unchecked")
164 private static Constructor<? extends RocketComponentConfig> findDialogContentsConstructor(RocketComponent component) {
165 Class<?> currentclass;
166 String currentclassname;
167 String configclassname;
169 Class<?> configclass;
170 Constructor<? extends RocketComponentConfig> c;
172 currentclass = component.getClass();
173 while ((currentclass != null) && (currentclass != Object.class)) {
174 currentclassname = currentclass.getCanonicalName();
175 int index = currentclassname.lastIndexOf('.');
177 currentclassname = currentclassname.substring(index + 1);
178 configclassname = CONFIGDIALOGPACKAGE + "." + currentclassname +
182 configclass = Class.forName(configclassname);
183 c = (Constructor<? extends RocketComponentConfig>)
184 configclass.getConstructor(RocketComponent.class);
186 } catch (Exception ignore) {
189 currentclass = currentclass.getSuperclass();
197 ////////// Static dialog /////////
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.
203 * @param document the document to configure.
204 * @param component the component to configure.
206 public static void showDialog(Window parent, OpenRocketDocument document,
207 RocketComponent component) {
211 dialog = new ComponentConfigDialog(parent, document, component);
212 dialog.setVisible(true);
215 document.addUndoPosition(trans.get("ComponentCfgDlg.Modify") + " " + component.getComponentName());
220 static void showDialog(RocketComponent component) {
221 showDialog(dialog.parent, dialog.document, component);
225 * Hides the configuration dialog. May be used even if not currently visible.
227 public static void hideDialog() {
228 if (dialog != null) {
229 dialog.closeDialog();
235 * Add an undo position for the current document. This is intended for use only
236 * by the currently open dialog.
238 * @param description Description of the undoable action
240 /*package*/static void addUndoPosition(String description) {
241 if (dialog == null) {
242 throw new IllegalStateException("Dialog not open, report bug!");
244 dialog.document.addUndoPosition(description);
248 static String getUndoDescription() {
249 if (dialog == null) {
250 throw new IllegalStateException("Dialog not open, report bug!");
252 return dialog.document.getUndoDescription();
256 * Returns whether the singleton configuration dialog is currently visible or not.
258 public static boolean isDialogVisible() {
259 return (dialog != null) && (dialog.isVisible());