diff --git a/core/src/net/sf/openrocket/file/rocksim/export/ParachuteDTO.java b/core/src/net/sf/openrocket/file/rocksim/export/ParachuteDTO.java index de60dfc7b..5b4dcef5a 100644 --- a/core/src/net/sf/openrocket/file/rocksim/export/ParachuteDTO.java +++ b/core/src/net/sf/openrocket/file/rocksim/export/ParachuteDTO.java @@ -17,8 +17,6 @@ public class ParachuteDTO extends BasePartDTO { @XmlElement(name = RocksimCommonConstants.DIAMETER) private double dia = 0d; - @XmlElement(name = RocksimCommonConstants.SPILL_HOLE_DIA) - private double spillHoleDia = 0d; @XmlElement(name = RocksimCommonConstants.SHROUD_LINE_COUNT) private int ShroudLineCount = 0; @XmlElement(name = RocksimCommonConstants.THICKNESS) @@ -63,14 +61,6 @@ public class ParachuteDTO extends BasePartDTO { dia = theDia; } - public double getSpillHoleDia() { - return spillHoleDia; - } - - public void setSpillHoleDia(double theSpillHoleDia) { - spillHoleDia = theSpillHoleDia; - } - public int getShroudLineCount() { return ShroudLineCount; } diff --git a/core/src/net/sf/openrocket/gui/main/BasicFrame.java b/core/src/net/sf/openrocket/gui/main/BasicFrame.java index b6bab9390..b4a383387 100644 --- a/core/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/core/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -1211,11 +1211,15 @@ public class BasicFrame extends JFrame { return true; } - - - - + /** + * "Save" action. If the design is new, then this is identical to "Save As", with a default file filter for .ork. + * If the rocket being edited previously was opened from a .ork file, then it will be saved immediately to the same + * file. But clicking on 'Save' for an existing design file with a .rkt will bring up a confirmation dialog because + * it's potentially a destructive write (loss of some fidelity if it's truly an original Rocksim generated file). + * + * @return true if the file was saved, false otherwise + */ private boolean saveAction() { File file = document.getFile(); if (file == null) { @@ -1224,43 +1228,46 @@ public class BasicFrame extends JFrame { } log.info("Saving document to " + file); - // Saving RockSim designs is not supported if (FileHelper.ROCKSIM_DESIGN_FILTER.accept(file)) { - file = new File(file.getAbsolutePath().replaceAll(".[rR][kK][tT](.[gG][zZ])?$", - ".ork")); - - log.info("Attempting to save in RockSim format, renaming to " + file); - int option = JOptionPane.showConfirmDialog(this, new Object[] { - "Saving designs in RockSim format is not supported.", - "Save in OpenRocket format instead (" + file.getName() + ")?" - }, "Save " + file.getName(), JOptionPane.YES_NO_OPTION, - JOptionPane.QUESTION_MESSAGE, null); - if (option != JOptionPane.YES_OPTION) { - log.user("User chose not to save"); - return false; - } - - document.setFile(file); + return saveAsRocksim(file); } return saveAs(file); } - - + + /** + * "Save As" action. + * + * Never should a .rkt file contain an OpenRocket content, or an .ork file contain a Rocksim design. Regardless of + * what extension the user has chosen, it would violate the Principle of Least Astonishment to do otherwise + * (and we want to make doing the wrong thing really hard to do). So always force the appropriate extension. + * + * This can result in some odd looking filenames (MyDesign.rkt.ork, MyDesign.rkt.ork.rkt, etc.) if the user is + * not paying attention, but the user can control that by modifying the filename in the dialog. + * + * @return true if the file was saved, false otherwise + */ private boolean saveAsAction() { File file = null; - - // TODO: HIGH: what if *.rkt chosen? + StorageOptionChooser storageChooser = new StorageOptionChooser(document, document.getDefaultStorageOptions()); - JFileChooser chooser = new JFileChooser(); + final JFileChooser chooser = new JFileChooser(); chooser.addChoosableFileFilter(FileHelper.OPENROCKET_DESIGN_FILTER); chooser.addChoosableFileFilter(FileHelper.ROCKSIM_DESIGN_FILTER); - chooser.setFileFilter(FileHelper.OPENROCKET_DESIGN_FILTER); + + //Force the file filter to match the file extension that was opened. Will default to OR if the file is null. + if (FileHelper.ROCKSIM_DESIGN_FILTER.accept(document.getFile())) { + chooser.setFileFilter(FileHelper.ROCKSIM_DESIGN_FILTER); + } + else { + chooser.setFileFilter(FileHelper.OPENROCKET_DESIGN_FILTER); + } chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory()); chooser.setAccessory(storageChooser); - if (document.getFile() != null) + if (document.getFile() != null) { chooser.setSelectedFile(document.getFile()); - + } + int option = chooser.showSaveDialog(BasicFrame.this); if (option != JFileChooser.APPROVE_OPTION) { log.user("User decided not to save, option=" + option); @@ -1275,34 +1282,45 @@ public class BasicFrame extends JFrame { ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(chooser.getCurrentDirectory()); storageChooser.storeOptions(document.getDefaultStorageOptions()); - - if (chooser.getFileFilter().equals(FileHelper.OPENROCKET_DESIGN_FILTER)) { - file = FileHelper.ensureExtension(file, "ork"); - if (!FileHelper.confirmWrite(file, this)) { - return false; - } - - return saveAs(file); - } - else if (chooser.getFileFilter().equals(FileHelper.ROCKSIM_DESIGN_FILTER)) { - file = FileHelper.ensureExtension(file, "rkt"); - if (!FileHelper.confirmWrite(file, this)) { - return false; - } - try { - new RocksimSaver().save(file, document); - return true; - } catch (IOException e) { - return false; - } + if (chooser.getFileFilter().equals(FileHelper.ROCKSIM_DESIGN_FILTER)) { + return saveAsRocksim(file); } else { - return false; + file = FileHelper.forceExtension(file, "ork"); + return FileHelper.confirmWrite(file, this) && saveAs(file); } } - - private boolean saveAs(File file) { + + /** + * Perform the writing of the design to the given file in Rocksim format. + * + * @param file the chosen file + * + * @return true if the file was written + */ + private boolean saveAsRocksim(File file) { + file = FileHelper.forceExtension(file, "rkt"); + if (!FileHelper.confirmWrite(file, this)) { + return false; + } + + try { + new RocksimSaver().save(file, document); + return true; + } catch (IOException e) { + return false; + } + } + + /** + * Perform the writing of the design to the given file in OpenRocket format. + * + * @param file the chosen file + * + * @return true if the file was written + */ + private boolean saveAs(File file) { log.info("Saving document as " + file); boolean saved = false; diff --git a/core/src/net/sf/openrocket/gui/util/FileHelper.java b/core/src/net/sf/openrocket/gui/util/FileHelper.java index 8c496393e..2f7623d7b 100644 --- a/core/src/net/sf/openrocket/gui/util/FileHelper.java +++ b/core/src/net/sf/openrocket/gui/util/FileHelper.java @@ -1,17 +1,16 @@ package net.sf.openrocket.gui.util; -import java.awt.Component; -import java.io.File; -import java.io.IOException; - -import javax.swing.JOptionPane; -import javax.swing.filechooser.FileFilter; - import net.sf.openrocket.l10n.L10N; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.logging.LogHelper; import net.sf.openrocket.startup.Application; +import javax.swing.*; +import javax.swing.filechooser.FileFilter; +import java.awt.*; +import java.io.File; +import java.io.IOException; + /** * Helper methods related to user-initiated file manipulation. *
@@ -61,7 +60,7 @@ public final class FileHelper { * * @param original the original file * @param extension the extension to append if none exists (without preceding dot) - * @return the resulting filen + * @return the resulting file */ public static File ensureExtension(File original, String extension) { @@ -75,7 +74,33 @@ public final class FileHelper { return original; } - + /** + * Ensure that the provided file has the given file extension. This differs from ensureExtension in that this + * method guarantees that the file will have the extension, whereas ensureExtension only treats the extension + * as a default. + * + * @param original the original file + * @param extension the extension to guarantee (without preceding dot) + * @return the resulting file + */ + public static File forceExtension(File original, String extension) { + + if (!original.getName().toLowerCase().endsWith(extension.toLowerCase())) { + log.debug(1, "File name does not contain extension, adding '" + extension + "'"); + String name = original.getAbsolutePath(); + if (extension.startsWith(".")) { + name = name + extension; + } + else { + name = name + "." + extension; + } + return new File(name); + } + + return original; + } + + /** * Confirm that it is allowed to write to a file. If the file exists, * a confirmation dialog will be presented to the user to ensure overwriting is ok.