package net.sf.openrocket.gui.main; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.BorderFactory; import javax.swing.ButtonGroup; import javax.swing.JCheckBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JSpinner; import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import net.miginfocom.swing.MigLayout; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.Simulation; import net.sf.openrocket.document.StorageOptions; import net.sf.openrocket.file.RocketSaver; import net.sf.openrocket.file.openrocket.OpenRocketSaver; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.simulation.FlightData; import net.sf.openrocket.simulation.FlightDataBranch; import net.sf.openrocket.startup.Application; public class StorageOptionChooser extends JPanel { public static final double DEFAULT_SAVE_TIME_SKIP = 0.20; private final OpenRocketDocument document; private JRadioButton allButton; private JRadioButton someButton; private JRadioButton noneButton; private JSpinner timeSpinner; private JLabel estimateLabel; private boolean artificialEvent = false; private static final Translator trans = Application.getTranslator(); public StorageOptionChooser(OpenRocketDocument doc, StorageOptions opts) { super(new MigLayout()); this.document = doc; ChangeListener changeUpdater = new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { updateEstimate(); } }; ActionListener actionUpdater = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { updateEstimate(); } }; ButtonGroup buttonGroup = new ButtonGroup(); String tip; //// Simulated data to store: this.add(new JLabel(trans.get("StorageOptChooser.lbl.Simdatatostore")), "spanx, wrap unrel"); //// All simulated data allButton = new JRadioButton(trans.get("StorageOptChooser.rdbut.Allsimdata")); //// <html>Store all simulated data.<br> //// This can result in very large files! allButton.setToolTipText(trans.get("StorageOptChooser.lbl.longA1") + trans.get("StorageOptChooser.lbl.longA2")); buttonGroup.add(allButton); allButton.addActionListener(actionUpdater); this.add(allButton, "spanx, wrap rel"); //// Every someButton = new JRadioButton(trans.get("StorageOptChooser.rdbut.Every")); //// <html>Store plottable values approximately this far apart.<br>" //// Larger values result in smaller files. tip = trans.get("StorageOptChooser.lbl.longB1") + trans.get("StorageOptChooser.lbl.longB2"); someButton.setToolTipText(tip); buttonGroup.add(someButton); someButton.addActionListener(actionUpdater); this.add(someButton, ""); timeSpinner = new JSpinner(new SpinnerNumberModel(0.0, 0.0, 5.0, 0.1)); timeSpinner.setToolTipText(tip); timeSpinner.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { if (artificialEvent) return; someButton.setSelected(true); } }); this.add(timeSpinner, "wmin 55lp"); timeSpinner.addChangeListener(changeUpdater); //// seconds JLabel label = new JLabel(trans.get("StorageOptChooser.lbl.seconds")); label.setToolTipText(tip); this.add(label, "wrap rel"); //// Only primary figures noneButton = new JRadioButton(trans.get("StorageOptChooser.rdbut.Onlyprimfig")); //// <html>Store only the values shown in the summary table.<br> //// This results in the smallest files. noneButton.setToolTipText(trans.get("StorageOptChooser.lbl.longC1") + trans.get("StorageOptChooser.lbl.longC2")); buttonGroup.add(noneButton); noneButton.addActionListener(actionUpdater); this.add(noneButton, "spanx, wrap 20lp"); // Estimate is updated in loadOptions(opts) estimateLabel = new JLabel(""); //// An estimate on how large the resulting file would //// be with the present options. estimateLabel.setToolTipText(trans.get("StorageOptChooser.lbl.longD1")); this.add(estimateLabel, "spanx"); this.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createEmptyBorder(0, 10, 0, 0), //// Save options BorderFactory.createTitledBorder(trans.get("StorageOptChooser.ttip.Saveopt")))); loadOptions(opts); } public void loadOptions(StorageOptions opts) { double t; // Data storage radio button t = opts.getSimulationTimeSkip(); if (t == StorageOptions.SIMULATION_DATA_ALL) { allButton.setSelected(true); t = DEFAULT_SAVE_TIME_SKIP; } else if (t == StorageOptions.SIMULATION_DATA_NONE) { noneButton.setSelected(true); t = DEFAULT_SAVE_TIME_SKIP; } else { someButton.setSelected(true); } // Time skip spinner artificialEvent = true; timeSpinner.setValue(t); artificialEvent = false; updateEstimate(); } public void storeOptions(StorageOptions opts) { double t; if (allButton.isSelected()) { t = StorageOptions.SIMULATION_DATA_ALL; } else if (noneButton.isSelected()) { t = StorageOptions.SIMULATION_DATA_NONE; } else { t = (Double)timeSpinner.getValue(); } opts.setSimulationTimeSkip(t); opts.setExplicitlySet(true); } // TODO: MEDIUM: The estimation method always uses OpenRocketSaver! private static final RocketSaver ROCKET_SAVER = new OpenRocketSaver(); private void updateEstimate() { StorageOptions opts = new StorageOptions(); storeOptions(opts); long size = ROCKET_SAVER.estimateFileSize(document, opts); size = Math.max((size+512)/1024, 1); String formatted; if (size >= 10000) { formatted = (size/1000) + " MB"; } else if (size >= 1000){ formatted = (size/1000) + "." + ((size/100)%10) + " MB"; } else if (size >= 100) { formatted = ((size/10)*10) + " kB"; } else { formatted = size + " kB"; } //// Estimated file size: estimateLabel.setText(trans.get("StorageOptChooser.lbl.Estfilesize") + " " + formatted); } /** * Asks the user the storage options using a modal dialog window if the document * contains simulated data and the user has not explicitly set how to store the data. * * @param document the document to check. * @param parent the parent frame for the dialog. * @return <code>true</code> to continue, <code>false</code> if the user cancelled. */ public static boolean verifyStorageOptions(OpenRocketDocument document, JFrame parent) { StorageOptions options = document.getDefaultStorageOptions(); if (options.isExplicitlySet()) { // User has explicitly set the values, save as is return true; } boolean hasData = false; simulationLoop: for (Simulation s: document.getSimulations()) { if (s.getStatus() == Simulation.Status.NOT_SIMULATED || s.getStatus() == Simulation.Status.EXTERNAL) continue; FlightData data = s.getSimulatedData(); if (data == null) continue; for (int i=0; i < data.getBranchCount(); i++) { FlightDataBranch branch = data.getBranch(i); if (branch == null) continue; if (branch.getLength() > 0) { hasData = true; break simulationLoop; } } } if (!hasData) { // No data to store, do not ask only about compression return true; } StorageOptionChooser chooser = new StorageOptionChooser(document, options); //// Save options if (JOptionPane.showConfirmDialog(parent, chooser, trans.get("StorageOptChooser.lbl.Saveopt"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE) != JOptionPane.OK_OPTION) { // User cancelled return false; } chooser.storeOptions(options); return true; } }