package net.sf.openrocket.gui.configdialog;
import java.awt.Window;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import javax.swing.JDialog;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.Reflection;
/**
* A dialog that contains the configuration elements of one component.
* The contents of the dialog are instantiated from CONFIGDIALOGPACKAGE according
* to the current component.
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class ComponentConfigDialog extends JDialog implements ComponentChangeListener {
private static final long serialVersionUID = 1L;
private static final String CONFIGDIALOGPACKAGE = "net.sf.openrocket.gui.configdialog";
private static final String CONFIGDIALOGPOSTFIX = "Config";
private static ComponentConfigDialog dialog = null;
private OpenRocketDocument document = null;
private RocketComponent component = null;
private RocketComponentConfig configurator = null;
private final Window parent;
private static final Translator trans = Application.getTranslator();
private ComponentConfigDialog(Window parent, OpenRocketDocument document,
RocketComponent component) {
super(parent);
this.parent = parent;
setComponent(document, component);
GUIUtil.setDisposableDialogOptions(this, null);
GUIUtil.rememberWindowPosition(this);
}
/**
* Set the component being configured. The listening connections of the old configurator
* will be removed and the new ones created.
*
* @param component Component to configure.
*/
private void setComponent(OpenRocketDocument document, RocketComponent component) {
if (this.document != null) {
this.document.getRocket().removeComponentChangeListener(this);
}
if (configurator != null) {
// Remove listeners by setting all applicable models to null
GUIUtil.setNullModels(configurator); // null-safe
}
this.document = document;
this.component = component;
this.document.getRocket().addComponentChangeListener(this);
configurator = getDialogContents();
this.setContentPane(configurator);
configurator.updateFields();
//// configuration
setTitle(trans.get("ComponentCfgDlg.configuration1") + " " + component.getComponentName() + " " + trans.get("ComponentCfgDlg.configuration"));
this.pack();
}
/**
* Return the configurator panel of the current component.
*/
private RocketComponentConfig getDialogContents() {
Constructor<? extends RocketComponentConfig> c =
findDialogContentsConstructor(component);
if (c != null) {
try {
return c.newInstance(document, component);
} catch (InstantiationException e) {
throw new BugException("BUG in constructor reflection", e);
} catch (IllegalAccessException e) {
throw new BugException("BUG in constructor reflection", e);
} catch (InvocationTargetException e) {
throw Reflection.handleWrappedException(e);
}
}
// Should never be reached, since RocketComponentConfig should catch all
// components without their own configurator.
throw new BugException("Unable to find any configurator for " + component);
}
private void closeDialog() {
this.setVisible(false);
this.dispose();
this.configurator.invalidateModels();
}
@Override
public void componentChanged(ComponentChangeEvent e) {
if (e.isTreeChange() || e.isUndoChange()) {
// Hide dialog in case of tree or undo change
hideDialog();
} else {
/*
* TODO: HIGH: The line below has caused a NullPointerException (without null check)
* How is this possible? The null check was added to avoid this, but the
* root cause should be analyzed.
* [Openrocket-bugs] 2009-12-12 19:23:22 Automatic bug report for OpenRocket 0.9.5
*/
if (configurator != null)
configurator.updateFields();
}
}
/**
* Finds the Constructor of the given component's config dialog panel in
* CONFIGDIALOGPACKAGE.
*/
@SuppressWarnings("unchecked")
private static Constructor<? extends RocketComponentConfig> findDialogContentsConstructor(RocketComponent component) {
Class<?> currentclass;
String currentclassname;
String configclassname;
Class<?> configclass;
Constructor<? extends RocketComponentConfig> c;
currentclass = component.getClass();
while ((currentclass != null) && (currentclass != Object.class)) {
currentclassname = currentclass.getCanonicalName();
int index = currentclassname.lastIndexOf('.');
if (index >= 0)
currentclassname = currentclassname.substring(index + 1);
configclassname = CONFIGDIALOGPACKAGE + "." + currentclassname +
CONFIGDIALOGPOSTFIX;
try {
configclass = Class.forName(configclassname);
c = (Constructor<? extends RocketComponentConfig>)
configclass.getConstructor(OpenRocketDocument.class, RocketComponent.class);
return c;
} catch (Exception ignore) {
}
currentclass = currentclass.getSuperclass();
}
return null;
}
////////// Static dialog /////////
/**
* A singleton configuration dialog. Will create and show a new dialog if one has not
* previously been used, or update the dialog and show it if a previous one exists.
*
* @param document the document to configure.
* @param component the component to configure.
*/
public static void showDialog(Window parent, OpenRocketDocument document,
RocketComponent component) {
if (dialog != null)
dialog.dispose();
dialog = new ComponentConfigDialog(parent, document, component);
dialog.setVisible(true);
////Modify
document.addUndoPosition(trans.get("ComponentCfgDlg.Modify") + " " + component.getComponentName());
}
/* package */
static void showDialog(RocketComponent component) {
showDialog(dialog.parent, dialog.document, component);
}
/**
* Hides the configuration dialog. May be used even if not currently visible.
*/
public static void hideDialog() {
if (dialog != null) {
dialog.closeDialog();
}
}
/**
* Returns whether the singleton configuration dialog is currently visible or not.
*/
public static boolean isDialogVisible() {
return (dialog != null) && (dialog.isVisible());
}
}