diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties
index 2d2cc8b79..650df356d 100644
--- a/core/resources/l10n/messages.properties
+++ b/core/resources/l10n/messages.properties
@@ -452,9 +452,10 @@ simedtdlg.but.delete = Delete
simedtdlg.title.Editsim = Edit simulation
simedtdlg.title.MultiSimEdit = Multi-simulation edit
simedtdlg.title.MultiSimEdit.ttip = You are editing the following simulations:
-simedtdlg.lbl.Simname = Simulation name:
+simedtdlg.lbl.Simname = Name:
simedtdlg.tab.Launchcond = Launch conditions
simedtdlg.tab.Simopt = Simulation options
+simedtdlg.tab.Settings = Settings
simedtdlg.tab.Plotdata = Plot data
simedtdlg.tab.CustomExpressions = Custom expressions
simedtdlg.tab.Exportdata = Export data
@@ -534,6 +535,10 @@ simedtdlg.IntensityDesc.High = High
simedtdlg.IntensityDesc.Veryhigh = Very high
simedtdlg.IntensityDesc.Extreme = Extreme
+! SimulationConfigDialog
+SimulationConfigDialog.tab.plotDis.ttip = Plotting not supported for multi-simulation editing
+SimulationConfigDialog.tab.expDis.ttip = Exporting not supported for multi-simulation editing
+
SimulationExtension.airstart.name.alt = Air-start ({alt})
SimulationExtension.airstart.name.altvel = Air-start ({alt}, {vel})
SimulationExtension.javacode.name = Java code
diff --git a/core/resources/l10n/messages_uk_UA.properties b/core/resources/l10n/messages_uk_UA.properties
index 72c45b44e..a99a4ccb3 100644
--- a/core/resources/l10n/messages_uk_UA.properties
+++ b/core/resources/l10n/messages_uk_UA.properties
@@ -321,7 +321,7 @@ simedtdlg.but.savedefault = Save as default
simedtdlg.but.add = Add
simedtdlg.but.delete = Delete
simedtdlg.title.Editsim = Edit simulation
-simedtdlg.lbl.Simname = Simulation name:
+simedtdlg.lbl.Simname = Name:
simedtdlg.tab.Launchcond = Launch conditions
simedtdlg.tab.Simopt = Simulation options
simedtdlg.tab.Plotdata = Plot data
diff --git a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java
index 5f762ee11..7a2ee0cfb 100644
--- a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java
+++ b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java
@@ -45,6 +45,7 @@ import javax.swing.table.DefaultTableCellRenderer;
import net.sf.openrocket.arch.SystemInfo;
import net.sf.openrocket.gui.components.CsvOptionPanel;
+import net.sf.openrocket.gui.simulation.SimulationConfigDialog;
import net.sf.openrocket.gui.util.ColorConversion;
import net.sf.openrocket.gui.util.FileHelper;
import net.sf.openrocket.gui.util.GUIUtil;
@@ -57,7 +58,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.logging.Warning;
import net.sf.openrocket.logging.WarningSet;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.document.Simulation;
@@ -72,7 +72,6 @@ import net.sf.openrocket.gui.adaptors.ColumnTableModel;
import net.sf.openrocket.gui.adaptors.ColumnTableRowSorter;
import net.sf.openrocket.gui.adaptors.ValueColumn;
import net.sf.openrocket.gui.components.StyledLabel;
-import net.sf.openrocket.gui.simulation.SimulationEditDialog;
import net.sf.openrocket.gui.simulation.SimulationRunDialog;
import net.sf.openrocket.gui.simulation.SimulationWarningDialog;
import net.sf.openrocket.gui.util.Icons;
@@ -888,9 +887,11 @@ public class SimulationPanel extends JPanel {
}
private void openDialog(boolean plotMode, boolean isNewSimulation, final Simulation... sims) {
- SimulationEditDialog d = new SimulationEditDialog(SwingUtilities.getWindowAncestor(this), document, isNewSimulation, sims);
+ SimulationConfigDialog d = new SimulationConfigDialog(SwingUtilities.getWindowAncestor(this), document, isNewSimulation, sims);
if (plotMode) {
- d.setPlotMode();
+ d.switchToPlotTab();
+ } else {
+ d.switchToSettingsTab();
}
d.setVisible(true);
fireMaintainSelection();
diff --git a/swing/src/net/sf/openrocket/gui/simulation/SimulationConfigDialog.java b/swing/src/net/sf/openrocket/gui/simulation/SimulationConfigDialog.java
new file mode 100644
index 000000000..a1037cfe1
--- /dev/null
+++ b/swing/src/net/sf/openrocket/gui/simulation/SimulationConfigDialog.java
@@ -0,0 +1,456 @@
+package net.sf.openrocket.gui.simulation;
+
+import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.document.OpenRocketDocument;
+import net.sf.openrocket.document.Simulation;
+import net.sf.openrocket.document.events.DocumentChangeEvent;
+import net.sf.openrocket.gui.components.ConfigurationComboBox;
+import net.sf.openrocket.gui.components.StyledLabel;
+import net.sf.openrocket.gui.util.GUIUtil;
+import net.sf.openrocket.gui.util.UITheme;
+import net.sf.openrocket.gui.widgets.SelectColorButton;
+import net.sf.openrocket.l10n.Translator;
+import net.sf.openrocket.rocketcomponent.FlightConfiguration;
+import net.sf.openrocket.rocketcomponent.FlightConfigurationId;
+import net.sf.openrocket.rocketcomponent.Rocket;
+import net.sf.openrocket.simulation.extension.SimulationExtension;
+import net.sf.openrocket.startup.Application;
+import net.sf.openrocket.startup.Preferences;
+import net.sf.openrocket.util.StateChangeListener;
+
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTabbedPane;
+import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import java.awt.Color;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowListener;
+import java.util.ArrayList;
+import java.util.EventObject;
+import java.util.List;
+
+public class SimulationConfigDialog extends JDialog {
+ private static final long serialVersionUID = -1068127685642912715L;
+
+ private final Window parentWindow;
+ private final Simulation[] simulationList;
+ private final OpenRocketDocument document;
+ private final JTabbedPane tabbedPane;
+ private JButton okButton;
+ private JButton cancelButton;
+ private static final Translator trans = Application.getTranslator();
+ private static final Preferences preferences = Application.getPreferences();
+
+
+ private final WindowListener applyChangesToSimsListener;
+ private final Simulation initialSim; // A copy of the first selected simulation before it was modified
+ private final boolean initialIsSaved; // Whether the document was saved before the dialog was opened
+ private boolean isModified = false; // Whether the simulation has been modified
+ private final boolean isNewSimulation; // Whether you are editing a new simulation, or an existing one
+
+ private static final int SETTINGS_IDX = 0;
+ private static final int PLOT_IDX = 1;
+ private static final int EXPORT_IDX = 2;
+
+ private final SimulationPlotPanel plotTab;
+ private final SimulationExportPanel exportTab;
+
+ private static Color multiCompEditColor;
+
+ static {
+ initColors();
+ }
+
+ public SimulationConfigDialog(Window parent, final OpenRocketDocument document, boolean isNewSimulation, Simulation... sims) {
+ super(parent, sims.length == 1 ? trans.get("simedtdlg.title.Editsim") : trans.get("simedtdlg.title.MultiSimEdit"),
+ JDialog.ModalityType.DOCUMENT_MODAL);
+ this.document = document;
+ this.parentWindow = parent;
+ this.simulationList = sims;
+ this.initialSim = simulationList[0].clone();
+ this.initialIsSaved = document.isSaved();
+ this.isNewSimulation = isNewSimulation;
+
+ simulationList[0].addChangeListener(new StateChangeListener() {
+ @Override
+ public void stateChanged(EventObject e) {
+ isModified = true;
+ setTitle("* " + getTitle()); // Add component changed indicator to the title
+ simulationList[0].removeChangeListener(this);
+ }
+ });
+
+ final JPanel contentPanel = new JPanel(new MigLayout("fill"));
+
+ // ======== Top panel ========
+ addTopPanel(document, contentPanel);
+
+
+ // ======== Tabbed pane ========
+ this.tabbedPane = new JTabbedPane();
+
+ //// Simulation Settings
+ final SimulationSettingsPanel settingsTab = new SimulationSettingsPanel(document, simulationList[0]);
+ tabbedPane.addTab(trans.get("simedtdlg.tab.Settings"), settingsTab);
+
+ //// Plot data
+ this.plotTab = new SimulationPlotPanel(simulationList[0]);
+ tabbedPane.addTab(trans.get("simedtdlg.tab.Plotdata"), plotTab);
+ if (isMultiCompEdit()) {
+ tabbedPane.setEnabledAt(PLOT_IDX, false);
+ tabbedPane.setToolTipTextAt(PLOT_IDX, trans.get("SimulationConfigDialog.tab.plotDis.ttip"));
+ }
+
+ //// Export data
+ this.exportTab = new SimulationExportPanel(simulationList[0]);
+ tabbedPane.addTab(trans.get("simedtdlg.tab.Exportdata"), exportTab);
+ if (isMultiCompEdit()) {
+ tabbedPane.setEnabledAt(EXPORT_IDX, false);
+ tabbedPane.setToolTipTextAt(EXPORT_IDX, trans.get("SimulationConfigDialog.tab.expDis.ttip"));
+ }
+
+ contentPanel.add(tabbedPane, "grow, wrap");
+
+
+ // ======== Bottom panel ========
+ addBottomPanel(contentPanel);
+
+
+ tabbedPane.addChangeListener(new ChangeListener() {
+
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ if (okButton == null) {
+ return;
+ }
+ int selectedIndex = tabbedPane.getSelectedIndex();
+ switch (selectedIndex) {
+ case SETTINGS_IDX:
+ okButton.setText(trans.get("dlg.but.ok"));
+ cancelButton.setText(trans.get("dlg.but.cancel"));
+ SimulationConfigDialog.this.revalidate();
+ break;
+ case PLOT_IDX:
+ okButton.setText(trans.get("SimulationEditDialog.btn.plot"));
+ cancelButton.setText(trans.get("dlg.but.close"));
+ SimulationConfigDialog.this.revalidate();
+ break;
+ case EXPORT_IDX:
+ okButton.setText(trans.get("SimulationEditDialog.btn.export"));
+ cancelButton.setText(trans.get("dlg.but.close"));
+ SimulationConfigDialog.this.revalidate();
+ break;
+ }
+ }
+
+ });
+
+ this.add(contentPanel);
+ this.validate();
+ this.pack();
+
+ this.setLocationByPlatform(true);
+
+ this.applyChangesToSimsListener = new WindowAdapter() {
+ @Override
+ public void windowClosed(WindowEvent e) {
+ copyChangesToAllSims();
+ }
+ };
+ this.addWindowListener(applyChangesToSimsListener);
+
+ GUIUtil.setDisposableDialogOptions(this, null);
+ }
+
+ private static void initColors() {
+ updateColors();
+ UITheme.Theme.addUIThemeChangeListener(SimulationConfigDialog::updateColors);
+ }
+
+ private static void updateColors() {
+ multiCompEditColor = GUIUtil.getUITheme().getMultiCompEditColor();
+ }
+
+ public void switchToSettingsTab() {
+ tabbedPane.setSelectedIndex(SETTINGS_IDX);
+ }
+
+ public void switchToPlotTab() {
+ tabbedPane.setSelectedIndex(PLOT_IDX);
+ }
+
+ public void switchToExportTab() {
+ tabbedPane.setSelectedIndex(EXPORT_IDX);
+ }
+
+ private void addTopPanel(OpenRocketDocument document, JPanel contentPanel) {
+ JPanel topPanel = new JPanel(new MigLayout("fill, ins 0"));
+
+ //// Name:
+ topPanel.add(new JLabel(trans.get("simedtdlg.lbl.Simname") + " "), "growx 0, gapright para");
+ final JTextField field = new JTextField(simulationList[0].getName());
+ field.getDocument().addDocumentListener(new DocumentListener() {
+ @Override
+ public void changedUpdate(DocumentEvent e) {
+ setText();
+ }
+
+ @Override
+ public void insertUpdate(DocumentEvent e) {
+ setText();
+ }
+
+ @Override
+ public void removeUpdate(DocumentEvent e) {
+ setText();
+ }
+
+ private void setText() {
+ String name = field.getText();
+ if (name == null || name.equals(""))
+ return;
+ simulationList[0].setName(name);
+
+ }
+ });
+ topPanel.add(field, "growx, wrap");
+
+ //// Flight selector
+ //// Flight configuration:
+ JLabel label = new JLabel(trans.get("simedtdlg.lbl.Flightcfg"));
+ //// Select the motor configuration to use.
+ label.setToolTipText(trans.get("simedtdlg.lbl.ttip.Flightcfg"));
+ topPanel.add(label, "growx 0, gapright para");
+
+ final Rocket rkt = document.getRocket();
+ final FlightConfiguration config = rkt.getFlightConfiguration(simulationList[0].getFlightConfigurationId());
+ final ConfigurationComboBox configComboBox = new ConfigurationComboBox(rkt, false);
+ configComboBox.setSelectedItem(config);
+
+ //// Select the motor configuration to use.
+ configComboBox.setToolTipText(trans.get("simedtdlg.combo.ttip.Flightcfg"));
+ configComboBox.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ FlightConfiguration config = (FlightConfiguration)configComboBox.getSelectedItem();
+ FlightConfigurationId id = config.getId();
+
+ simulationList[0].setFlightConfigurationId( id );
+ }
+ });
+ topPanel.add(configComboBox, "span");
+
+ topPanel.add(new JPanel(), "growx, wrap");
+
+ contentPanel.add(topPanel, "growx, wrap");
+ }
+
+ private void addBottomPanel(JPanel contentPanel) {
+ final JPanel bottomPanel = new JPanel(new MigLayout("fill, ins 0"));
+
+ //// Multi-simulation edit
+ if (isMultiCompEdit()) {
+ StyledLabel multiSimEditLabel = new StyledLabel("", -1, StyledLabel.Style.BOLD);
+ multiSimEditLabel.setFontColor(multiCompEditColor);
+ multiSimEditLabel.setText(trans.get("simedtdlg.title.MultiSimEdit"));
+ StringBuilder components = new StringBuilder(trans.get("simedtdlg.title.MultiSimEdit.ttip"));
+ for (int i = 0; i < simulationList.length; i++) {
+ if (i < simulationList.length - 1) {
+ components.append(simulationList[i].getName()).append(", ");
+ } else {
+ components.append(simulationList[i].getName());
+ }
+ }
+ multiSimEditLabel.setToolTipText(components.toString());
+ bottomPanel.add(multiSimEditLabel, "align left");
+ }
+
+ //// Run simulation button
+ // TODO: disable when sim is up to date?
+ /*JButton button = new SelectColorButton(trans.get("SimulationEditDialog.btn.simulateAndPlot"));
+ if (!isSingleEdit()) {
+ button.setText(trans.get("SimulationEditDialog.btn.simulate"));
+ }
+ button.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ copyChangesToAllSims();
+ SimulationRunDialog dialog = SimulationRunDialog.runSimulations(parentWindow, SimulationEditDialog.this.document, simulationList);
+ if (allowsPlotMode() && dialog.isAllSimulationsSuccessful()) {
+ refreshView();
+ setPlotMode();
+ }
+ }
+ });
+ simEditPanel.add(button, "align right, gapright 10lp, tag ok");*/
+
+ //// Cancel button
+ this.cancelButton = new SelectColorButton(trans.get("dlg.but.cancel"));
+ this.cancelButton.setToolTipText(trans.get("SimulationEditDialog.btn.Cancel.ttip"));
+ this.cancelButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (tabbedPane.getSelectedIndex() == SETTINGS_IDX) {
+ cancelSimEdit();
+ } else {
+ // Normal close action
+ closeDialog();
+ }
+
+ // TODO: include plot/export undo?
+ }
+ });
+ bottomPanel.add(this.cancelButton, "split 2, tag ok");
+
+ //// Ok button
+ this.okButton = new SelectColorButton(trans.get("dlg.but.ok"));
+ this.okButton.setToolTipText(trans.get("SimulationEditDialog.btn.OK.ttip"));
+ this.okButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ copyChangesToAllSims();
+
+ // Run outdated simulations
+ Simulation[] outdatedSims = getOutdatedSimulations();
+ if (outdatedSims.length > 0) {
+ new SimulationRunDialog(SimulationConfigDialog.this.parentWindow, document, outdatedSims).setVisible(true);
+ }
+
+ int tabIdx = tabbedPane.getSelectedIndex();
+ if (tabIdx == PLOT_IDX) {
+ JDialog plot = plotTab.doPlot(SimulationConfigDialog.this.parentWindow);
+ if (plot != null) {
+ plot.setVisible(true);
+ }
+ closeDialog();
+ return;
+ } else if (tabIdx == EXPORT_IDX) {
+ if (exportTab.doExport()) {
+ closeDialog();
+ }
+ return;
+ }
+
+ closeDialog();
+ }
+ });
+ bottomPanel.add(this.okButton, "tag ok");
+
+ contentPanel.add(bottomPanel, "growx, wrap");
+ }
+
+ private void copyChangesToAllSims() {
+ if (isMultiCompEdit()) {
+ for (int i = 1; i < simulationList.length; i++) {
+ simulationList[i].getOptions().copyConditionsFrom(simulationList[0].getOptions());
+ simulationList[i].getSimulationExtensions().clear();
+ for (SimulationExtension c : simulationList[0].getSimulationExtensions()) {
+ simulationList[i].getSimulationExtensions().add(c.clone());
+ }
+ }
+ }
+ }
+
+ private Simulation[] getOutdatedSimulations() {
+ List outdated = new ArrayList<>();
+ for (Simulation sim : simulationList) {
+ if (!Simulation.isStatusUpToDate(sim.getStatus())) {
+ outdated.add(sim);
+ }
+ }
+ return outdated.toArray(new Simulation[0]);
+ }
+
+ private boolean isMultiCompEdit() {
+ return simulationList.length > 1;
+ }
+
+ private void closeDialog() {
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ SimulationConfigDialog.this.removeWindowListener(applyChangesToSimsListener);
+ SimulationConfigDialog.this.dispose();
+ }
+ });
+ }
+
+ private JPanel createCancelOperationContent() {
+ JPanel panel = new JPanel(new MigLayout());
+ String msg = isNewSimulation ? trans.get("SimulationEditDialog.CancelOperation.msg.undoAdd") :
+ trans.get("SimulationEditDialog.CancelOperation.msg.discardChanges");
+ JLabel msgLabel = new JLabel(msg);
+ JCheckBox dontAskAgain = new JCheckBox(trans.get("SimulationEditDialog.CancelOperation.checkbox.dontAskAgain"));
+ dontAskAgain.setSelected(false);
+ dontAskAgain.addItemListener(new ItemListener() {
+ @Override
+ public void itemStateChanged(ItemEvent e) {
+ if (e.getStateChange() == ItemEvent.SELECTED) {
+ preferences.setShowDiscardSimulationConfirmation(false);
+ }
+ // Unselected state should be not be possible and thus not be handled
+ }
+ });
+
+ panel.add(msgLabel, "left, wrap");
+ panel.add(dontAskAgain, "left, gaptop para");
+
+ return panel;
+ }
+
+ private void cancelSimEdit() {
+ // Don't do anything on cancel if you are editing an existing simulation, and it is not modified
+ if (!isNewSimulation && !isModified) {
+ closeDialog();
+ return;
+ }
+
+ // Apply the cancel operation if set to auto discard in preferences
+ if (!preferences.isShowDiscardSimulationConfirmation()) {
+ discardChanges();
+ return;
+ }
+
+ // Yes/No dialog: Are you sure you want to discard your changes?
+ JPanel msg = createCancelOperationContent();
+ int resultYesNo = JOptionPane.showConfirmDialog(SimulationConfigDialog.this, msg,
+ trans.get("SimulationEditDialog.CancelOperation.title"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
+ if (resultYesNo == JOptionPane.YES_OPTION) {
+ discardChanges();
+ }
+ }
+
+ private void discardChanges() {
+ if (isNewSimulation) {
+ document.removeSimulation(simulationList[0]);
+ } else {
+ undoSimulationChanges();
+ }
+ document.setSaved(this.initialIsSaved); // Restore the saved state of the document
+ document.fireDocumentChangeEvent(new DocumentChangeEvent(this));
+
+ closeDialog();
+ }
+
+ private void undoSimulationChanges() {
+ if (simulationList == null || simulationList.length == 0) {
+ return;
+ }
+ simulationList[0].loadFrom(initialSim);
+ }
+}
diff --git a/swing/src/net/sf/openrocket/gui/simulation/SimulationEditDialog.java b/swing/src/net/sf/openrocket/gui/simulation/SimulationEditDialog.java
deleted file mode 100644
index 51fd38235..000000000
--- a/swing/src/net/sf/openrocket/gui/simulation/SimulationEditDialog.java
+++ /dev/null
@@ -1,476 +0,0 @@
-package net.sf.openrocket.gui.simulation;
-
-
-import java.awt.CardLayout;
-import java.awt.Color;
-import java.awt.Window;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.ItemEvent;
-import java.awt.event.ItemListener;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
-import java.awt.event.WindowListener;
-import java.util.EventObject;
-
-import javax.swing.JButton;
-import javax.swing.JCheckBox;
-import javax.swing.JDialog;
-import javax.swing.JLabel;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JTabbedPane;
-import javax.swing.JTextField;
-import javax.swing.event.ChangeEvent;
-import javax.swing.event.ChangeListener;
-import javax.swing.event.DocumentEvent;
-import javax.swing.event.DocumentListener;
-
-import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.document.OpenRocketDocument;
-import net.sf.openrocket.document.Simulation;
-import net.sf.openrocket.document.events.DocumentChangeEvent;
-import net.sf.openrocket.gui.components.ConfigurationComboBox;
-import net.sf.openrocket.gui.components.StyledLabel;
-import net.sf.openrocket.gui.util.GUIUtil;
-import net.sf.openrocket.gui.util.UITheme;
-import net.sf.openrocket.gui.widgets.SelectColorButton;
-import net.sf.openrocket.l10n.Translator;
-import net.sf.openrocket.rocketcomponent.FlightConfiguration;
-import net.sf.openrocket.rocketcomponent.FlightConfigurationId;
-import net.sf.openrocket.rocketcomponent.Rocket;
-import net.sf.openrocket.simulation.extension.SimulationExtension;
-import net.sf.openrocket.startup.Application;
-import net.sf.openrocket.startup.Preferences;
-import net.sf.openrocket.util.StateChangeListener;
-
-
-public class SimulationEditDialog extends JDialog {
- private static final long serialVersionUID = -4468157685542912715L;
- private final Window parentWindow;
- private final Simulation[] simulationList;
- private final OpenRocketDocument document;
- private static final Translator trans = Application.getTranslator();
- private static final Preferences preferences = Application.getPreferences();
-
- JPanel cards;
- private final static String EDITMODE = "EDIT";
- private final static String PLOTMODE = "PLOT";
-
- private final WindowListener applyChangesToSimsListener;
- private final Simulation initialSim; // A copy of the first selected simulation before it was modified
- private final boolean initialIsSaved; // Whether the document was saved before the dialog was opened
- private boolean isModified = false; // Whether the simulation has been modified
- private final boolean isNewSimulation; // Whether you are editing a new simulation, or an existing one
-
- private static Color multiCompEditColor;
-
- static {
- initColors();
- }
-
- public SimulationEditDialog(Window parent, final OpenRocketDocument document, boolean isNewSimulation, Simulation... sims) {
- //// Edit simulation
- super(parent, sims.length == 1 ? trans.get("simedtdlg.title.Editsim") : trans.get("simedtdlg.title.MultiSimEdit"),
- JDialog.ModalityType.DOCUMENT_MODAL);
- this.document = document;
- this.parentWindow = parent;
- this.simulationList = sims;
- this.initialSim = simulationList[0].clone();
- this.initialIsSaved = document.isSaved();
- this.isNewSimulation = isNewSimulation;
-
- simulationList[0].addChangeListener(new StateChangeListener() {
- @Override
- public void stateChanged(EventObject e) {
- isModified = true;
- setTitle("* " + getTitle()); // Add component changed indicator to the title
- simulationList[0].removeChangeListener(this);
- }
- });
-
- this.cards = new JPanel(new CardLayout());
- this.add(cards);
- buildEditCard();
- buildPlotCard();
-
- this.validate();
- this.pack();
-
- this.setLocationByPlatform(true);
-
- this.applyChangesToSimsListener = new WindowAdapter() {
- @Override
- public void windowClosed(WindowEvent e) {
- copyChangesToAllSims();
- }
- };
- this.addWindowListener(applyChangesToSimsListener);
-
- GUIUtil.setDisposableDialogOptions(this, null);
- }
-
- private static void initColors() {
- updateColors();
- UITheme.Theme.addUIThemeChangeListener(SimulationEditDialog::updateColors);
- }
-
- private static void updateColors() {
- multiCompEditColor = GUIUtil.getUITheme().getMultiCompEditColor();
- }
-
- private boolean isSingleEdit() {
- return simulationList.length == 1;
- }
-
- private boolean allowsPlotMode() {
- return simulationList.length == 1 && simulationList[0].hasSimulationData();
- }
-
- public void setEditMode() {
- String baseTitle = simulationList.length == 1 ?
- trans.get("simedtdlg.title.Editsim") : trans.get("simedtdlg.title.MultiSimEdit");
- setTitle((isModified ? "* " : "") + baseTitle);
- CardLayout cl = (CardLayout) (cards.getLayout());
- cl.show(cards, EDITMODE);
- cards.validate();
- this.addWindowListener(applyChangesToSimsListener);
- }
-
- public void setPlotMode() {
- if (!allowsPlotMode()) {
- return;
- }
- this.removeWindowListener(applyChangesToSimsListener);
- setTitle((isModified ? "* " : "") + trans.get("simplotpanel.title.Plotsim"));
- CardLayout cl = (CardLayout) (cards.getLayout());
- cl.show(cards, PLOTMODE);
- cards.validate();
- }
-
- private void copyChangesToAllSims() {
- if (simulationList.length > 1) {
- for (int i = 1; i < simulationList.length; i++) {
- simulationList[i].getOptions().copyConditionsFrom(simulationList[0].getOptions());
- simulationList[i].getSimulationExtensions().clear();
- for (SimulationExtension c : simulationList[0].getSimulationExtensions()) {
- simulationList[i].getSimulationExtensions().add(c.clone());
- }
- }
- }
- }
-
- private void refreshView() {
- cards.removeAll();
- buildEditCard();
- buildPlotCard();
- this.validate();
- }
-
- private void buildEditCard() {
- JPanel simEditPanel = new JPanel(new MigLayout("fill, hidemode 1"));
-
- if (isSingleEdit()) {
- JPanel panel = new JPanel(new MigLayout("fill, ins 0"));
-
- //// Simulation name:
- panel.add(new JLabel(trans.get("simedtdlg.lbl.Simname") + " "), "growx 0, gapright para");
- final JTextField field = new JTextField(simulationList[0].getName());
- field.getDocument().addDocumentListener(new DocumentListener() {
- @Override
- public void changedUpdate(DocumentEvent e) {
- setText();
- }
-
- @Override
- public void insertUpdate(DocumentEvent e) {
- setText();
- }
-
- @Override
- public void removeUpdate(DocumentEvent e) {
- setText();
- }
-
- private void setText() {
- String name = field.getText();
- if (name == null || name.equals(""))
- return;
- simulationList[0].setName(name);
-
- }
- });
- panel.add(field, "growx, wrap");
-
- //// Flight selector
- //// Flight configuration:
- JLabel label = new JLabel(trans.get("simedtdlg.lbl.Flightcfg"));
- //// Select the motor configuration to use.
- label.setToolTipText(trans.get("simedtdlg.lbl.ttip.Flightcfg"));
- panel.add(label, "growx 0, gapright para");
-
- final Rocket rkt = document.getRocket();
- final FlightConfiguration config = rkt.getFlightConfiguration(simulationList[0].getFlightConfigurationId());
- final ConfigurationComboBox configComboBox = new ConfigurationComboBox(rkt, false);
- configComboBox.setSelectedItem(config);
-
- //// Select the motor configuration to use.
- configComboBox.setToolTipText(trans.get("simedtdlg.combo.ttip.Flightcfg"));
- configComboBox.addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- FlightConfiguration config = (FlightConfiguration)configComboBox.getSelectedItem();
- FlightConfigurationId id = config.getId();
-
- simulationList[0].setFlightConfigurationId( id );
- }
- });
- panel.add(configComboBox, "span");
-
- panel.add(new JPanel(), "growx, wrap");
-
- simEditPanel.add(panel, "growx, wrap");
- }
- JTabbedPane tabbedPane = new JTabbedPane();
-
- //// Launch conditions
- tabbedPane.addTab(trans.get("simedtdlg.tab.Launchcond"), new SimulationConditionsPanel(simulationList[0]));
- //// Simulation options
- tabbedPane.addTab(trans.get("simedtdlg.tab.Simopt"), new SimulationOptionsPanel(document, simulationList[0]));
-
- tabbedPane.setSelectedIndex(0);
-
- simEditPanel.add(tabbedPane, "spanx, grow, wrap");
-
-
- //// Open Plot button
- JButton button = new SelectColorButton(trans.get("SimulationEditDialog.btn.plot") + " >>");
- button.addActionListener(new ActionListener() {
-
- @Override
- public void actionPerformed(ActionEvent e) {
- SimulationEditDialog.this.setPlotMode();
- }
-
- });
- simEditPanel.add(button, "spanx, split 5, align left");
- if (allowsPlotMode()) {
- button.setVisible(true);
- } else {
- button.setVisible(false);
- }
-
- //// Multi-simulation edit
- if (simulationList.length > 1) {
- StyledLabel multiSimEditLabel = new StyledLabel("", -1, StyledLabel.Style.BOLD);
- multiSimEditLabel.setFontColor(multiCompEditColor);
- multiSimEditLabel.setText(trans.get("simedtdlg.title.MultiSimEdit"));
- StringBuilder components = new StringBuilder(trans.get("simedtdlg.title.MultiSimEdit.ttip"));
- for (int i = 0; i < simulationList.length; i++) {
- if (i < simulationList.length - 1) {
- components.append(simulationList[i].getName()).append(", ");
- } else {
- components.append(simulationList[i].getName());
- }
- }
- multiSimEditLabel.setToolTipText(components.toString());
- simEditPanel.add(multiSimEditLabel, "align left");
- }
-
- //// Run simulation button
- button = new SelectColorButton(trans.get("SimulationEditDialog.btn.simulateAndPlot"));
- if (!isSingleEdit()) {
- button.setText(trans.get("SimulationEditDialog.btn.simulate"));
- }
- button.addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- copyChangesToAllSims();
- SimulationRunDialog dialog = SimulationRunDialog.runSimulations(parentWindow, SimulationEditDialog.this.document, simulationList);
- if (allowsPlotMode() && dialog.isAllSimulationsSuccessful()) {
- refreshView();
- setPlotMode();
- }
- }
- });
- simEditPanel.add(button, "align right, gapright 10lp, tag ok");
-
- //// Cancel button
- JButton cancelButton = new SelectColorButton(trans.get("dlg.but.cancel"));
- cancelButton.setToolTipText(trans.get("SimulationEditDialog.btn.Cancel.ttip"));
- cancelButton.addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- // Don't do anything on cancel if you are editing an existing simulation, and it is not modified
- if (!isNewSimulation && !isModified) {
- SimulationEditDialog.this.removeWindowListener(applyChangesToSimsListener);
- SimulationEditDialog.this.dispose();
- return;
- }
-
- // Apply the cancel operation if set to auto discard in preferences
- if (!preferences.isShowDiscardSimulationConfirmation()) {
- discardChanges();
- return;
- }
-
- // Yes/No dialog: Are you sure you want to discard your changes?
- JPanel msg = createCancelOperationContent();
- int resultYesNo = JOptionPane.showConfirmDialog(SimulationEditDialog.this, msg,
- trans.get("SimulationEditDialog.CancelOperation.title"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
- if (resultYesNo == JOptionPane.YES_OPTION) {
- discardChanges();
- }
- }
- });
- simEditPanel.add(cancelButton, "tag ok");
-
- //// Ok button
- JButton okButton = new SelectColorButton(trans.get("dlg.but.ok"));
- okButton.setToolTipText(trans.get("SimulationEditDialog.btn.OK.ttip"));
- okButton.addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- copyChangesToAllSims();
- SimulationEditDialog.this.dispose();
- }
- });
- simEditPanel.add(okButton, "tag ok");
-
- cards.add(simEditPanel, EDITMODE);
- }
-
- private void buildPlotCard() {
- if (allowsPlotMode()) {
- JPanel plotExportPanel = new JPanel(new MigLayout("fill"));
-
- //// Simulation name:
- plotExportPanel.add(new JLabel(trans.get("simedtdlg.lbl.Simname") + " "), "span, split 2, shrink");
- final JTextField field = new JTextField(simulationList[0].getName());
- field.setEditable(false);
- plotExportPanel.add(field, "shrinky, growx, wrap");
-
- final JTabbedPane tabbedPane = new JTabbedPane();
-
- //// Plot data
- final SimulationPlotPanel plotTab = new SimulationPlotPanel(simulationList[0]);
- tabbedPane.addTab(trans.get("simedtdlg.tab.Plotdata"), plotTab);
- //// Export data
- final SimulationExportPanel exportTab = new SimulationExportPanel(simulationList[0]);
- tabbedPane.addTab(trans.get("simedtdlg.tab.Exportdata"), exportTab);
-
- plotExportPanel.add(tabbedPane, "grow, wrap");
-
- JButton button = new SelectColorButton("<< " + trans.get("SimulationEditDialog.btn.edit"));
- button.addActionListener(new ActionListener() {
-
- @Override
- public void actionPerformed(ActionEvent e) {
- SimulationEditDialog.this.setEditMode();
- }
-
- });
-
- plotExportPanel.add(button, "spanx, split 3, align left");
-
- final JButton ok = new SelectColorButton(trans.get("SimulationEditDialog.btn.plot"));
-
- tabbedPane.addChangeListener(new ChangeListener() {
-
- @Override
- public void stateChanged(ChangeEvent e) {
- int selectedIndex = tabbedPane.getSelectedIndex();
- switch (selectedIndex) {
- case 0:
- ok.setText(trans.get("SimulationEditDialog.btn.plot"));
- plotExportPanel.revalidate();
- break;
- case 1:
- ok.setText(trans.get("SimulationEditDialog.btn.export"));
- plotExportPanel.revalidate();
- break;
- }
- }
-
- });
-
- ok.addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- // If the simulation is out of date, run the simulation.
- if (!Simulation.isStatusUpToDate(simulationList[0].getStatus())) {
- new SimulationRunDialog(SimulationEditDialog.this.parentWindow, document, simulationList[0]).setVisible(true);
- }
-
- if (tabbedPane.getSelectedIndex() == 0) {
- JDialog plot = plotTab.doPlot(SimulationEditDialog.this.parentWindow);
- if (plot != null) {
- plot.setVisible(true);
- }
- } else {
- if (exportTab.doExport()) {
- SimulationEditDialog.this.dispose();
- }
- }
- }
- });
- plotExportPanel.add(ok, "tag ok, split 2");
-
- //// Close button
- JButton close = new SelectColorButton(trans.get("dlg.but.close"));
- close.addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- SimulationEditDialog.this.dispose();
- }
- });
- plotExportPanel.add(close, "tag cancel");
- //plotExportPanel.validate();
- cards.add(plotExportPanel, PLOTMODE);
-
- }
- }
-
- private JPanel createCancelOperationContent() {
- JPanel panel = new JPanel(new MigLayout());
- String msg = isNewSimulation ? trans.get("SimulationEditDialog.CancelOperation.msg.undoAdd") :
- trans.get("SimulationEditDialog.CancelOperation.msg.discardChanges");
- JLabel msgLabel = new JLabel(msg);
- JCheckBox dontAskAgain = new JCheckBox(trans.get("SimulationEditDialog.CancelOperation.checkbox.dontAskAgain"));
- dontAskAgain.setSelected(false);
- dontAskAgain.addItemListener(new ItemListener() {
- @Override
- public void itemStateChanged(ItemEvent e) {
- if (e.getStateChange() == ItemEvent.SELECTED) {
- preferences.setShowDiscardSimulationConfirmation(false);
- }
- // Unselected state should be not be possible and thus not be handled
- }
- });
-
- panel.add(msgLabel, "left, wrap");
- panel.add(dontAskAgain, "left, gaptop para");
-
- return panel;
- }
-
- private void discardChanges() {
- if (isNewSimulation) {
- document.removeSimulation(simulationList[0]);
- } else {
- undoSimulationChanges();
- }
- document.setSaved(this.initialIsSaved); // Restore the saved state of the document
- document.fireDocumentChangeEvent(new DocumentChangeEvent(this));
-
- SimulationEditDialog.this.removeWindowListener(applyChangesToSimsListener);
- SimulationEditDialog.this.dispose();
- }
-
- private void undoSimulationChanges() {
- if (simulationList == null || simulationList.length == 0) {
- return;
- }
- simulationList[0].loadFrom(initialSim);
- }
-}
diff --git a/swing/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java b/swing/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java
index baba99187..5e4db1160 100644
--- a/swing/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java
+++ b/swing/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java
@@ -5,6 +5,7 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
+import java.io.Serial;
import java.util.Arrays;
import java.util.EnumSet;
@@ -51,6 +52,7 @@ import net.sf.openrocket.gui.widgets.SelectColorButton;
* @author Sampo Niskanen
*/
public class SimulationPlotPanel extends JPanel {
+ @Serial
private static final long serialVersionUID = -2227129713185477998L;
private static final Translator trans = Application.getTranslator();
diff --git a/swing/src/net/sf/openrocket/gui/simulation/SimulationSettingsPanel.java b/swing/src/net/sf/openrocket/gui/simulation/SimulationSettingsPanel.java
new file mode 100644
index 000000000..466d4a3e6
--- /dev/null
+++ b/swing/src/net/sf/openrocket/gui/simulation/SimulationSettingsPanel.java
@@ -0,0 +1,36 @@
+package net.sf.openrocket.gui.simulation;
+
+import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.document.OpenRocketDocument;
+import net.sf.openrocket.document.Simulation;
+import net.sf.openrocket.l10n.Translator;
+import net.sf.openrocket.startup.Application;
+
+import javax.swing.JPanel;
+import javax.swing.JTabbedPane;
+import java.io.Serial;
+
+public class SimulationSettingsPanel extends JPanel {
+ @Serial
+ private static final long serialVersionUID = -2114129713185477998L;
+
+ private static final Translator trans = Application.getTranslator();
+
+ public SimulationSettingsPanel(final OpenRocketDocument document, final Simulation simulation) {
+ super(new MigLayout("fill"));
+
+ JTabbedPane tabbedPane = new JTabbedPane();
+
+ //// Launch conditions
+ tabbedPane.addTab(trans.get("simedtdlg.tab.Launchcond"), new SimulationConditionsPanel(simulation));
+
+ //// Simulation options
+ tabbedPane.addTab(trans.get("simedtdlg.tab.Simopt"), new SimulationOptionsPanel(document, simulation));
+
+ tabbedPane.setSelectedIndex(0);
+
+ this.add(tabbedPane, "spanx, grow, wrap");
+ }
+
+
+}