From f6b932c4a7947d16a7a555549c2bd9a3f91d24fb Mon Sep 17 00:00:00 2001 From: SiboVG Date: Thu, 30 Mar 2023 00:47:40 +0200 Subject: [PATCH] [#2158] Add ok/cancel button for simulation editing --- core/resources/l10n/messages.properties | 8 ++ .../sf/openrocket/startup/Preferences.java | 17 +++ .../preferences/DesignPreferencesPanel.java | 13 ++ .../openrocket/gui/main/SimulationPanel.java | 10 +- .../gui/simulation/SimulationEditDialog.java | 118 ++++++++++++++++-- 5 files changed, 151 insertions(+), 15 deletions(-) diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 88290ee6d..435a71a49 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -318,6 +318,8 @@ pref.dlg.checkbox.AlwaysOpenLeftmost = Always open leftmost tab when opening a c pref.dlg.checkbox.AlwaysOpenLeftmost.ttip = If checked, a component edit dialog will always pop up with the first tab selected.
If unchecked, the previous selected tab will be used. pref.dlg.checkbox.ShowDiscardConfirmation = Show confirmation dialog for discarding component changes pref.dlg.checkbox.ShowDiscardConfirmation.ttip = If checked, you will be asked if you want really want to discard component configuration configuration changes. +pref.dlg.checkbox.ShowDiscardSimulationConfirmation = Show confirmation dialog for discarding simulation changes +pref.dlg.checkbox.ShowDiscardSimulationConfirmation.ttip = If checked, you will be asked if you want really want to discard simulation configuration configuration changes. pref.dlg.lbl.User-definedthrust = User-defined thrust curves: pref.dlg.lbl.Windspeed = Wind speed pref.dlg.Allthrustcurvefiles = All thrust curve files (*.eng; *.rse; *.zip; directories) @@ -516,6 +518,12 @@ SimulationEditDialog.btn.export = Export SimulationEditDialog.btn.edit = Edit SimulationEditDialog.btn.simulate = Simulate SimulationEditDialog.btn.simulateAndPlot = Simulate & Plot +SimulationEditDialog.btn.OK.ttip = Keep changes and close the dialog +SimulationEditDialog.btn.Cancel.ttip = Discard changes and close the dialog +SimulationEditDialog.CancelOperation.msg.discardChanges = Are you sure you want to discard your changes to this simulation? +SimulationEditDialog.CancelOperation.msg.undoAdd = Are you sure you want to undo adding this simulation? +SimulationEditDialog.CancelOperation.title = Cancel operation +SimulationEditDialog.CancelOperation.checkbox.dontAskAgain = Don't ask me again GeodeticComputationStrategy.flat.name = Flat Earth GeodeticComputationStrategy.flat.desc = Perform computations with a flat Earth approximation. Sufficient for low-altitude flights. diff --git a/core/src/net/sf/openrocket/startup/Preferences.java b/core/src/net/sf/openrocket/startup/Preferences.java index eac3b3642..8b7d01d13 100644 --- a/core/src/net/sf/openrocket/startup/Preferences.java +++ b/core/src/net/sf/openrocket/startup/Preferences.java @@ -77,6 +77,7 @@ public abstract class Preferences implements ChangeSource { private static final String AUTO_OPEN_LAST_DESIGN = "AUTO_OPEN_LAST_DESIGN"; private static final String OPEN_LEFTMOST_DESIGN_TAB = "OPEN_LEFTMOST_DESIGN_TAB"; private static final String SHOW_DISCARD_CONFIRMATION = "IgnoreDiscardEditingWarning"; + private static final String SHOW_DISCARD_SIMULATION_CONFIRMATION = "IgnoreDiscardSimulationEditingWarning"; public static final String MARKER_STYLE_ICON = "MARKER_STYLE_ICON"; private static final String SHOW_MARKERS = "SHOW_MARKERS"; private static final String SHOW_ROCKSIM_FORMAT_WARNING = "SHOW_ROCKSIM_FORMAT_WARNING"; @@ -537,6 +538,22 @@ public abstract class Preferences implements ChangeSource { this.putBoolean(SHOW_DISCARD_CONFIRMATION, enabled); } + /** + * Answer if a confirmation dialog should be shown when canceling a simulation config operation. + * + * @return true if the confirmation dialog should be shown. + */ + public final boolean isShowDiscardSimulationConfirmation() { + return this.getBoolean(SHOW_DISCARD_SIMULATION_CONFIRMATION, true); + } + + /** + * Enable/Disable showing a confirmation warning when canceling a simulation config operation. + */ + public final void setShowDiscardSimulationConfirmation(boolean enabled) { + this.putBoolean(SHOW_DISCARD_SIMULATION_CONFIRMATION, enabled); + } + /** * Answer if the always open leftmost tab is enabled. * diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preferences/DesignPreferencesPanel.java b/swing/src/net/sf/openrocket/gui/dialogs/preferences/DesignPreferencesPanel.java index c91962d7c..74dcf1580 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/preferences/DesignPreferencesPanel.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/preferences/DesignPreferencesPanel.java @@ -115,6 +115,19 @@ public class DesignPreferencesPanel extends PreferencesPanel { }); this.add(showDiscardConfirmation, "wrap, growx, spanx"); + // // Show confirmation dialog for discarding simulation configuration changes + final JCheckBox showDiscardSimulationConfirmation = new JCheckBox( + trans.get("pref.dlg.checkbox.ShowDiscardSimulationConfirmation")); + showDiscardSimulationConfirmation.setSelected(preferences.isShowDiscardSimulationConfirmation()); + showDiscardSimulationConfirmation.setToolTipText(trans.get("pref.dlg.checkbox.ShowDiscardSimulationConfirmation.ttip")); + showDiscardSimulationConfirmation.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + preferences.setShowDiscardSimulationConfirmation(e.getStateChange() == ItemEvent.SELECTED); + } + }); + this.add(showDiscardSimulationConfirmation, "wrap, growx, spanx"); + // // Update flight estimates in the design window final JCheckBox updateEstimates = new JCheckBox( trans.get("pref.dlg.checkbox.Updateestimates")); diff --git a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java index b1a31377b..a22987392 100644 --- a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java @@ -297,7 +297,7 @@ public class SimulationPanel extends JPanel { simulationTable.addRowSelectionInterval(n, n); updatePreviousSelection(); - openDialog(false, sim); + openDialog(false, true, sim); } private void plotSimulation() { @@ -595,8 +595,8 @@ public class SimulationPanel extends JPanel { return simulationTable.getSelectionModel(); } - private void openDialog(boolean plotMode, final Simulation... sims) { - SimulationEditDialog d = new SimulationEditDialog(SwingUtilities.getWindowAncestor(this), document, sims); + private void openDialog(boolean plotMode, boolean isNewSimulation, final Simulation... sims) { + SimulationEditDialog d = new SimulationEditDialog(SwingUtilities.getWindowAncestor(this), document, isNewSimulation, sims); if (plotMode) { d.setPlotMode(); } @@ -605,6 +605,10 @@ public class SimulationPanel extends JPanel { takeTheSpotlight(); } + private void openDialog(boolean plotMode, final Simulation... sims) { + openDialog(plotMode, false, sims); + } + private void openDialog(final Simulation sim) { boolean plotMode = false; if (sim.hasSimulationData() && Simulation.isStatusUpToDate(sim.getStatus())) { diff --git a/swing/src/net/sf/openrocket/gui/simulation/SimulationEditDialog.java b/swing/src/net/sf/openrocket/gui/simulation/SimulationEditDialog.java index 32effec96..9f54e68de 100644 --- a/swing/src/net/sf/openrocket/gui/simulation/SimulationEditDialog.java +++ b/swing/src/net/sf/openrocket/gui/simulation/SimulationEditDialog.java @@ -5,14 +5,18 @@ import java.awt.CardLayout; 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.JComboBox; +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; @@ -33,6 +37,8 @@ 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 { @@ -41,19 +47,34 @@ public class SimulationEditDialog extends JDialog { 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 WindowListener applyChangesToSimsListener; + private final WindowListener applyChangesToSimsListener; + private final Simulation initialSim; + private boolean isModified = false; + private final boolean isNewSimulation; - public SimulationEditDialog(Window parent, final OpenRocketDocument document, Simulation... sims) { + public SimulationEditDialog(Window parent, final OpenRocketDocument document, boolean isNewSimulation, Simulation... sims) { //// Edit simulation super(parent, trans.get("simedtdlg.title.Editsim"), JDialog.ModalityType.DOCUMENT_MODAL); this.document = document; this.parentWindow = parent; this.simulationList = sims; + this.initialSim = simulationList[0].clone(); + 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); @@ -85,6 +106,7 @@ public class SimulationEditDialog extends JDialog { } public void setEditMode() { + setTitle((isModified ? "* " : "") + trans.get("simedtdlg.title.Editsim")); CardLayout cl = (CardLayout) (cards.getLayout()); cl.show(cards, EDITMODE); cards.validate(); @@ -96,7 +118,7 @@ public class SimulationEditDialog extends JDialog { return; } this.removeWindowListener(applyChangesToSimsListener); - setTitle(trans.get("simplotpanel.title.Plotsim")); + setTitle((isModified ? "* " : "") + trans.get("simplotpanel.title.Plotsim")); CardLayout cl = (CardLayout) (cards.getLayout()); cl.show(cards, PLOTMODE); cards.validate(); @@ -150,7 +172,6 @@ public class SimulationEditDialog extends JDialog { String name = field.getText(); if (name == null || name.equals("")) return; - //System.out.println("Setting name:" + name); simulationList[0].setName(name); } @@ -208,7 +229,7 @@ public class SimulationEditDialog extends JDialog { } }); - simEditPanel.add(button, "spanx, split 3, align left"); + simEditPanel.add(button, "spanx, split 4, align left"); if (allowsPlotMode()) { button.setVisible(true); } else { @@ -231,17 +252,49 @@ public class SimulationEditDialog extends JDialog { } } }); - simEditPanel.add(button, " align right, tag ok"); - - //// Close button - JButton close = new SelectColorButton(trans.get("dlg.but.close")); - close.addActionListener(new ActionListener() { + 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(close, "tag ok"); + simEditPanel.add(okButton, "tag ok"); cards.add(simEditPanel, EDITMODE); } @@ -336,4 +389,45 @@ public class SimulationEditDialog extends JDialog { } } + + 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(); + } + + SimulationEditDialog.this.removeWindowListener(applyChangesToSimsListener); + SimulationEditDialog.this.dispose(); + } + + private void undoSimulationChanges() { + if (simulationList == null || simulationList.length == 0) { + return; + } + simulationList[0].loadFrom(initialSim); + } }