From e1c86f6a7411e8473f01b45890d518bd813deb02 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Thu, 23 Mar 2023 01:20:58 +0100 Subject: [PATCH 1/4] [#2131] Throw warning message when design filename contains illegal characters --- core/resources/l10n/messages.properties | 4 + .../sf/openrocket/gui/main/BasicFrame.java | 10 +- ....java => DesignFileSaveAsFileChooser.java} | 232 +++++++++--------- .../gui/widgets/SaveFileChooser.java | 45 ++++ 4 files changed, 172 insertions(+), 119 deletions(-) rename swing/src/net/sf/openrocket/gui/main/{SaveAsFileChooser.java => DesignFileSaveAsFileChooser.java} (88%) create mode 100644 swing/src/net/sf/openrocket/gui/widgets/SaveFileChooser.java diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 6d70535fc..e607c6497 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -1341,6 +1341,10 @@ SaveRktWarningDialog.donotshow=Do not show this dialog again saveAs.openrocket.title=Save as OpenRocket ork file saveAs.rocksim.title=Export as RockSim rkt file +! SaveAsFileChooser +SaveAsFileChooser.illegalFilename.title = Illegal filename +SaveAsFileChooser.illegalFilename.message = The filename '%s' may not contain the character ' %c '. Please remove it. + ! StorageOptionChooser StorageOptChooser.lbl.Simdatatostore = Simulated data to store: StorageOptChooser.rdbut.Allsimdata = All simulated data diff --git a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java index 260f27d7d..4efe32789 100644 --- a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -86,6 +86,7 @@ import net.sf.openrocket.gui.util.OpenFileWorker; import net.sf.openrocket.gui.util.SaveFileWorker; import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.gui.util.URLUtil; +import net.sf.openrocket.gui.widgets.SaveFileChooser; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.logging.Markers; import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; @@ -1410,7 +1411,7 @@ public class BasicFrame extends JFrame { public boolean exportRockSimAction() { File file; - final SaveAsFileChooser chooser = SaveAsFileChooser.build(document, FileType.ROCKSIM); + final DesignFileSaveAsFileChooser chooser = DesignFileSaveAsFileChooser.build(document, FileType.ROCKSIM); int option = chooser.showSaveDialog(BasicFrame.this); @@ -1507,10 +1508,15 @@ public class BasicFrame extends JFrame { private boolean saveAsAction() { File file = null; - final SaveAsFileChooser chooser = SaveAsFileChooser.build(document, FileType.OPENROCKET); + final DesignFileSaveAsFileChooser chooser = DesignFileSaveAsFileChooser.build(document, FileType.OPENROCKET); int option = chooser.showSaveDialog(BasicFrame.this); + // If the user entered an illegal filename, show the dialog again + while (option == SaveFileChooser.ILLEGAL_FILENAME_ERROR) { + option = chooser.showSaveDialog(BasicFrame.this); + } + if (option != JFileChooser.APPROVE_OPTION) { log.info(Markers.USER_MARKER, "User decided not to save, option=" + option); return false; diff --git a/swing/src/net/sf/openrocket/gui/main/SaveAsFileChooser.java b/swing/src/net/sf/openrocket/gui/main/DesignFileSaveAsFileChooser.java similarity index 88% rename from swing/src/net/sf/openrocket/gui/main/SaveAsFileChooser.java rename to swing/src/net/sf/openrocket/gui/main/DesignFileSaveAsFileChooser.java index 755e64827..4344d8e10 100644 --- a/swing/src/net/sf/openrocket/gui/main/SaveAsFileChooser.java +++ b/swing/src/net/sf/openrocket/gui/main/DesignFileSaveAsFileChooser.java @@ -1,117 +1,115 @@ -package net.sf.openrocket.gui.main; - -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import javax.swing.JFileChooser; - -import net.sf.openrocket.document.OpenRocketDocument; -import net.sf.openrocket.document.StorageOptions; -import net.sf.openrocket.document.StorageOptions.FileType; -import net.sf.openrocket.gui.util.FileHelper; -import net.sf.openrocket.gui.util.SimpleFileFilter; -import net.sf.openrocket.gui.util.SwingPreferences; -import net.sf.openrocket.l10n.Translator; -import net.sf.openrocket.startup.Application; - -public class SaveAsFileChooser extends JFileChooser { - - private final FileType type; - private final OpenRocketDocument document; - private final StorageOptionChooser storageChooser; - - private static final Translator trans = Application.getTranslator(); - - public static SaveAsFileChooser build( OpenRocketDocument document, FileType type ) { - return new SaveAsFileChooser(document,type); - } - - private SaveAsFileChooser( OpenRocketDocument document, FileType type ) { - this.document = document; - this.type = type; - - this.setAcceptAllFileFilterUsed(false); - - File defaultFilename = document.getFileNoExtension(); - - switch( type ) { - default: - case OPENROCKET: - defaultFilename = FileHelper.forceExtension(defaultFilename,"ork"); - this.setDialogTitle(trans.get("saveAs.openrocket.title")); - storageChooser = new StorageOptionChooser(document, document.getDefaultStorageOptions()); - this.setAccessory(storageChooser); - this.addChoosableFileFilter(FileHelper.OPENROCKET_DESIGN_FILTER); - this.setFileFilter(FileHelper.OPENROCKET_DESIGN_FILTER); - break; - case ROCKSIM: - defaultFilename = FileHelper.forceExtension(defaultFilename,"rkt"); - this.setDialogTitle(trans.get("saveAs.rocksim.title")); - storageChooser = null; - this.addChoosableFileFilter(FileHelper.ROCKSIM_DESIGN_FILTER); - this.setFileFilter(FileHelper.ROCKSIM_DESIGN_FILTER); - break; - } - - final RememberFilenamePropertyListener listener = new RememberFilenamePropertyListener(); - this.addPropertyChangeListener(JFileChooser.FILE_FILTER_CHANGED_PROPERTY, listener); - this.addPropertyChangeListener(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY, listener); - this.addPropertyChangeListener(JFileChooser.SELECTED_FILES_CHANGED_PROPERTY, listener); - - this.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory()); - - if (defaultFilename != null) { - this.setSelectedFile(defaultFilename); - } - } - - public void storeOptions(StorageOptions opts) { - if ( storageChooser != null ) { - storageChooser.storeOptions(opts); - } - } - -} - -class RememberFilenamePropertyListener implements PropertyChangeListener { - private String oldFileName=null; - - @Override - public void propertyChange(PropertyChangeEvent event){ - if( JFileChooser.SELECTED_FILE_CHANGED_PROPERTY.equals(event.getPropertyName())){ - if(null != event.getOldValue()){ - this.oldFileName = ((File)event.getOldValue()).getName(); - } - }else if(JFileChooser.FILE_FILTER_CHANGED_PROPERTY.equals(event.getPropertyName())) { - JFileChooser chooser = (JFileChooser)event.getSource(); - if( chooser.getFileFilter() instanceof SimpleFileFilter) { - SimpleFileFilter filter = (SimpleFileFilter) (chooser.getFileFilter()); - String desiredExtension = filter.getExtensions()[0]; - - if (null == this.oldFileName) { - return; - } - String thisFileName = this.oldFileName; - - if (filter.accept(new File(thisFileName))) { - // nop - return; - } else { - String[] splitResults = thisFileName.split("\\."); - if (0 < splitResults.length) { - thisFileName = splitResults[0]; - } - chooser.setSelectedFile(new File(thisFileName + desiredExtension)); - return; - } - } - } - } - -} - - +package net.sf.openrocket.gui.main; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.File; + +import javax.swing.JFileChooser; + +import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.document.StorageOptions; +import net.sf.openrocket.document.StorageOptions.FileType; +import net.sf.openrocket.gui.util.FileHelper; +import net.sf.openrocket.gui.util.SimpleFileFilter; +import net.sf.openrocket.gui.util.SwingPreferences; +import net.sf.openrocket.gui.widgets.SaveFileChooser; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.startup.Application; + +public class DesignFileSaveAsFileChooser extends SaveFileChooser { + + private final FileType type; + private final OpenRocketDocument document; + private final StorageOptionChooser storageChooser; + + private static final Translator trans = Application.getTranslator(); + + public static DesignFileSaveAsFileChooser build(OpenRocketDocument document, FileType type ) { + return new DesignFileSaveAsFileChooser(document,type); + } + + private DesignFileSaveAsFileChooser(OpenRocketDocument document, FileType type ) { + this.document = document; + this.type = type; + + this.setAcceptAllFileFilterUsed(false); + + File defaultFilename = document.getFileNoExtension(); + + switch( type ) { + default: + case OPENROCKET: + defaultFilename = FileHelper.forceExtension(defaultFilename,"ork"); + this.setDialogTitle(trans.get("saveAs.openrocket.title")); + storageChooser = new StorageOptionChooser(document, document.getDefaultStorageOptions()); + this.setAccessory(storageChooser); + this.addChoosableFileFilter(FileHelper.OPENROCKET_DESIGN_FILTER); + this.setFileFilter(FileHelper.OPENROCKET_DESIGN_FILTER); + break; + case ROCKSIM: + defaultFilename = FileHelper.forceExtension(defaultFilename,"rkt"); + this.setDialogTitle(trans.get("saveAs.rocksim.title")); + storageChooser = null; + this.addChoosableFileFilter(FileHelper.ROCKSIM_DESIGN_FILTER); + this.setFileFilter(FileHelper.ROCKSIM_DESIGN_FILTER); + break; + } + + final RememberFilenamePropertyListener listener = new RememberFilenamePropertyListener(); + this.addPropertyChangeListener(JFileChooser.FILE_FILTER_CHANGED_PROPERTY, listener); + this.addPropertyChangeListener(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY, listener); + this.addPropertyChangeListener(JFileChooser.SELECTED_FILES_CHANGED_PROPERTY, listener); + + this.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory()); + + if (defaultFilename != null) { + this.setSelectedFile(defaultFilename); + } + } + + public void storeOptions(StorageOptions opts) { + if ( storageChooser != null ) { + storageChooser.storeOptions(opts); + } + } + +} + +class RememberFilenamePropertyListener implements PropertyChangeListener { + private String oldFileName=null; + + @Override + public void propertyChange(PropertyChangeEvent event){ + if( JFileChooser.SELECTED_FILE_CHANGED_PROPERTY.equals(event.getPropertyName())){ + if(null != event.getOldValue()){ + this.oldFileName = ((File)event.getOldValue()).getName(); + } + }else if(JFileChooser.FILE_FILTER_CHANGED_PROPERTY.equals(event.getPropertyName())) { + JFileChooser chooser = (JFileChooser)event.getSource(); + if( chooser.getFileFilter() instanceof SimpleFileFilter) { + SimpleFileFilter filter = (SimpleFileFilter) (chooser.getFileFilter()); + String desiredExtension = filter.getExtensions()[0]; + + if (null == this.oldFileName) { + return; + } + String thisFileName = this.oldFileName; + + if (filter.accept(new File(thisFileName))) { + // nop + return; + } else { + String[] splitResults = thisFileName.split("\\."); + if (0 < splitResults.length) { + thisFileName = splitResults[0]; + } + chooser.setSelectedFile(new File(thisFileName + desiredExtension)); + return; + } + } + } + } + +} + + diff --git a/swing/src/net/sf/openrocket/gui/widgets/SaveFileChooser.java b/swing/src/net/sf/openrocket/gui/widgets/SaveFileChooser.java new file mode 100644 index 000000000..6ec9b0011 --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/widgets/SaveFileChooser.java @@ -0,0 +1,45 @@ +package net.sf.openrocket.gui.widgets; + +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.startup.Application; + +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import java.awt.Component; +import java.awt.HeadlessException; +import java.io.File; + +public class SaveFileChooser extends JFileChooser { + private static final Translator trans = Application.getTranslator(); + + private static final char[] ILLEGAL_CHARS = new char[] { '/', '\\', ':', '*', '?', '"', '<', '>', '|' }; + public static final int ILLEGAL_FILENAME_ERROR = 23111998; + + + @Override + public int showSaveDialog(Component parent) throws HeadlessException { + int option = super.showSaveDialog(parent); + if (option != JFileChooser.APPROVE_OPTION) { + return option; + } + + // Check for invalid characters + File file = getSelectedFile(); + if (file == null) { + return ERROR_OPTION; + } + String filename = file.getName(); + for (char c : ILLEGAL_CHARS) { + if (filename.indexOf(c) >= 0) { + // Illegal character found + JOptionPane.showMessageDialog(parent, + String.format(trans.get("SaveAsFileChooser.illegalFilename.message"), filename, c), + trans.get("SaveAsFileChooser.illegalFilename.title"), + JOptionPane.WARNING_MESSAGE); + return ILLEGAL_FILENAME_ERROR; + } + } + + return option; + } +} From 605c92b3253bcd8daa214cde07ce923664c2b4a0 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Thu, 23 Mar 2023 01:27:55 +0100 Subject: [PATCH 2/4] Use SaveFileChooser in other parts of OR --- .../openrocket/gui/dialogs/PrintDialog.java | 3 +- .../GeneralOptimizationDialog.java | 3 +- .../gui/figure3d/photo/PhotoFrame.java | 3 +- .../sf/openrocket/gui/main/BasicFrame.java | 5 --- .../openrocket/gui/main/SimulationPanel.java | 35 ++++++++++--------- .../gui/plot/SimulationPlotDialog.java | 3 +- .../gui/simulation/SimulationExportPanel.java | 3 +- .../gui/widgets/SaveFileChooser.java | 11 +++++- .../utils/ComponentPresetEditor.java | 3 +- 9 files changed, 40 insertions(+), 29 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/dialogs/PrintDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/PrintDialog.java index 1e107363f..91336dd48 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/PrintDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/PrintDialog.java @@ -27,6 +27,7 @@ import javax.swing.event.TreeSelectionListener; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; +import net.sf.openrocket.gui.widgets.SaveFileChooser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -363,7 +364,7 @@ public class PrintDialog extends JDialog implements TreeSelectionListener { */ private boolean onSavePDF() { - JFileChooser chooser = new JFileChooser(); + JFileChooser chooser = new SaveFileChooser(); chooser.setFileFilter(FileHelper.PDF_FILTER); // Select initial directory diff --git a/swing/src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java index 5485c60b1..0354815ad 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/optimization/GeneralOptimizationDialog.java @@ -52,6 +52,7 @@ import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreePath; import net.sf.openrocket.arch.SystemInfo; +import net.sf.openrocket.gui.widgets.SaveFileChooser; import net.sf.openrocket.rocketcomponent.FlightConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -1154,7 +1155,7 @@ public class GeneralOptimizationDialog extends JDialog { trans.get("export.header"), trans.get("export.header.ttip")); - JFileChooser chooser = new JFileChooser(); + JFileChooser chooser = new SaveFileChooser(); chooser.setFileFilter(FileHelper.CSV_FILTER); chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory()); chooser.setAccessory(csvOptions); diff --git a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java index de088d5d0..e57ebc7f0 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java @@ -42,6 +42,7 @@ import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.util.Icons; import net.sf.openrocket.gui.util.SimpleFileFilter; import net.sf.openrocket.gui.util.SwingPreferences; +import net.sf.openrocket.gui.widgets.SaveFileChooser; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.logging.LoggingSystemSetup; import net.sf.openrocket.logging.Markers; @@ -200,7 +201,7 @@ public class PhotoFrame extends JFrame { final FileFilter png = new SimpleFileFilter(trans.get("PhotoFrame.fileFilter.png"), ".png"); - final JFileChooser chooser = new JFileChooser(); + final JFileChooser chooser = new SaveFileChooser(); chooser.addChoosableFileFilter(png); chooser.setFileFilter(png); diff --git a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java index 4efe32789..33d7f5266 100644 --- a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -1512,11 +1512,6 @@ public class BasicFrame extends JFrame { int option = chooser.showSaveDialog(BasicFrame.this); - // If the user entered an illegal filename, show the dialog again - while (option == SaveFileChooser.ILLEGAL_FILENAME_ERROR) { - option = chooser.showSaveDialog(BasicFrame.this); - } - if (option != JFileChooser.APPROVE_OPTION) { log.info(Markers.USER_MARKER, "User decided not to save, option=" + option); return false; diff --git a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java index 78731f5e3..17336d761 100644 --- a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java @@ -43,6 +43,7 @@ import net.sf.openrocket.arch.SystemInfo; import net.sf.openrocket.gui.components.CsvOptionPanel; import net.sf.openrocket.gui.util.FileHelper; import net.sf.openrocket.gui.util.SwingPreferences; +import net.sf.openrocket.gui.widgets.SaveFileChooser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -403,25 +404,25 @@ public class SimulationPanel extends JPanel { return; } - JFileChooser fch = setUpSimExportCSVFileChooser(); - int selectionStatus = fch.showSaveDialog(tableParent); + JFileChooser chooser = setUpSimExportCSVFileChooser(); + int selectionStatus = chooser.showSaveDialog(tableParent); if (selectionStatus != JFileChooser.APPROVE_OPTION) { log.debug("User cancelled CSV export"); return; } // Fetch the info from the file chooser - File CSVFile = fch.getSelectedFile(); + File CSVFile = chooser.getSelectedFile(); CSVFile = FileHelper.forceExtension(CSVFile, "csv"); if (!FileHelper.confirmWrite(CSVFile, SimulationPanel.this)) { log.debug("User cancelled CSV export overwrite"); return; } - String separator = ((CsvOptionPanel) fch.getAccessory()).getFieldSeparator(); - int precision = ((CsvOptionPanel) fch.getAccessory()).getDecimalPlaces(); - boolean isExponentialNotation = ((CsvOptionPanel) fch.getAccessory()).isExponentialNotation(); - ((CsvOptionPanel) fch.getAccessory()).storePreferences(); + String separator = ((CsvOptionPanel) chooser.getAccessory()).getFieldSeparator(); + int precision = ((CsvOptionPanel) chooser.getAccessory()).getDecimalPlaces(); + boolean isExponentialNotation = ((CsvOptionPanel) chooser.getAccessory()).isExponentialNotation(); + ((CsvOptionPanel) chooser.getAccessory()).storePreferences(); // Handle some special separator options from CsvOptionPanel if (separator.equals(trans.get("CsvOptionPanel.separator.space"))) { @@ -439,29 +440,29 @@ public class SimulationPanel extends JPanel { * @return The file chooser. */ private JFileChooser setUpSimExportCSVFileChooser() { - JFileChooser fch = new JFileChooser(); - fch.setDialogTitle(trans.get("simpanel.pop.exportToCSV.save.dialog.title")); - fch.setFileFilter(FileHelper.CSV_FILTER); - fch.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory()); - fch.setAcceptAllFileFilterUsed(false); + JFileChooser chooser = new SaveFileChooser(); + chooser.setDialogTitle(trans.get("simpanel.pop.exportToCSV.save.dialog.title")); + chooser.setFileFilter(FileHelper.CSV_FILTER); + chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory()); + chooser.setAcceptAllFileFilterUsed(false); // Default output CSV to same name as the document's rocket name. String fileName = document.getRocket().getName() + ".csv"; - fch.setSelectedFile(new File(fileName)); + chooser.setSelectedFile(new File(fileName)); // Add CSV options to FileChooser CsvOptionPanel CSVOptions = new CsvOptionPanel(SimulationTableCSVExport.class); - fch.setAccessory(CSVOptions); + chooser.setAccessory(CSVOptions); // TODO: update this dynamically instead of hard-coded values // The macOS file chooser has an issue where it does not update its size when the accessory is added. if (SystemInfo.getPlatform() == SystemInfo.Platform.MAC_OS) { - Dimension currentSize = fch.getPreferredSize(); + Dimension currentSize = chooser.getPreferredSize(); Dimension newSize = new Dimension((int) (1.5 * currentSize.width), (int) (1.3 * currentSize.height)); - fch.setPreferredSize(newSize); + chooser.setPreferredSize(newSize); } - return fch; + return chooser; } private Simulation[] getSelectedSimulations() { diff --git a/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java b/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java index 3c30fcd87..d9c1724ea 100644 --- a/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java +++ b/swing/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java @@ -26,6 +26,7 @@ import net.sf.openrocket.gui.util.FileHelper; import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.util.Icons; import net.sf.openrocket.gui.util.SwingPreferences; +import net.sf.openrocket.gui.widgets.SaveFileChooser; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.startup.Application; import net.sf.openrocket.startup.Preferences; @@ -173,7 +174,7 @@ public class SimulationPlotDialog extends JDialog { } private boolean doPngExport(ChartPanel chartPanel, JFreeChart chart){ - JFileChooser chooser = new JFileChooser(); + JFileChooser chooser = new SaveFileChooser(); chooser.setAcceptAllFileFilterUsed(false); chooser.setFileFilter(FileHelper.PNG_FILTER); chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory()); diff --git a/swing/src/net/sf/openrocket/gui/simulation/SimulationExportPanel.java b/swing/src/net/sf/openrocket/gui/simulation/SimulationExportPanel.java index ad6172ffb..f42bd9800 100644 --- a/swing/src/net/sf/openrocket/gui/simulation/SimulationExportPanel.java +++ b/swing/src/net/sf/openrocket/gui/simulation/SimulationExportPanel.java @@ -32,6 +32,7 @@ import net.sf.openrocket.gui.util.FileHelper; import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.util.SaveCSVWorker; import net.sf.openrocket.gui.util.SwingPreferences; +import net.sf.openrocket.gui.widgets.SaveFileChooser; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.simulation.FlightData; import net.sf.openrocket.simulation.FlightDataBranch; @@ -219,7 +220,7 @@ public class SimulationExportPanel extends JPanel { } public boolean doExport() { - JFileChooser chooser = new JFileChooser(); + JFileChooser chooser = new SaveFileChooser(); chooser.setFileFilter(FileHelper.CSV_FILTER); chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory()); diff --git a/swing/src/net/sf/openrocket/gui/widgets/SaveFileChooser.java b/swing/src/net/sf/openrocket/gui/widgets/SaveFileChooser.java index 6ec9b0011..6ab633ca9 100644 --- a/swing/src/net/sf/openrocket/gui/widgets/SaveFileChooser.java +++ b/swing/src/net/sf/openrocket/gui/widgets/SaveFileChooser.java @@ -1,5 +1,6 @@ package net.sf.openrocket.gui.widgets; +import net.sf.openrocket.gui.main.BasicFrame; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.startup.Application; @@ -36,7 +37,15 @@ public class SaveFileChooser extends JFileChooser { String.format(trans.get("SaveAsFileChooser.illegalFilename.message"), filename, c), trans.get("SaveAsFileChooser.illegalFilename.title"), JOptionPane.WARNING_MESSAGE); - return ILLEGAL_FILENAME_ERROR; + + option = ILLEGAL_FILENAME_ERROR; + + // If the user entered an illegal filename, show the dialog again + while (option == SaveFileChooser.ILLEGAL_FILENAME_ERROR) { + option = showSaveDialog(parent); + } + + return option; } } diff --git a/swing/src/net/sf/openrocket/utils/ComponentPresetEditor.java b/swing/src/net/sf/openrocket/utils/ComponentPresetEditor.java index d6faa15f8..a70cfc878 100644 --- a/swing/src/net/sf/openrocket/utils/ComponentPresetEditor.java +++ b/swing/src/net/sf/openrocket/utils/ComponentPresetEditor.java @@ -37,6 +37,7 @@ import net.sf.openrocket.gui.preset.PresetResultListener; import net.sf.openrocket.gui.util.FileHelper; import net.sf.openrocket.gui.util.Icons; import net.sf.openrocket.gui.util.SwingPreferences; +import net.sf.openrocket.gui.widgets.SaveFileChooser; import net.sf.openrocket.logging.Markers; import net.sf.openrocket.material.Material; import net.sf.openrocket.preset.ComponentPreset; @@ -404,7 +405,7 @@ public class ComponentPresetEditor extends JPanel implements PresetResultListene private boolean saveAsORC() throws JAXBException, IOException { File file = null; - final JFileChooser chooser = new JFileChooser(); + final JFileChooser chooser = new SaveFileChooser(); chooser.addChoosableFileFilter(FileHelper.OPEN_ROCKET_COMPONENT_FILTER); chooser.setFileFilter(FileHelper.OPEN_ROCKET_COMPONENT_FILTER); From 24de589f6926da4098146cc979612ede444da56d Mon Sep 17 00:00:00 2001 From: SiboVG Date: Thu, 23 Mar 2023 03:35:32 +0100 Subject: [PATCH 3/4] Ensure proper file name is parsed to character checker --- .../gui/widgets/SaveFileChooser.java | 68 ++++++++++++------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/widgets/SaveFileChooser.java b/swing/src/net/sf/openrocket/gui/widgets/SaveFileChooser.java index 6ab633ca9..a501e66c9 100644 --- a/swing/src/net/sf/openrocket/gui/widgets/SaveFileChooser.java +++ b/swing/src/net/sf/openrocket/gui/widgets/SaveFileChooser.java @@ -1,54 +1,74 @@ package net.sf.openrocket.gui.widgets; -import net.sf.openrocket.gui.main.BasicFrame; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.startup.Application; import javax.swing.JFileChooser; import javax.swing.JOptionPane; -import java.awt.Component; -import java.awt.HeadlessException; import java.io.File; public class SaveFileChooser extends JFileChooser { private static final Translator trans = Application.getTranslator(); private static final char[] ILLEGAL_CHARS = new char[] { '/', '\\', ':', '*', '?', '"', '<', '>', '|' }; - public static final int ILLEGAL_FILENAME_ERROR = 23111998; + + private File cwd = null; + private File currentFile = null; + private String fileName = null; @Override - public int showSaveDialog(Component parent) throws HeadlessException { - int option = super.showSaveDialog(parent); - if (option != JFileChooser.APPROVE_OPTION) { - return option; + public void setSelectedFile(File file) { + currentFile = file; + if (file != null) { + if (file.getParentFile() != getCurrentDirectory()) { + cwd = getCurrentDirectory(); + fileName = getFilenameInput(file); + return; + } else { + fileName = file.getName(); + } } + super.setSelectedFile(file); + } - // Check for invalid characters - File file = getSelectedFile(); - if (file == null) { - return ERROR_OPTION; + @Override + public void approveSelection() { + if (!containsIllegalChars(fileName)) { + super.setSelectedFile(currentFile); + setCurrentDirectory(cwd); + super.approveSelection(); + } + } + + private boolean containsIllegalChars(String filename) { + if (filename == null || filename.isEmpty()) { + return false; } - String filename = file.getName(); for (char c : ILLEGAL_CHARS) { if (filename.indexOf(c) >= 0) { // Illegal character found - JOptionPane.showMessageDialog(parent, + JOptionPane.showMessageDialog(getParent(), String.format(trans.get("SaveAsFileChooser.illegalFilename.message"), filename, c), trans.get("SaveAsFileChooser.illegalFilename.title"), JOptionPane.WARNING_MESSAGE); - - option = ILLEGAL_FILENAME_ERROR; - - // If the user entered an illegal filename, show the dialog again - while (option == SaveFileChooser.ILLEGAL_FILENAME_ERROR) { - option = showSaveDialog(parent); - } - - return option; + return true; } } + return false; + } - return option; + private String getFilenameInput(File file) { + if (file == null) { + return null; + } + + String fullPath = file.getAbsolutePath(); + String cwdPath = cwd.getAbsolutePath(); + + String relativePath = fullPath.replaceFirst("^" + cwdPath, "").trim(); + relativePath = relativePath.replaceFirst("^" + File.separator, ""); + + return relativePath; } } From f4ea3275eba793cbd422ca83ccd72bab4b519ec5 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Thu, 23 Mar 2023 11:14:33 +0100 Subject: [PATCH 4/4] Fix some small bugs --- .../gui/widgets/SaveFileChooser.java | 70 +++++++++++++------ 1 file changed, 48 insertions(+), 22 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/widgets/SaveFileChooser.java b/swing/src/net/sf/openrocket/gui/widgets/SaveFileChooser.java index a501e66c9..61b5ce3ba 100644 --- a/swing/src/net/sf/openrocket/gui/widgets/SaveFileChooser.java +++ b/swing/src/net/sf/openrocket/gui/widgets/SaveFileChooser.java @@ -6,6 +6,8 @@ import net.sf.openrocket.startup.Application; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import java.io.File; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; public class SaveFileChooser extends JFileChooser { private static final Translator trans = Application.getTranslator(); @@ -20,55 +22,79 @@ public class SaveFileChooser extends JFileChooser { @Override public void setSelectedFile(File file) { currentFile = file; - if (file != null) { - if (file.getParentFile() != getCurrentDirectory()) { - cwd = getCurrentDirectory(); - fileName = getFilenameInput(file); + if (file == null) { + super.setSelectedFile(null); + return; + } + + if (file.getParentFile() != getCurrentDirectory()) { + cwd = getCurrentDirectory(); + fileName = getFilenameInput(currentFile, cwd); + if (getIllegalChar(fileName) != null) { return; - } else { - fileName = file.getName(); } } + super.setSelectedFile(file); + fileName = file.getName(); + cwd = getCurrentDirectory(); } @Override public void approveSelection() { - if (!containsIllegalChars(fileName)) { + Character c = getIllegalChar(fileName); + if (c != null) { + // Illegal character found + JOptionPane.showMessageDialog(getParent(), + String.format(trans.get("SaveAsFileChooser.illegalFilename.message"), fileName, c), + trans.get("SaveAsFileChooser.illegalFilename.title"), + JOptionPane.WARNING_MESSAGE); + } else { + // Successful filename super.setSelectedFile(currentFile); setCurrentDirectory(cwd); super.approveSelection(); } } - private boolean containsIllegalChars(String filename) { + /** + * Returns an illegal character if one is found in the filename, otherwise returns null. + * @param filename The filename to check + * @return The illegal character, or null if none is found + */ + private Character getIllegalChar(String filename) { if (filename == null || filename.isEmpty()) { - return false; + return null; } for (char c : ILLEGAL_CHARS) { if (filename.indexOf(c) >= 0) { - // Illegal character found - JOptionPane.showMessageDialog(getParent(), - String.format(trans.get("SaveAsFileChooser.illegalFilename.message"), filename, c), - trans.get("SaveAsFileChooser.illegalFilename.title"), - JOptionPane.WARNING_MESSAGE); - return true; + return c; } } - return false; + return null; } - private String getFilenameInput(File file) { - if (file == null) { + /** + * Returns the filename input by the user, or null if the filename is invalid. + * You can't simply use getSelectedFile().getName() because it won't work for malformed filenames. + * @param file The file to get the filename from + * @param cwd The current working directory + * @return The filename input by the user, or null if the filename is invalid + */ + private String getFilenameInput(File file, File cwd) { + if (file == null || cwd == null) { return null; } String fullPath = file.getAbsolutePath(); String cwdPath = cwd.getAbsolutePath(); - String relativePath = fullPath.replaceFirst("^" + cwdPath, "").trim(); - relativePath = relativePath.replaceFirst("^" + File.separator, ""); - - return relativePath; + try { + String relativePath = fullPath.replaceFirst(Pattern.quote(cwdPath), "").trim(); + relativePath = relativePath.replaceFirst(Pattern.quote(File.separator), ""); + return relativePath; + } catch (PatternSyntaxException e) { + return null; + } } }