diff --git a/swing/src/main/java/info/openrocket/swing/gui/plot/PlotDialog.java b/swing/src/main/java/info/openrocket/swing/gui/plot/PlotDialog.java new file mode 100644 index 000000000..ff16749e3 --- /dev/null +++ b/swing/src/main/java/info/openrocket/swing/gui/plot/PlotDialog.java @@ -0,0 +1,188 @@ +package info.openrocket.swing.gui.plot; + +import info.openrocket.core.l10n.Translator; +import info.openrocket.core.preferences.ApplicationPreferences; +import info.openrocket.core.simulation.DataBranch; +import info.openrocket.core.simulation.DataType; +import info.openrocket.core.startup.Application; +import info.openrocket.swing.gui.components.StyledLabel; +import info.openrocket.swing.gui.util.FileHelper; +import info.openrocket.swing.gui.util.GUIUtil; +import info.openrocket.swing.gui.util.Icons; +import info.openrocket.swing.gui.util.SwingPreferences; +import info.openrocket.swing.gui.widgets.SaveFileChooser; +import net.miginfocom.swing.MigLayout; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.ChartUtils; +import org.jfree.chart.JFreeChart; + +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JDialog; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JPanel; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.io.File; +import java.util.List; + +public abstract class PlotDialog, C extends PlotConfiguration, + P extends Plot> extends JDialog { + protected static final Translator trans = Application.getTranslator(); + + public PlotDialog(Window parent, String name, P plot, C config, List allBranches, boolean initialShowPoints) { + super(parent, name); + this.setModalityType(ModalityType.DOCUMENT_MODAL); + + // Create the dialog + JPanel panel = new JPanel(new MigLayout("fill, hidemode 3","[]","[grow][]")); + this.add(panel); + + final ChartPanel chartPanel = new SimulationChart(plot.getJFreeChart()); + final JFreeChart jChart = plot.getJFreeChart(); + panel.add(chartPanel, "grow, wrap 20lp"); + + // Ensures normal aspect-ratio of chart elements when resizing the panel + chartPanel.setMinimumDrawWidth(0); + chartPanel.setMaximumDrawWidth(Integer.MAX_VALUE); + chartPanel.setMinimumDrawHeight(0); + chartPanel.setMaximumDrawHeight(Integer.MAX_VALUE); + + //// Description text + JLabel label = new StyledLabel(trans.get("PlotDialog.lbl.Chart"), -2); + panel.add(label, "wrap"); + + // Add X axis warnings + addXAxisWarningMessage(panel, config); + + //// Show data points + final JCheckBox checkData = new JCheckBox(trans.get("PlotDialog.CheckBox.Showdatapoints")); + checkData.setSelected(initialShowPoints); + checkData.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + boolean show = checkData.isSelected(); + Application.getPreferences().putBoolean(ApplicationPreferences.PLOT_SHOW_POINTS, show); + plot.setShowPoints(show); + } + }); + panel.add(checkData, "split, left"); + + // Show errors checkbox + showErrorsCheckbox(panel, plot); + + // Add series selection box + addSeriesSelectionBox(panel, plot, allBranches); + + //// Zoom in button + JButton button = new JButton(Icons.ZOOM_IN); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if ((e.getModifiers() & InputEvent.ALT_MASK) == InputEvent.ALT_MASK) { + chartPanel.actionPerformed(new ActionEvent(chartPanel, ActionEvent.ACTION_FIRST, ChartPanel.ZOOM_IN_DOMAIN_COMMAND)); + } else { + chartPanel.actionPerformed(new ActionEvent(chartPanel, ActionEvent.ACTION_FIRST, ChartPanel.ZOOM_IN_BOTH_COMMAND)); + + } + } + }); + panel.add(button, "gapleft rel"); + + //// Reset Zoom button. + button = new JButton(Icons.ZOOM_RESET); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + chartPanel.actionPerformed(new ActionEvent(chartPanel, ActionEvent.ACTION_FIRST, ChartPanel.ZOOM_RESET_BOTH_COMMAND)); + } + }); + panel.add(button, "gapleft rel"); + + + //// Zoom out button + button = new JButton(Icons.ZOOM_OUT); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if ((e.getModifiers() & InputEvent.ALT_MASK) == InputEvent.ALT_MASK) { + chartPanel.actionPerformed(new ActionEvent(chartPanel, ActionEvent.ACTION_FIRST, ChartPanel.ZOOM_OUT_DOMAIN_COMMAND)); + } else { + chartPanel.actionPerformed(new ActionEvent(chartPanel, ActionEvent.ACTION_FIRST, ChartPanel.ZOOM_OUT_BOTH_COMMAND)); + } + } + }); + panel.add(button, "gapleft rel"); + + //// Print chart button + button = new JButton(trans.get("PlotDialog.btn.exportImage")); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + doPNGExport(chartPanel,jChart); + } + }); + panel.add(button, "gapleft rel"); + + //// Close button + button = new JButton(trans.get("dlg.but.close")); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + PlotDialog.this.dispose(); + } + }); + panel.add(button, "gapbefore push, right"); + this.setLocationByPlatform(true); + this.pack(); + + GUIUtil.setDisposableDialogOptions(this, button); + GUIUtil.rememberWindowSize(this); + this.setLocationByPlatform(true); + GUIUtil.rememberWindowPosition(this); + } + + protected void addXAxisWarningMessage(JPanel panel, C config) { + // Default implementation does nothing + } + + protected void showErrorsCheckbox(JPanel panel, P plot) { + // Default implementation does nothing + } + + protected void addSeriesSelectionBox(JPanel panel, P plot, List allBranches) { + // Default implementation does nothing + } + + private boolean doPNGExport(ChartPanel chartPanel, JFreeChart chart){ + JFileChooser chooser = new SaveFileChooser(); + chooser.setAcceptAllFileFilterUsed(false); + chooser.setFileFilter(FileHelper.PNG_FILTER); + chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory()); + + //// Ensures No Problems When Choosing File + if (chooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION) + return false; + + File file = chooser.getSelectedFile(); + if (file == null) + return false; + + file = FileHelper.forceExtension(file, "png"); + if (!FileHelper.confirmWrite(file, this)) { + return false; + } + + //// Uses JFreeChart Built In PNG Export Method + try{ + ChartUtils.saveChartAsPNG(file, chart, chartPanel.getWidth(), chartPanel.getHeight()); + } catch(Exception e){ + return false; + } + + return true; + } +} diff --git a/swing/src/main/java/info/openrocket/swing/gui/plot/SimulationPlotDialog.java b/swing/src/main/java/info/openrocket/swing/gui/plot/SimulationPlotDialog.java index 7062ac4a4..cc16e2a21 100644 --- a/swing/src/main/java/info/openrocket/swing/gui/plot/SimulationPlotDialog.java +++ b/swing/src/main/java/info/openrocket/swing/gui/plot/SimulationPlotDialog.java @@ -4,126 +4,108 @@ import java.awt.Color; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.InputEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; -import java.io.File; import java.util.ArrayList; +import java.util.List; -import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; -import javax.swing.JDialog; -import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JPanel; import info.openrocket.core.document.Simulation; import info.openrocket.core.l10n.Translator; import info.openrocket.core.preferences.ApplicationPreferences; +import info.openrocket.core.simulation.FlightDataBranch; import info.openrocket.core.simulation.FlightDataType; import info.openrocket.core.startup.Application; -import net.miginfocom.swing.MigLayout; import info.openrocket.swing.gui.components.StyledLabel; -import info.openrocket.swing.gui.util.FileHelper; import info.openrocket.swing.gui.util.GUIUtil; -import info.openrocket.swing.gui.util.Icons; -import info.openrocket.swing.gui.util.SwingPreferences; import info.openrocket.swing.gui.theme.UITheme; -import info.openrocket.swing.gui.widgets.SaveFileChooser; - -import org.jfree.chart.ChartPanel; -import org.jfree.chart.ChartUtils; -import org.jfree.chart.JFreeChart; /** * Dialog that shows a plot of a simulation results based on user options. * * @author Sampo Niskanen */ -public class SimulationPlotDialog extends JDialog { +public class SimulationPlotDialog extends PlotDialog { private static final Translator trans = Application.getTranslator(); + private final Simulation simulation; + private static Color darkErrorColor; - private final JCheckBox checkErrors; + private JCheckBox checkErrors; static { initColors(); } - private SimulationPlotDialog(Window parent, Simulation simulation, SimulationPlotConfiguration config) { - //// Flight data plot - super(parent, simulation.getName()); - this.setModalityType(ModalityType.DOCUMENT_MODAL); - + private SimulationPlotDialog(Window parent, Simulation simulation, SimulationPlotConfiguration config, + SimulationPlot plot, boolean initialShowPoints) { + super(parent, simulation.getName(), plot, config, simulation.getSimulatedData().getBranches(), initialShowPoints); + this.simulation = simulation; + this.checkErrors.setVisible(this.simulation.hasErrors()); + } + + /** + * Static method that shows a plot with the specified parameters. + * + * @param parent the parent window, which will be blocked. + * @param simulation the simulation to plot. + * @param config the configuration of the plot. + */ + public static SimulationPlotDialog getPlot(Window parent, Simulation simulation, SimulationPlotConfiguration config) { final boolean initialShowPoints = Application.getPreferences().getBoolean(ApplicationPreferences.PLOT_SHOW_POINTS, false); - final SimulationPlot myPlot = SimulationPlot.create(simulation, config, initialShowPoints); - - // Create the dialog - JPanel panel = new JPanel(new MigLayout("fill, hidemode 3","[]","[grow][]")); - this.add(panel); - - final ChartPanel chartPanel = new SimulationChart(myPlot.getJFreeChart()); - final JFreeChart jChart = myPlot.getJFreeChart(); - panel.add(chartPanel, "grow, wrap 20lp"); + final SimulationPlot plot = SimulationPlot.create(simulation, config, initialShowPoints); - // Ensures normal aspect-ratio of chart elements when resizing the panel - chartPanel.setMinimumDrawWidth(0); - chartPanel.setMaximumDrawWidth(Integer.MAX_VALUE); - chartPanel.setMinimumDrawHeight(0); - chartPanel.setMaximumDrawHeight(Integer.MAX_VALUE); - - //// Description text - JLabel label = new StyledLabel(trans.get("PlotDialog.lbl.Chart"), -2); - panel.add(label, "wrap"); + return new SimulationPlotDialog(parent, simulation, config, plot, initialShowPoints); + } + @Override + protected void addXAxisWarningMessage(JPanel panel, SimulationPlotConfiguration config) { // Add warning if X axis type is not time if (config.getDomainAxisType() != FlightDataType.TYPE_TIME) { JLabel msg = new StyledLabel(trans.get("PlotDialog.lbl.timeSeriesWarning"), -2); msg.setForeground(darkErrorColor); panel.add(msg, "wrap"); } - - //// Show data points - final JCheckBox checkData = new JCheckBox(trans.get("PlotDialog.CheckBox.Showdatapoints")); - checkData.setSelected(initialShowPoints); - checkData.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - boolean show = checkData.isSelected(); - Application.getPreferences().putBoolean(ApplicationPreferences.PLOT_SHOW_POINTS, show); - myPlot.setShowPoints(show); - } - }); - panel.add(checkData, "split, left"); + } + @Override + protected void showErrorsCheckbox(JPanel panel, SimulationPlot plot) { //// Show errors if any //// Always enable 'show errors' initially; make user turn it off for themselves - checkErrors = new JCheckBox(trans.get("PlotDialog.CheckBox.ShowErrors")); - checkErrors.setSelected(true); - checkErrors.addActionListener(new ActionListener() { + this.checkErrors = new JCheckBox(trans.get("PlotDialog.CheckBox.ShowErrors")); + this.checkErrors.setSelected(true); + this.checkErrors.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - myPlot.setShowErrors(checkErrors.isSelected()); + plot.setShowErrors(checkErrors.isSelected()); } }); - panel.add(checkErrors, "split, left"); - checkErrors.setVisible(simulation.hasErrors()); + panel.add(this.checkErrors, "split, left"); + } - //// Add series selection box + @Override + protected void addSeriesSelectionBox(JPanel panel, SimulationPlot plot, List allBranches) { ArrayList stages = new ArrayList<>(); stages.add(trans.get("PlotDialog.StageDropDown.allStages")); - stages.addAll(Util.generateSeriesLabels(simulation.getSimulatedData().getBranches())); + stages.addAll(Util.generateSeriesLabels(allBranches)); final JComboBox stageSelection = new JComboBox<>(stages.toArray(new String[0])); stageSelection.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { + if (simulation == null) { + return; + } int selectedStage = stageSelection.getSelectedIndex() - 1; checkErrors.setEnabled(selectedStage == -1 ? simulation.hasErrors() : simulation.hasErrors(selectedStage)); - myPlot.setShowBranch(selectedStage); + plot.setShowBranch(selectedStage); } }); @@ -131,73 +113,6 @@ public class SimulationPlotDialog extends JDialog { // Only show the combo box if there are at least 3 entries (ie, "All", "Main", and one other one) panel.add(stageSelection, "gapleft rel"); } - - //// Zoom in button - JButton button = new JButton(Icons.ZOOM_IN); - button.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if ((e.getModifiers() & InputEvent.ALT_MASK) == InputEvent.ALT_MASK) { - chartPanel.actionPerformed(new ActionEvent(chartPanel, ActionEvent.ACTION_FIRST, ChartPanel.ZOOM_IN_DOMAIN_COMMAND)); - } else { - chartPanel.actionPerformed(new ActionEvent(chartPanel, ActionEvent.ACTION_FIRST, ChartPanel.ZOOM_IN_BOTH_COMMAND)); - - } - } - }); - panel.add(button, "gapleft rel"); - - //// Reset Zoom button. - button = new JButton(Icons.ZOOM_RESET); - button.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - chartPanel.actionPerformed(new ActionEvent(chartPanel, ActionEvent.ACTION_FIRST, ChartPanel.ZOOM_RESET_BOTH_COMMAND)); - } - }); - panel.add(button, "gapleft rel"); - - - //// Zoom out button - button = new JButton(Icons.ZOOM_OUT); - button.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if ((e.getModifiers() & InputEvent.ALT_MASK) == InputEvent.ALT_MASK) { - chartPanel.actionPerformed(new ActionEvent(chartPanel, ActionEvent.ACTION_FIRST, ChartPanel.ZOOM_OUT_DOMAIN_COMMAND)); - } else { - chartPanel.actionPerformed(new ActionEvent(chartPanel, ActionEvent.ACTION_FIRST, ChartPanel.ZOOM_OUT_BOTH_COMMAND)); - } - } - }); - panel.add(button, "gapleft rel"); - - //// Print chart button - button = new JButton(trans.get("PlotDialog.btn.exportImage")); - button.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - doPNGExport(chartPanel,jChart); - } - }); - panel.add(button, "gapleft rel"); - - //// Close button - button = new JButton(trans.get("dlg.but.close")); - button.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - SimulationPlotDialog.this.dispose(); - } - }); - panel.add(button, "gapbefore push, right"); - this.setLocationByPlatform(true); - this.pack(); - - GUIUtil.setDisposableDialogOptions(this, button); - GUIUtil.rememberWindowSize(this); - this.setLocationByPlatform(true); - GUIUtil.rememberWindowPosition(this); } private static void initColors() { @@ -208,46 +123,4 @@ public class SimulationPlotDialog extends JDialog { private static void updateColors() { darkErrorColor = GUIUtil.getUITheme().getDarkErrorColor(); } - - private boolean doPNGExport(ChartPanel chartPanel, JFreeChart chart){ - JFileChooser chooser = new SaveFileChooser(); - chooser.setAcceptAllFileFilterUsed(false); - chooser.setFileFilter(FileHelper.PNG_FILTER); - chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory()); - - //// Ensures No Problems When Choosing File - if (chooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION) - return false; - - File file = chooser.getSelectedFile(); - if (file == null) - return false; - - file = FileHelper.forceExtension(file, "png"); - if (!FileHelper.confirmWrite(file, this)) { - return false; - } - - //// Uses JFreeChart Built In PNG Export Method - try{ - ChartUtils.saveChartAsPNG(file, chart, chartPanel.getWidth(), chartPanel.getHeight()); - } catch(Exception e){ - return false; - } - - return true; - } - - - /** - * Static method that shows a plot with the specified parameters. - * - * @param parent the parent window, which will be blocked. - * @param simulation the simulation to plot. - * @param config the configuration of the plot. - */ - public static SimulationPlotDialog getPlot(Window parent, Simulation simulation, SimulationPlotConfiguration config) { - return new SimulationPlotDialog(parent, simulation, config); - } - }