openrocket/swing/src/net/sf/openrocket/gui/simulation/SimulationOptionsPanel.java

439 lines
14 KiB
Java

package net.sf.openrocket.gui.simulation;
import java.awt.Color;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.MenuElement;
import javax.swing.SwingUtilities;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.gui.SpinnerEditor;
import net.sf.openrocket.gui.adaptors.DoubleModel;
import net.sf.openrocket.gui.adaptors.EnumModel;
import net.sf.openrocket.gui.components.BasicSlider;
import net.sf.openrocket.gui.components.DescriptionArea;
import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.gui.components.StyledLabel.Style;
import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.gui.util.Icons;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.simulation.RK4SimulationStepper;
import net.sf.openrocket.simulation.SimulationOptions;
import net.sf.openrocket.simulation.extension.SimulationExtension;
import net.sf.openrocket.simulation.extension.SimulationExtensionProvider;
import net.sf.openrocket.simulation.extension.SwingSimulationExtensionConfigurator;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.startup.Preferences;
import net.sf.openrocket.unit.UnitGroup;
import net.sf.openrocket.util.GeodeticComputationStrategy;
import net.sf.openrocket.gui.widgets.SelectColorButton;
import com.google.inject.Key;
class SimulationOptionsPanel extends JPanel {
private static final long serialVersionUID = -5251458539346201239L;
private static final Translator trans = Application.getTranslator();
private OpenRocketDocument document;
final Simulation simulation;
private JPanel currentExtensions;
final JPopupMenu extensionMenu;
JMenu extensionMenuCopyExtension;
SimulationOptionsPanel(OpenRocketDocument document, final Simulation simulation) {
super(new MigLayout("fill"));
this.document = document;
this.simulation = simulation;
final SimulationOptions conditions = simulation.getOptions();
JPanel sub, subsub;
String tip;
JLabel label;
DoubleModel m;
JSpinner spin;
UnitSelector unit;
BasicSlider slider;
// // Simulation options
sub = new JPanel(new MigLayout("fill, gap rel unrel",
"[grow][65lp!][30lp!][75lp!]", ""));
// // Simulator options
sub.setBorder(BorderFactory.createTitledBorder(trans
.get("simedtdlg.border.Simopt")));
this.add(sub, "growx, growy, aligny 0");
// Separate panel for computation methods, as they use a different
// layout
subsub = new JPanel(new MigLayout("insets 0, fill", "[grow][min!][min!][]"));
// // Calculation method:
tip = trans.get("simedtdlg.lbl.ttip.Calcmethod");
label = new JLabel(trans.get("simedtdlg.lbl.Calcmethod"));
label.setToolTipText(tip);
subsub.add(label, "gapright para");
// // Extended Barrowman
label = new JLabel(trans.get("simedtdlg.lbl.ExtBarrowman"));
label.setToolTipText(tip);
subsub.add(label, "growx, span 3, wrap");
// Simulation method
tip = trans.get("simedtdlg.lbl.ttip.Simmethod1")
+ trans.get("simedtdlg.lbl.ttip.Simmethod2");
label = new JLabel(trans.get("simedtdlg.lbl.Simmethod"));
label.setToolTipText(tip);
subsub.add(label, "gapright para");
label = new JLabel("6-DOF Runge-Kutta 4");
label.setToolTipText(tip);
subsub.add(label, "growx, span 3, wrap");
// // Geodetic calculation method:
label = new JLabel(trans.get("simedtdlg.lbl.GeodeticMethod"));
label.setToolTipText(trans.get("simedtdlg.lbl.ttip.GeodeticMethodTip"));
subsub.add(label, "gapright para");
EnumModel<GeodeticComputationStrategy> gcsModel = new EnumModel<GeodeticComputationStrategy>(
conditions, "GeodeticComputation");
final JComboBox<GeodeticComputationStrategy> gcsCombo = new JComboBox<GeodeticComputationStrategy>(gcsModel);
ActionListener gcsTTipListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
GeodeticComputationStrategy gcs = (GeodeticComputationStrategy) gcsCombo
.getSelectedItem();
gcsCombo.setToolTipText(gcs.getDescription());
}
};
gcsCombo.addActionListener(gcsTTipListener);
gcsTTipListener.actionPerformed(null);
subsub.add(gcsCombo, "span 3, wrap para");
// // Time step:
label = new JLabel(trans.get("simedtdlg.lbl.Timestep"));
tip = trans.get("simedtdlg.lbl.ttip.Timestep1")
+ trans.get("simedtdlg.lbl.ttip.Timestep2")
+ " "
+ UnitGroup.UNITS_TIME_STEP
.toStringUnit(RK4SimulationStepper.RECOMMENDED_TIME_STEP)
+ ".";
label.setToolTipText(tip);
subsub.add(label, "gapright para");
m = new DoubleModel(conditions, "TimeStep", UnitGroup.UNITS_TIME_STEP,
0.01, 1);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin));
spin.setToolTipText(tip);
subsub.add(spin, "");
unit = new UnitSelector(m);
unit.setToolTipText(tip);
subsub.add(unit, "");
slider = new BasicSlider(m.getSliderModel(0.01, 0.2));
slider.setToolTipText(tip);
subsub.add(slider, "w 100");
sub.add(subsub, "spanx, wrap para");
// Reset to default button
JButton button = new SelectColorButton(trans.get("simedtdlg.but.resettodefault"));
// Reset the time step to its default value (
button.setToolTipText(trans.get("simedtdlg.but.ttip.resettodefault")
+ UnitGroup.UNITS_SHORT_TIME
.toStringUnit(RK4SimulationStepper.RECOMMENDED_TIME_STEP)
+ ").");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Preferences preferences = Application.getPreferences();
conditions.setTimeStep(preferences.getDouble(
Preferences.SIMULATION_TIME_STEP,
RK4SimulationStepper.RECOMMENDED_TIME_STEP));
conditions.setGeodeticComputation(preferences.getEnum(
Preferences.GEODETIC_COMPUTATION,
GeodeticComputationStrategy.SPHERICAL));
}
});
sub.add(button, "align left");
//// Simulation extensions
sub = new JPanel(new MigLayout("fill, gap 0 0"));
sub.setBorder(BorderFactory.createTitledBorder(trans.get("simedtdlg.border.SimExt")));
this.add(sub, "wmin 300lp, growx, growy");
DescriptionArea desc = new DescriptionArea(5);
desc.setText(trans.get("simedtdlg.SimExt.desc"));
sub.add(desc, "aligny 0, hmin 100lp, growx, wrap para");
final JButton addExtension = new SelectColorButton(trans.get("simedtdlg.SimExt.add"));
extensionMenu = getExtensionMenu();
addExtension.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
extensionMenu.show(addExtension, 5, addExtension.getBounds().height);
}
});
sub.add(addExtension, "growx, wrap 0");
currentExtensions = new JPanel(new MigLayout("fillx, gap 0 0, ins 0"));
JScrollPane scroll = new JScrollPane(currentExtensions);
// &#$%! scroll pane will not honor "growy"...
sub.add(scroll, "growx, growy, h 100%");
updateCurrentExtensions();
}
private JPopupMenu getExtensionMenu() {
Set<SimulationExtensionProvider> extensions = Application.getInjector().getInstance(new Key<Set<SimulationExtensionProvider>>() {
});
JPopupMenu basemenu = new JPopupMenu();
//// Use code / Launch conditions
for (final SimulationExtensionProvider provider : extensions) {
List<String> ids = provider.getIds();
for (final String id : ids) {
List<String> menuItems = provider.getName(id);
if (menuItems != null) {
JComponent menu = findMenu(basemenu, menuItems);
JMenuItem item = new JMenuItem(menuItems.get(menuItems.size() - 1));
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
SimulationExtension e = provider.getInstance(id);
simulation.getSimulationExtensions().add(e);
updateCurrentExtensions();
SwingSimulationExtensionConfigurator configurator = findConfigurator(e);
if (configurator != null) {
configurator.configure(e, simulation, SwingUtilities.windowForComponent(SimulationOptionsPanel.this));
updateCurrentExtensions();
}
}
});
menu.add(item);
}
}
}
//// Copy extension
updateExtensionMenuCopyExtension(basemenu);
return basemenu;
}
/**
* Updates the contents of the "Copy extension" menu item in the extension menu.
* @param extensionMenu extension menu to add the "Copy extension" menu item to
*/
private void updateExtensionMenuCopyExtension(JPopupMenu extensionMenu) {
if (extensionMenu == null) {
return;
}
if (this.extensionMenuCopyExtension != null) {
extensionMenu.remove(this.extensionMenuCopyExtension);
}
this.extensionMenuCopyExtension = null;
for (Simulation sim : document.getSimulations()) {
if (sim.getSimulationExtensions().isEmpty()) {
continue;
}
JMenu menu = new JMenu(sim.getName());
for (final SimulationExtension ext : sim.getSimulationExtensions()) {
JMenuItem item = new JMenuItem(ext.getName());
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
SimulationExtension e = ext.clone();
simulation.getSimulationExtensions().add(e);
updateCurrentExtensions();
SwingSimulationExtensionConfigurator configurator = findConfigurator(e);
if (configurator != null) {
configurator.configure(e, simulation, SwingUtilities.windowForComponent(SimulationOptionsPanel.this));
updateCurrentExtensions();
}
}
});
menu.add(item);
}
if (this.extensionMenuCopyExtension == null) {
this.extensionMenuCopyExtension = new JMenu(trans.get("simedtdlg.SimExt.copyExtension"));
}
this.extensionMenuCopyExtension.add(menu);
}
if (this.extensionMenuCopyExtension != null) {
extensionMenu.add(this.extensionMenuCopyExtension);
}
}
private JComponent findMenu(MenuElement menu, List<String> menuItems) {
for (int i = 0; i < menuItems.size() - 1; i++) {
String menuItem = menuItems.get(i);
MenuElement found = null;
for (MenuElement e : menu.getSubElements()) {
if (e instanceof JMenu && ((JMenu) e).getText().equals(menuItem)) {
found = e;
break;
}
}
if (found != null) {
menu = found;
} else {
JMenu m = new JMenu(menuItem);
((JComponent) menu).add(m);
menu = m;
}
}
return (JComponent) menu;
}
private void updateCurrentExtensions() {
currentExtensions.removeAll();
if (simulation.getSimulationExtensions().isEmpty()) {
StyledLabel l = new StyledLabel(trans.get("simedtdlg.SimExt.noExtensions"), Style.ITALIC);
l.setForeground(Color.DARK_GRAY);
currentExtensions.add(l, "growx, pad 5 5 5 5, wrap");
} else {
for (SimulationExtension e : simulation.getSimulationExtensions()) {
currentExtensions.add(new SimulationExtensionPanel(e), "growx, wrap");
}
}
updateExtensionMenuCopyExtension(this.extensionMenu);
// Both needed:
this.revalidate();
this.repaint();
}
private class SimulationExtensionPanel extends JPanel {
/**
*
*/
private static final long serialVersionUID = -3296795614810745035L;
public SimulationExtensionPanel(final SimulationExtension extension) {
super(new MigLayout("fillx, gapx 0"));
this.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));
this.add(new JLabel(extension.getName()), "spanx, growx, wrap");
JButton button;
this.add(new JPanel(), "spanx, split, growx, right");
// Configure
if (findConfigurator(extension) != null) {
button = new SelectColorButton(Icons.CONFIGURE);
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
findConfigurator(extension).configure(extension, simulation,
SwingUtilities.windowForComponent(SimulationOptionsPanel.this));
updateCurrentExtensions();
}
});
this.add(button, "right");
}
// Help
if (extension.getDescription() != null) {
button = new SelectColorButton(Icons.HELP);
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final JDialog dialog = new JDialog(SwingUtilities.windowForComponent(SimulationOptionsPanel.this),
extension.getName(), ModalityType.APPLICATION_MODAL);
JPanel panel = new JPanel(new MigLayout("fill"));
DescriptionArea area = new DescriptionArea(extension.getDescription(), 10, 0);
panel.add(area, "width 400lp, wrap para");
JButton close = new SelectColorButton(trans.get("button.close"));
close.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
dialog.setVisible(false);
}
});
panel.add(close, "right");
dialog.add(panel);
GUIUtil.setDisposableDialogOptions(dialog, close);
dialog.setLocationRelativeTo(SwingUtilities.windowForComponent(SimulationOptionsPanel.this));
dialog.setVisible(true);
}
});
this.add(button, "right");
}
// Delete
button = new SelectColorButton(Icons.EDIT_DELETE);
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
Iterator<SimulationExtension> iter = simulation.getSimulationExtensions().iterator();
while (iter.hasNext()) {
// Compare with identity
if (iter.next() == extension) {
iter.remove();
break;
}
}
updateCurrentExtensions();
}
});
this.add(button, "right");
}
}
private SwingSimulationExtensionConfigurator findConfigurator(SimulationExtension extension) {
Set<SwingSimulationExtensionConfigurator> configurators = Application.getInjector().getInstance(new Key<Set<SwingSimulationExtensionConfigurator>>() {
});
for (SwingSimulationExtensionConfigurator c : configurators) {
if (c.support(extension)) {
return c;
}
}
return null;
}
}