diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 72f5c27ab..c5338156b 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -258,6 +258,7 @@ pref.dlg.tab.Design = Design pref.dlg.tab.Simulation = Simulation pref.dlg.tab.Launch = Launch pref.dlg.tab.Miscellaneousoptions = Miscellaneous options +pref.dlg.lbl.RockSimWarning = Show warning when saving in RockSim format pref.dlg.tab.Graphics = Graphics pref.dlg.lbl.DecalEditor = Graphics Editor @@ -1134,6 +1135,13 @@ FinsetCfg.ttip.Finfillets1 = Adds the predicted mass of fin fillets to the FinsetCfg.ttip.Finfillets2 = Assumes the fillet is concave and tangent to the body tube and fin.
FinsetCfg.ttip.Finfillets3 = Zero radius will give no fillet. +! Save RKT Warning Dialog +SaveRktWarningDialog.txt1=Exporting to RockSim file format does not support all features of OpenRocket. +SaveRktWarningDialog.donotshow=Do not show this dialog again + +saveAs.openrocket.title=Save as OpenRocket ork file +saveAs.rocksim.title=Export as RockSim rkt file + ! StorageOptionChooser StorageOptChooser.lbl.Simdatatostore = Simulated data to store: StorageOptChooser.rdbut.Allsimdata = All simulated data @@ -1201,26 +1209,30 @@ main.menu.file.desc = File-handling related tasks main.menu.file.new = New main.menu.file.new.desc = Create a new rocket design main.menu.file.open = Open... -BasicFrame.item.Openrocketdesign = Open a rocket design +main.menu.file.open.desc = Open a rocket design main.menu.file.openRecent = Open Recent... -BasicFrame.item.Openrecentrocketdesign = Open a recent rocket design -main.menu.file.openExample = Open example... -BasicFrame.item.Openexamplerocketdesign = Open an example rocket design +main.menu.file.openRecent.desc = Open a recent rocket design +main.menu.file.openExample = Open Example... +main.menu.file.openExample.desc = Open an example rocket design main.menu.file.save = Save -BasicFrame.item.SavecurRocketdesign = Save the current rocket design +main.menu.file.save.desc = Save the current rocket design main.menu.file.saveAs = Save as... -BasicFrame.item.SavecurRocketdesnewfile = Save the current rocket design to a new file +main.menu.file.saveAs.desc = Save the current rocket design to a new file +main.menu.file.import = Import... +main.menu.file.import.desc = Import model from RockSim rkt file +main.menu.file.export = Export... +main.menu.file.export.desc = Export model to RockSim rkt file main.menu.file.print = Print / Export PDF... main.menu.file.print.desc = Print or save as PDF the parts list and fin templates main.menu.file.close = Close -BasicFrame.item.Closedesign = Close the current rocket design +main.menu.file.close.desc = Close the current rocket design main.menu.file.quit = Quit -BasicFrame.item.Quitprogram = Quit the program +main.menu.file.quit.desc = Quit the program main.menu.file.exportDecal = Export Decal main.menu.file.exportDecal.desc = Export a decal from the current rocket design to a file for editing. main.menu.edit = Edit -BasicFrame.menu.Rocketedt = Rocket editing +main.menu.edit.desc = Rocket editing main.menu.edit.undo = Undo main.menu.edit.undo.desc = Undo the previous operation main.menu.edit.redo = Redo diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java b/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java index 464dda9c4..8bfba9ae7 100644 --- a/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java +++ b/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java @@ -4,9 +4,15 @@ import java.io.IOException; import java.io.InputStream; import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.Simulation; import net.sf.openrocket.document.StorageOptions; +import net.sf.openrocket.document.StorageOptions.FileType; import net.sf.openrocket.file.AbstractRocketLoader; import net.sf.openrocket.file.DocumentLoadingContext; import net.sf.openrocket.file.RocketLoadException; @@ -15,11 +21,6 @@ import net.sf.openrocket.simulation.FlightDataBranch; import net.sf.openrocket.simulation.FlightDataType; import net.sf.openrocket.simulation.extension.SimulationExtension; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; - /** * Class that loads a rocket definition from an OpenRocket rocket file. @@ -70,7 +71,7 @@ public class OpenRocketLoader extends AbstractRocketLoader { List list = branch.get(FlightDataType.TYPE_TIME); if (list == null) continue; - + double previousTime = Double.NaN; for (double time : list) { if (time - previousTime < timeSkip) @@ -81,6 +82,7 @@ public class OpenRocketLoader extends AbstractRocketLoader { timeSkip = Math.rint(timeSkip * 100) / 100; doc.getDefaultStorageOptions().setSimulationTimeSkip(timeSkip); doc.getDefaultStorageOptions().setExplicitlySet(false); + doc.getDefaultStorageOptions().setFileType(FileType.OPENROCKET); // Call simulation extensions for (Simulation sim : doc.getSimulations()) { diff --git a/core/src/net/sf/openrocket/file/rocksim/importt/RocksimLoader.java b/core/src/net/sf/openrocket/file/rocksim/importt/RocksimLoader.java index 60bea006a..bf452c20e 100644 --- a/core/src/net/sf/openrocket/file/rocksim/importt/RocksimLoader.java +++ b/core/src/net/sf/openrocket/file/rocksim/importt/RocksimLoader.java @@ -6,14 +6,15 @@ package net.sf.openrocket.file.rocksim.importt; import java.io.IOException; import java.io.InputStream; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import net.sf.openrocket.document.StorageOptions.FileType; import net.sf.openrocket.file.AbstractRocketLoader; import net.sf.openrocket.file.DocumentLoadingContext; import net.sf.openrocket.file.RocketLoadException; import net.sf.openrocket.file.simplesax.SimpleSAX; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; - /** * This class is the main entry point for Rocksim design file imported to OpenRocket. Currently only Rocksim v9 * file formats are supported, although it is possible that v8 formats will work for most components. @@ -52,5 +53,6 @@ public class RocksimLoader extends AbstractRocketLoader { context.getOpenRocketDocument().setFile(null); context.getOpenRocketDocument().clearUndo(); + context.getOpenRocketDocument().getDefaultStorageOptions().setFileType(FileType.ROCKSIM); } } diff --git a/core/src/net/sf/openrocket/startup/Preferences.java b/core/src/net/sf/openrocket/startup/Preferences.java index a759d9d48..4a542a4d3 100644 --- a/core/src/net/sf/openrocket/startup/Preferences.java +++ b/core/src/net/sf/openrocket/startup/Preferences.java @@ -61,6 +61,7 @@ public abstract class Preferences implements ChangeSource { // Node names public static final String PREFERRED_THRUST_CURVE_MOTOR_NODE = "preferredThrustCurveMotors"; private static final String AUTO_OPEN_LAST_DESIGN = "AUTO_OPEN_LAST_DESIGN"; + private static final String SHOW_ROCKSIM_FORMAT_WARNING = "SHOW_ROCKSIM_FORMAT_WARNING"; //Preferences related to 3D graphics public static final String OPENGL_ENABLED = "OpenGL_Is_Enabled"; @@ -162,6 +163,14 @@ public abstract class Preferences implements ChangeSource { this.putBoolean(LAUNCH_INTO_WIND, check); } + public final boolean getShowRockSimFormatWarning() { + return this.getBoolean(SHOW_ROCKSIM_FORMAT_WARNING, true); + } + + public final void setShowRockSimFormatWarning(boolean check) { + this.putBoolean(SHOW_ROCKSIM_FORMAT_WARNING, check); + } + public final double getDefaultMach() { return Application.getPreferences().getChoice(Preferences.DEFAULT_MACH_NUMBER, 0.9, 0.3); } @@ -533,7 +542,7 @@ public abstract class Preferences implements ChangeSource { String color = get("componentColors", c, StaticFieldHolder.DEFAULT_COLORS); if (color == null) return Color.BLACK; - + Color clr = parseColor(color); if (clr != null) { return clr; @@ -584,7 +593,7 @@ public abstract class Preferences implements ChangeSource { public Material getDefaultComponentMaterial( Class componentClass, Material.Type type) { - + String material = get("componentMaterials", componentClass, null); if (material != null) { try { @@ -613,7 +622,7 @@ public abstract class Preferences implements ChangeSource { */ public void setDefaultComponentMaterial( Class componentClass, Material material) { - + putString("componentMaterials", componentClass.getSimpleName(), material == null ? null : material.toStorableString()); } @@ -689,7 +698,7 @@ public abstract class Preferences implements ChangeSource { protected String get(String directory, Class componentClass, Map, String> defaultMap) { - + // Search preferences Class c = componentClass; while (c != null && RocketComponent.class.isAssignableFrom(c)) { @@ -701,7 +710,7 @@ public abstract class Preferences implements ChangeSource { if (defaultMap == null) return null; - + // Search defaults c = componentClass; while (RocketComponent.class.isAssignableFrom(c)) { @@ -736,12 +745,14 @@ public abstract class Preferences implements ChangeSource { */ private static final HashMap, String> DEFAULT_LINE_STYLES = new HashMap, String>(); + static { DEFAULT_LINE_STYLES.put(RocketComponent.class, LineStyle.SOLID.name()); DEFAULT_LINE_STYLES.put(MassObject.class, LineStyle.DASHED.name()); } private static final HashMap, String> DEFAULT_COLORS = new HashMap, String>(); + static { DEFAULT_COLORS.put(BodyComponent.class, "0,0,240"); DEFAULT_COLORS.put(TubeFinSet.class, "0,0,200"); diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preferences/GeneralPreferencesPanel.java b/swing/src/net/sf/openrocket/gui/dialogs/preferences/GeneralPreferencesPanel.java index 607b42cbf..43183f537 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/preferences/GeneralPreferencesPanel.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/preferences/GeneralPreferencesPanel.java @@ -208,6 +208,30 @@ public class GeneralPreferencesPanel extends PreferencesPanel { } }); this.add(button, "right, wrap"); + + //// Open most recent file on startup + final JCheckBox openRecentOnStartupBox = new JCheckBox(trans.get("pref.dlg.but.openlast")); + openRecentOnStartupBox.setSelected(preferences.isAutoOpenLastDesignOnStartupEnabled()); + openRecentOnStartupBox.addActionListener( new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + preferences.setAutoOpenLastDesignOnStartup(openRecentOnStartupBox.isSelected()); + } + }); + this.add(openRecentOnStartupBox,"spanx, wrap"); + + //// Save RockSim Format warning dialog + final JCheckBox rocksimWarningDialogBox = new JCheckBox(trans.get("pref.dlg.lbl.RockSimWarning")); + rocksimWarningDialogBox.setSelected(preferences.getShowRockSimFormatWarning()); + rocksimWarningDialogBox.addActionListener( new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + preferences.setShowRockSimFormatWarning(rocksimWarningDialogBox.isSelected()); + } + }); + this.add(rocksimWarningDialogBox,"spanx, wrap"); + + } diff --git a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java index 9cac11c80..ddfd3dcef 100644 --- a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -12,8 +12,6 @@ import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -33,6 +31,7 @@ import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.InputMap; import javax.swing.JButton; +import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JFileChooser; @@ -54,22 +53,25 @@ import javax.swing.ScrollPaneConstants; import javax.swing.SwingUtilities; import javax.swing.border.BevelBorder; import javax.swing.border.TitledBorder; +import javax.swing.event.ChangeEvent; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; -import javax.swing.event.ChangeEvent; import javax.swing.tree.DefaultTreeSelectionModel; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import net.miginfocom.swing.MigLayout; import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.OpenRocketDocumentFactory; import net.sf.openrocket.document.StorageOptions; +import net.sf.openrocket.document.StorageOptions.FileType; import net.sf.openrocket.file.GeneralRocketSaver; import net.sf.openrocket.file.RocketLoadException; -import net.sf.openrocket.gui.ExportDecalDialog; -import net.sf.openrocket.gui.StorageOptionChooser; +import net.sf.openrocket.gui.components.StyledLabel; import net.sf.openrocket.gui.configdialog.ComponentConfigDialog; import net.sf.openrocket.gui.customexpression.CustomExpressionDialog; import net.sf.openrocket.gui.dialogs.AboutDialog; @@ -94,7 +96,6 @@ import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.util.Icons; import net.sf.openrocket.gui.util.OpenFileWorker; import net.sf.openrocket.gui.util.SaveFileWorker; -import net.sf.openrocket.gui.util.SimpleFileFilter; import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.logging.Markers; @@ -103,6 +104,7 @@ import net.sf.openrocket.rocketcomponent.ComponentChangeListener; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.startup.Application; +import net.sf.openrocket.startup.Preferences; import net.sf.openrocket.util.BugException; import net.sf.openrocket.util.MemoryManagement; import net.sf.openrocket.util.MemoryManagement.MemoryData; @@ -111,55 +113,51 @@ import net.sf.openrocket.util.StateChangeListener; import net.sf.openrocket.util.TestRockets; import net.sf.openrocket.utils.ComponentPresetEditor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class BasicFrame extends JFrame implements PropertyChangeListener { +public class BasicFrame extends JFrame { private static final Logger log = LoggerFactory.getLogger(BasicFrame.class); - + private static final GeneralRocketSaver ROCKET_SAVER = new GeneralRocketSaver(); - + private static final Translator trans = Application.getTranslator(); - + private static final Preferences prefs = Application.getPreferences(); + private static final int SHORTCUT_KEY = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); - + public static final int COMPONENT_TAB = 0; public static final int CONFIGURATION_TAB = 1; public static final int SIMULATION_TAB = 2; - - + + /** * List of currently open frames. When the list goes empty * it is time to exit the application. */ private static final ArrayList frames = new ArrayList(); - - + + /** * Whether "New" and "Open" should replace this frame. * Should be set to false on the first rocket modification. */ private boolean replaceable = false; - - - + private final OpenRocketDocument document; private final Rocket rocket; - + private JTabbedPane tabbedPane; private RocketPanel rocketpanel; private ComponentTree tree = null; - + private final DocumentSelectionModel selectionModel; private final TreeSelectionModel componentSelectionModel; private final ListSelectionModel simulationSelectionModel; - + /** Actions available for rocket modifications */ private final RocketActions actions; - + private SimulationPanel simulationPanel; - - + + /** * Sole constructor. Creates a new frame based on the supplied document * and adds it to the current frames list. @@ -168,36 +166,36 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { */ public BasicFrame(OpenRocketDocument document) { log.debug("Instantiating new BasicFrame"); - + this.document = document; this.rocket = document.getRocket(); this.rocket.getDefaultConfiguration().setAllStages(); - + // Create the component tree selection model that will be used componentSelectionModel = new DefaultTreeSelectionModel(); componentSelectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); - + // Obtain the simulation selection model that will be used simulationPanel = new SimulationPanel(document); simulationSelectionModel = simulationPanel.getSimulationListSelectionModel(); - + // Combine into a DocumentSelectionModel selectionModel = new DocumentSelectionModel(document); selectionModel.attachComponentTreeSelectionModel(componentSelectionModel); selectionModel.attachSimulationListSelectionModel(simulationSelectionModel); - - + + actions = new RocketActions(document, selectionModel, this); - - + + log.debug("Constructing the BasicFrame UI"); - + // The main vertical split pane JSplitPane vertical = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true); vertical.setResizeWeight(0.5); this.add(vertical); - - + + // The top tabbed pane tabbedPane = new JTabbedPane(); //// Rocket design @@ -206,51 +204,51 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { tabbedPane.addTab(trans.get("BasicFrame.tab.Flightconfig"), null, new FlightConfigurationPanel(document)); //// Flight simulations tabbedPane.addTab(trans.get("BasicFrame.tab.Flightsim"), null, simulationPanel); - + // Add change listener to catch when the tabs are changed. This is to run simulations // automagically when the simulation tab is selected. tabbedPane.addChangeListener(new BasicFrame_changeAdapter(this)); - - + + vertical.setTopComponent(tabbedPane); - - - + + + // Bottom segment, rocket figure - + rocketpanel = new RocketPanel(document); vertical.setBottomComponent(rocketpanel); - + rocketpanel.setSelectionModel(tree.getSelectionModel()); - - + + createMenu(); - - + + rocket.addComponentChangeListener(new ComponentChangeListener() { @Override public void componentChanged(ComponentChangeEvent e) { setTitle(); } }); - + setTitle(); this.pack(); - - + + // Set initial window size Dimension size = Toolkit.getDefaultToolkit().getScreenSize(); size.width = size.width * 9 / 10; size.height = size.height * 9 / 10; this.setSize(size); - + // Remember changed size GUIUtil.rememberWindowSize(this); - + this.setLocationByPlatform(true); - + GUIUtil.setWindowIcons(this); - + this.validate(); vertical.setDividerLocation(0.4); setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); @@ -260,11 +258,11 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { closeAction(); } }); - + frames.add(this); log.debug("BasicFrame instantiation complete"); } - + /** * Construct the "Rocket design" tab. This contains a horizontal split pane * with the left component the design tree and the right component buttons @@ -273,15 +271,15 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { private JComponent designTab() { JSplitPane horizontal = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true); horizontal.setResizeWeight(0.5); - - + + // Upper-left segment, component tree - + JPanel panel = new JPanel(new MigLayout("fill, flowy", "[grow][grow 0]","[grow]")); - + tree = new ComponentTree(document); tree.setSelectionModel(componentSelectionModel); - + // Remove JTree key events that interfere with menu accelerators InputMap im = SwingUtilities.getUIInputMap(tree, JComponent.WHEN_FOCUSED); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, SHORTCUT_KEY), null); @@ -291,9 +289,9 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, SHORTCUT_KEY), null); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_O, SHORTCUT_KEY), null); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_N, SHORTCUT_KEY), null); - - - + + + // Double-click opens config dialog MouseListener ml = new MouseAdapter() { @Override @@ -311,7 +309,7 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }; tree.addMouseListener(ml); - + // Update dialog when selection is changed componentSelectionModel.addTreeSelectionListener(new TreeSelectionListener() { @Override @@ -321,7 +319,7 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { if (path == null) return; tree.scrollPathToVisible(path); - + if (!ComponentConfigDialog.isDialogVisible()) return; RocketComponent c = (RocketComponent) path.getLastPathComponent(); @@ -329,57 +327,57 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { BasicFrame.this.document, c); } }); - + // Place tree inside scroll pane JScrollPane scroll = new JScrollPane(tree); panel.add(scroll, "spany, grow, wrap"); - - + + // Buttons JButton button = new JButton(actions.getMoveUpAction()); panel.add(button, "sizegroup buttons, aligny 65%"); - + button = new JButton(actions.getMoveDownAction()); panel.add(button, "sizegroup buttons, aligny 0%"); - + button = new JButton(actions.getEditAction()); panel.add(button, "sizegroup buttons"); - + button = new JButton(actions.getNewStageAction()); panel.add(button, "sizegroup buttons"); - + button = new JButton(actions.getDeleteAction()); button.setIcon(null); button.setMnemonic(0); panel.add(button, "sizegroup buttons"); - + horizontal.setLeftComponent(panel); - - + + // Upper-right segment, component addition buttons - + panel = new JPanel(new MigLayout("fill, insets 0", "[0::]")); - + scroll = new JScrollPane(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); scroll.setViewportView(new ComponentAddButtons(document, componentSelectionModel, scroll.getViewport())); scroll.setBorder(null); scroll.setViewportBorder(null); - + TitledBorder border = BorderFactory.createTitledBorder(trans.get("BasicFrame.title.Addnewcomp")); GUIUtil.changeFontStyle(border, Font.BOLD); scroll.setBorder(border); - + panel.add(scroll, "grow"); - + horizontal.setRightComponent(panel); - + return horizontal; } - - - + + + /** * Return the currently selected rocket component, or null if none selected. */ @@ -388,11 +386,11 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { if (path == null) return null; tree.scrollPathToVisible(path); - + return (RocketComponent) path.getLastPathComponent(); } - - + + /** * Creates the menu for the window. */ @@ -400,14 +398,14 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { JMenuBar menubar = new JMenuBar(); JMenu menu; JMenuItem item; - + //// File menu = new JMenu(trans.get("main.menu.file")); menu.setMnemonic(KeyEvent.VK_F); //// File-handling related tasks menu.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.desc")); menubar.add(menu); - + //// New item = new JMenuItem(trans.get("main.menu.file.new"), KeyEvent.VK_N); item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, SHORTCUT_KEY)); @@ -424,12 +422,12 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - + //// Open... item = new JMenuItem(trans.get("main.menu.file.open"), KeyEvent.VK_O); item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, SHORTCUT_KEY)); //// Open a rocket design - item.getAccessibleContext().setAccessibleDescription(trans.get("BasicFrame.item.Openrocketdesign")); + item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.open.desc")); item.setIcon(Icons.FILE_OPEN); item.addActionListener(new ActionListener() { @Override @@ -439,26 +437,26 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - + //// Open Recent... item = new MRUDesignFileAction(trans.get("main.menu.file.openRecent"), this); - item.getAccessibleContext().setAccessibleDescription(trans.get("BasicFrame.item.Openrecentrocketdesign")); + item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.openRecent.desc")); item.setIcon(Icons.FILE_OPEN); menu.add(item); - + //// Open example... item = new ExampleDesignFileAction(trans.get("main.menu.file.openExample"), this); - item.getAccessibleContext().setAccessibleDescription(trans.get("BasicFrame.item.Openexamplerocketdesign")); + item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.openExample.desc")); item.setIcon(Icons.FILE_OPEN_EXAMPLE); menu.add(item); - + menu.addSeparator(); - + //// Save item = new JMenuItem(trans.get("main.menu.file.save"), KeyEvent.VK_S); item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, SHORTCUT_KEY)); //// Save the current rocket design - item.getAccessibleContext().setAccessibleDescription(trans.get("BasicFrame.item.SavecurRocketdesign")); + item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.save.desc")); item.setIcon(Icons.FILE_SAVE); item.addActionListener(new ActionListener() { @Override @@ -468,13 +466,13 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - + //// Save as... item = new JMenuItem(trans.get("main.menu.file.saveAs"), KeyEvent.VK_A); item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, SHORTCUT_KEY | ActionEvent.SHIFT_MASK)); //// Save the current rocket design to a new file - item.getAccessibleContext().setAccessibleDescription(trans.get("BasicFrame.item.SavecurRocketdesnewfile")); + item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.saveAs.desc")); item.setIcon(Icons.FILE_SAVE_AS); item.addActionListener(new ActionListener() { @Override @@ -484,8 +482,33 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - - + + menu.addSeparator(); + + //// Import Rocksim + item = new JMenuItem(trans.get("main.menu.file.import")); + item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.import.desc")); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + log.info(Markers.USER_MARKER, "Import... selected"); + importAction(); + } + }); + menu.add(item); + + //// Export Rocksim + item = new JMenuItem(trans.get("main.menu.file.export")); + item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.export.desc")); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + log.info(Markers.USER_MARKER, "Export... selected"); + exportAction(); + } + }); + menu.add(item); + //// Export decal... item = new JMenuItem(trans.get("main.menu.file.exportDecal")); item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.exportDecal.desc")); @@ -498,16 +521,16 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { item.setEnabled(document.getDecalList().size() > 0); final JMenuItem exportMenuItem = item; document.getRocket().addChangeListener(new StateChangeListener() { - + @Override public void stateChanged(EventObject e) { exportMenuItem.setEnabled(document.getDecalList().size() > 0); } - + }); menu.add(item); - - + + //// Print... item = new JMenuItem(trans.get("main.menu.file.print"), KeyEvent.VK_P); item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, SHORTCUT_KEY)); @@ -522,15 +545,15 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - - + + menu.addSeparator(); - + //// Close item = new JMenuItem(trans.get("main.menu.file.close"), KeyEvent.VK_C); item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, SHORTCUT_KEY)); //// Close the current rocket design - item.getAccessibleContext().setAccessibleDescription(trans.get("BasicFrame.item.Closedesign")); + item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.close.desc")); item.setIcon(Icons.FILE_CLOSE); item.addActionListener(new ActionListener() { @Override @@ -540,14 +563,14 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - + menu.addSeparator(); - + //// Quit item = new JMenuItem(trans.get("main.menu.file.quit"), KeyEvent.VK_Q); item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, SHORTCUT_KEY)); //// Quit the program - item.getAccessibleContext().setAccessibleDescription(trans.get("BasicFrame.item.Quitprogram")); + item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.quit.desc")); item.setIcon(Icons.FILE_QUIT); item.addActionListener(new ActionListener() { @Override @@ -557,26 +580,26 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - - - + + + //// Edit menu = new JMenu(trans.get("main.menu.edit")); menu.setMnemonic(KeyEvent.VK_E); //// Rocket editing - menu.getAccessibleContext().setAccessibleDescription(trans.get("BasicFrame.menu.Rocketedt")); + menu.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.edit.desc")); menubar.add(menu); - - + + Action action = UndoRedoAction.newUndoAction(document); item = new JMenuItem(action); item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, SHORTCUT_KEY)); item.setMnemonic(KeyEvent.VK_U); //// Undo the previous operation item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.edit.undo.desc")); - + menu.add(item); - + action = UndoRedoAction.newRedoAction(document); item = new JMenuItem(action); item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Y, SHORTCUT_KEY)); @@ -584,26 +607,26 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { //// Redo the previously undone operation item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.edit.redo.desc")); menu.add(item); - + menu.addSeparator(); - - + + item = new JMenuItem(actions.getCutAction()); menu.add(item); - + item = new JMenuItem(actions.getCopyAction()); menu.add(item); - + item = new JMenuItem(actions.getPasteAction()); menu.add(item); - + item = new JMenuItem(actions.getDeleteAction()); menu.add(item); - + menu.addSeparator(); - - - + + + item = new JMenuItem(trans.get("main.menu.edit.resize")); item.setIcon(Icons.EDIT_SCALE); item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.edit.resize.desc")); @@ -617,9 +640,9 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - - - + + + //// Preferences item = new JMenuItem(trans.get("main.menu.edit.preferences")); item.setIcon(Icons.PREFERENCES); @@ -633,7 +656,7 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - + //// Edit Component Preset File if (System.getProperty("openrocket.preseteditor.menu") != null) { item = new JMenuItem(trans.get("main.menu.edit.editpreset")); @@ -649,15 +672,15 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { }); menu.add(item); } - - + + //// Analyze menu = new JMenu(trans.get("main.menu.analyze")); menu.setMnemonic(KeyEvent.VK_A); //// Analyzing the rocket menu.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.analyze.desc")); menubar.add(menu); - + //// Component analysis item = new JMenuItem(trans.get("main.menu.analyze.componentAnalysis"), KeyEvent.VK_C); //// Analyze the rocket components separately @@ -670,7 +693,7 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - + //// Optimize item = new JMenuItem(trans.get("main.menu.analyze.optimization"), KeyEvent.VK_O); item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.analyze.optimization.desc")); @@ -682,7 +705,7 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - + //// Custom expressions item = new JMenuItem(trans.get("main.menu.analyze.customExpressions"), KeyEvent.VK_E); item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.analyze.customExpressions.desc")); @@ -694,7 +717,7 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - + item = new JMenuItem(trans.get("PhotoFrame.title"), KeyEvent.VK_P); item.getAccessibleContext().setAccessibleDescription(trans.get("PhotoFrame.desc")); item.addActionListener(new ActionListener() { @@ -706,25 +729,25 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - + //// Debug // (shown if openrocket.debug.menu is defined) if (System.getProperty("openrocket.debug.menu") != null) { menubar.add(makeDebugMenu()); } - - - + + + //// Help - + menu = new JMenu(trans.get("main.menu.help")); menu.setMnemonic(KeyEvent.VK_H); menu.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.desc")); menubar.add(menu); - - + + // Guided tours - + item = new JMenuItem(trans.get("main.menu.help.tours"), KeyEvent.VK_L); item.setIcon(Icons.HELP_TOURS); item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.tours.desc")); @@ -736,9 +759,9 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - + menu.addSeparator(); - + //// Bug report item = new JMenuItem(trans.get("main.menu.help.bugReport"), KeyEvent.VK_B); item.setIcon(Icons.HELP_BUG_REPORT); @@ -751,7 +774,7 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - + //// Debug log item = new JMenuItem(trans.get("main.menu.help.debugLog")); item.setIcon(Icons.HELP_DEBUG_LOG); @@ -764,10 +787,10 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - + menu.addSeparator(); - - + + //// License item = new JMenuItem(trans.get("main.menu.help.license"), KeyEvent.VK_L); item.setIcon(Icons.HELP_LICENSE); @@ -780,8 +803,8 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - - + + //// About item = new JMenuItem(trans.get("main.menu.help.about"), KeyEvent.VK_A); item.setIcon(Icons.HELP_ABOUT); @@ -794,24 +817,24 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - - + + this.setJMenuBar(menubar); } - + private JMenu makeDebugMenu() { JMenu menu; JMenuItem item; - + /* * This menu is intentionally left untranslated. */ - + //// Debug menu menu = new JMenu("Debug"); //// OpenRocket debugging tasks menu.getAccessibleContext().setAccessibleDescription("OpenRocket debugging tasks"); - + //// What is this menu? item = new JMenuItem("What is this menu?"); item.addActionListener(new ActionListener() { @@ -822,16 +845,16 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { new Object[] { "The 'Debug' menu includes actions for testing and debugging " + "OpenRocket.", " ", - "The menu is made visible by defining the system property " + - "'openrocket.debug.menu' when starting OpenRocket.", - "It should not be visible by default." }, + "The menu is made visible by defining the system property " + + "'openrocket.debug.menu' when starting OpenRocket.", + "It should not be visible by default." }, "Debug menu", JOptionPane.INFORMATION_MESSAGE); } }); menu.add(item); - + menu.addSeparator(); - + //// Create test rocket item = new JMenuItem("Create test rocket"); item.addActionListener(new ActionListener() { @@ -845,8 +868,8 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { }, "Generate random test rocket", JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, new Object[] { "Random", "OK" - }, "OK"); - + }, "OK"); + Rocket r; if (sel == 0) { r = new TestRockets(null).makeTestRocket(); @@ -855,7 +878,7 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } else { return; } - + OpenRocketDocument doc = OpenRocketDocumentFactory.createDocumentFromRocket(r); doc.setSaved(true); BasicFrame frame = new BasicFrame(doc); @@ -863,9 +886,9 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - - - + + + item = new JMenuItem("Create 'Iso-Haisu'"); item.addActionListener(new ActionListener() { @Override @@ -879,8 +902,8 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - - + + item = new JMenuItem("Create 'Big Blue'"); item.addActionListener(new ActionListener() { @Override @@ -894,16 +917,16 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - + menu.addSeparator(); - - + + item = new JMenuItem("Memory statistics"); item.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { log.info(Markers.USER_MARKER, "Memory statistics selected"); - + // Get discarded but remaining objects (this also runs System.gc multiple times) List objects = MemoryManagement.getRemainingCollectableObjects(); StringBuilder sb = new StringBuilder(); @@ -914,13 +937,13 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { if (o == null) continue; sb.append("Age ").append(System.currentTimeMillis() - data.getRegistrationTime()) - .append(" ms: ").append(o).append('\n'); + .append(" ms: ").append(o).append('\n'); count++; // Explicitly null the strong reference to avoid possibility of invisible references o = null; } sb.append("Total: " + count); - + // Get basic memory stats System.gc(); long max = Runtime.getRuntime().maxMemory(); @@ -931,14 +954,14 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { stats[1] = String.format(" Max memory: %.1f MB", max / 1024.0 / 1024.0); stats[2] = String.format(" Used memory: %.1f MB (%.0f%%)", used / 1024.0 / 1024.0, 100.0 * used / max); stats[3] = String.format(" Free memory: %.1f MB (%.0f%%)", free / 1024.0 / 1024.0, 100.0 * free / max); - - + + DetailDialog.showDetailedMessageDialog(BasicFrame.this, stats, sb.toString(), "Memory statistics", JOptionPane.INFORMATION_MESSAGE); } }); menu.add(item); - + //// Exhaust memory item = new JMenuItem("Exhaust memory"); item.addActionListener(new ActionListener() { @@ -968,10 +991,10 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - - + + menu.addSeparator(); - + //// Exception here item = new JMenuItem("Exception here"); item.addActionListener(new ActionListener() { @@ -982,7 +1005,7 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - + item = new JMenuItem("Exception from EDT"); item.addActionListener(new ActionListener() { @Override @@ -998,7 +1021,7 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - + item = new JMenuItem("Exception from other thread"); item.addActionListener(new ActionListener() { @Override @@ -1013,7 +1036,7 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - + item = new JMenuItem("OutOfMemoryError here"); item.addActionListener(new ActionListener() { @Override @@ -1023,11 +1046,11 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - - + + menu.addSeparator(); - - + + item = new JMenuItem("Test popup"); item.addActionListener(new ActionListener() { @Override @@ -1043,14 +1066,14 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } }); menu.add(item); - - - - + + + + return menu; } - - + + /** * Select the tab on the main pane. * @@ -1059,17 +1082,16 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { public void selectTab(int tab) { tabbedPane.setSelectedIndex(tab); } - - - + + + private void openAction() { JFileChooser chooser = new JFileChooser(); - + chooser.addChoosableFileFilter(FileHelper.ALL_DESIGNS_FILTER); chooser.addChoosableFileFilter(FileHelper.OPENROCKET_DESIGN_FILTER); - chooser.addChoosableFileFilter(FileHelper.ROCKSIM_DESIGN_FILTER); - chooser.setFileFilter(FileHelper.ALL_DESIGNS_FILTER); - + chooser.setFileFilter(FileHelper.OPENROCKET_DESIGN_FILTER); + chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); chooser.setMultiSelectionEnabled(true); chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory()); @@ -1078,12 +1100,12 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { log.info(Markers.USER_MARKER, "Decided not to open files, option=" + option); return; } - + ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(chooser.getCurrentDirectory()); - + File[] files = chooser.getSelectedFiles(); log.info(Markers.USER_MARKER, "Opening files " + Arrays.toString(files)); - + for (File file : files) { log.info("Opening file: " + file); if (open(file, this)) { @@ -1092,20 +1114,51 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } } } - + + private void importAction() { + JFileChooser chooser = new JFileChooser(); + + chooser.addChoosableFileFilter(FileHelper.ALL_DESIGNS_FILTER); + chooser.addChoosableFileFilter(FileHelper.ROCKSIM_DESIGN_FILTER); + chooser.setFileFilter(FileHelper.ROCKSIM_DESIGN_FILTER); + + chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + chooser.setMultiSelectionEnabled(true); + chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory()); + int option = chooser.showOpenDialog(this); + if (option != JFileChooser.APPROVE_OPTION) { + log.info(Markers.USER_MARKER, "Decided not to open files, option=" + option); + return; + } + + ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(chooser.getCurrentDirectory()); + + File[] files = chooser.getSelectedFiles(); + log.info(Markers.USER_MARKER, "Opening files " + Arrays.toString(files)); + + for (File file : files) { + log.info("Opening file: " + file); + if (open(file, this)) { + MRUDesignFile opts = MRUDesignFile.getInstance(); + opts.addFile(file.getAbsolutePath()); + } + } + } + + void closeIfReplaceable() { // Close previous window if replacing if (replaceable && document.isSaved()) { // We are replacing the frame, make new window have current location BasicFrame newFrame = frames.get(frames.size() - 1); newFrame.setLocation(this.getLocation()); - + log.info("Closing window because it is replaceable"); closeAction(); } - + } - + /** * Open a file based on a URL. * @param url the file to open. @@ -1115,14 +1168,14 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { public static void open(URL url, BasicFrame parent) { String displayName = null; // First figure out the file name from the URL - + // Try using URI.getPath(); try { URI uri = url.toURI(); displayName = uri.getPath(); } catch (URISyntaxException ignore) { } - + // Try URL-decoding the URL if (displayName == null) { try { @@ -1130,25 +1183,25 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } catch (UnsupportedEncodingException ignore) { } } - + if (displayName == null) { displayName = ""; } - + // Remove path from filename if (displayName.lastIndexOf('/') >= 0) { displayName = displayName.substring(displayName.lastIndexOf('/') + 1); } - - + + // Open the file log.info("Opening file from url=" + url + " filename=" + displayName); - + OpenFileWorker worker = new OpenFileWorker(url); open(worker, displayName, parent, true); } - - + + /** * Open the specified file in a new design frame. If an error occurs, an error * dialog is shown and false is returned. @@ -1161,8 +1214,8 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { OpenFileWorker worker = new OpenFileWorker(file); return open(worker, file.getName(), parent, false); } - - + + /** * Open the specified file using the provided worker. * @@ -1181,50 +1234,50 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { log.info("User cancelled the OpenFileWorker"); return false; } - - + + // Handle the document OpenRocketDocument doc = null; try { - + doc = worker.get(); - + } catch (ExecutionException e) { - + Throwable cause = e.getCause(); - + if (cause instanceof FileNotFoundException) { - + log.warn("File not found", cause); JOptionPane.showMessageDialog(parent, "File not found: " + displayName, "Error opening file", JOptionPane.ERROR_MESSAGE); return false; - + } else if (cause instanceof RocketLoadException) { - + log.warn("Error loading the file", cause); JOptionPane.showMessageDialog(parent, "Unable to open file '" + displayName + "': " + cause.getMessage(), - "Error opening file", JOptionPane.ERROR_MESSAGE); + "Error opening file", JOptionPane.ERROR_MESSAGE); return false; - + } else { - + throw new BugException("Unknown error when opening file", e); - + } - + } catch (InterruptedException e) { throw new BugException("EDT was interrupted", e); } - + if (doc == null) { throw new BugException("Document loader returned null"); } - - + + // Show warnings WarningSet warnings = worker.getRocketLoader().getWarnings(); if (!warnings.isEmpty()) { @@ -1235,26 +1288,26 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { trans.get("BasicFrame.WarningDialog.txt1") + " " + displayName + ".", //// Some design features may not have been loaded correctly. trans.get("BasicFrame.WarningDialog.txt2") - }, + }, //// Warnings while opening file trans.get("BasicFrame.WarningDialog.title"), warnings); } - + // Open the frame log.debug("Opening new frame with the document"); BasicFrame frame = new BasicFrame(doc); frame.setVisible(true); - + if (parent != null && parent instanceof BasicFrame) { ((BasicFrame) parent).closeIfReplaceable(); } if (openRocketConfigDialog) { ComponentConfigDialog.showDialog(frame, doc, doc.getRocket()); } - + 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 @@ -1265,114 +1318,47 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { */ private boolean saveAction() { File file = document.getFile(); - if (file == null) { + if (file == null || document.getDefaultStorageOptions().getFileType().equals(FileType.ROCKSIM)) { log.info("Document does not contain file, opening save as dialog instead"); return saveAsAction(); } + log.info("Saving document to " + file); - - if (FileHelper.ROCKSIM_DESIGN_FILTER.accept(file)) { - return saveAsRocksim(file); - } - return saveAs(file); + + return saveAsOpenRocket(file); } - - private static String oldFileName=null; - public void propertyChange(PropertyChangeEvent event){ - if( JFileChooser.SELECTED_FILE_CHANGED_PROPERTY == event.getPropertyName()){ - if(null != event.getOldValue()){ - BasicFrame.oldFileName = ((File)event.getOldValue()).getName(); - } - return; - }else if(JFileChooser.FILE_FILTER_CHANGED_PROPERTY == event.getPropertyName()){ - JFileChooser chooser = (JFileChooser)event.getSource(); - SimpleFileFilter filter = (SimpleFileFilter)(chooser.getFileFilter()); - String desiredExtension = filter.getExtensions()[0]; - - if( null == BasicFrame.oldFileName){ - return; - } - String thisFileName = BasicFrame.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; - } - } - } - + + /** - * "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. + * "Export" action. * * @return true if the file was saved, false otherwise */ - private boolean saveAsAction() { + private boolean exportAction() { File file = null; - - StorageOptionChooser storageChooser = - new StorageOptionChooser(document, document.getDefaultStorageOptions()); - final JFileChooser chooser = new JFileChooser(); - chooser.setAcceptAllFileFilterUsed(false); - chooser.addChoosableFileFilter(FileHelper.OPENROCKET_DESIGN_FILTER); - chooser.addChoosableFileFilter(FileHelper.ROCKSIM_DESIGN_FILTER); - chooser.addPropertyChangeListener(JFileChooser.FILE_FILTER_CHANGED_PROPERTY, this); - chooser.addPropertyChangeListener(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY, this); - chooser.addPropertyChangeListener(JFileChooser.SELECTED_FILES_CHANGED_PROPERTY, this); - - //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) { - chooser.setSelectedFile(document.getFile()); - } - + + final SaveAsFileChooser chooser = SaveAsFileChooser.build(document, FileType.ROCKSIM); + int option = chooser.showSaveDialog(BasicFrame.this); + if (option != JFileChooser.APPROVE_OPTION) { log.info(Markers.USER_MARKER, "User decided not to save, option=" + option); return false; } - + file = chooser.getSelectedFile(); if (file == null) { log.info(Markers.USER_MARKER, "User did not select a file"); return false; } - + ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(chooser.getCurrentDirectory()); - storageChooser.storeOptions(document.getDefaultStorageOptions()); - - if (chooser.getFileFilter().equals(FileHelper.ROCKSIM_DESIGN_FILTER)) { + + file = FileHelper.forceExtension(file, "ork"); + if (FileHelper.confirmWrite(file, this) ) { return saveAsRocksim(file); } - else { - file = FileHelper.forceExtension(file, "ork"); - boolean result = FileHelper.confirmWrite(file, this) && saveAs(file); - if (result) { - MRUDesignFile opts = MRUDesignFile.getInstance(); - opts.addFile(file.getAbsolutePath()); - } - return result; - } + return false; } /** @@ -1383,11 +1369,35 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { * @return true if the file was written */ private boolean saveAsRocksim(File file) { - file = FileHelper.forceExtension(file, "rkt"); + if ( prefs.getShowRockSimFormatWarning() ) { + // Show Rocksim format warning + JPanel panel = new JPanel(new MigLayout()); + panel.add(new StyledLabel(trans.get("SaveRktWarningDialog.txt1")), "wrap"); + final JCheckBox check = new JCheckBox(trans.get("SaveRktWarningDialog.donotshow")); + check.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + prefs.setShowRockSimFormatWarning(!check.isSelected()); + } + }); + panel.add(check); + int sel = JOptionPane.showOptionDialog(null, + panel, + "", // title + JOptionPane.OK_CANCEL_OPTION, + JOptionPane.WARNING_MESSAGE, + null, // icon + null, // options + null // default option + ); + if ( sel == 1 ) { + return false; + } + } if (!FileHelper.confirmWrite(file, this)) { return false; } - + try { StorageOptions options = new StorageOptions(); options.setFileType(StorageOptions.FileType.ROCKSIM); @@ -1398,7 +1408,42 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { return false; } } - + + /** + * "Save As" action. + * + * @return true if the file was saved, false otherwise + */ + private boolean saveAsAction() { + File file = null; + + final SaveAsFileChooser chooser = SaveAsFileChooser.build(document, FileType.OPENROCKET); + + int option = chooser.showSaveDialog(BasicFrame.this); + + if (option != JFileChooser.APPROVE_OPTION) { + log.info(Markers.USER_MARKER, "User decided not to save, option=" + option); + return false; + } + + file = chooser.getSelectedFile(); + if (file == null) { + log.info(Markers.USER_MARKER, "User did not select a file"); + return false; + } + + ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(chooser.getCurrentDirectory()); + chooser.storeOptions(document.getDefaultStorageOptions()); + + file = FileHelper.forceExtension(file, "ork"); + boolean result = FileHelper.confirmWrite(file, this) && saveAsOpenRocket(file); + if (result) { + MRUDesignFile opts = MRUDesignFile.getInstance(); + opts.addFile(file.getAbsolutePath()); + } + return result; + } + /** * Perform the writing of the design to the given file in OpenRocket format. * @@ -1406,38 +1451,38 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { * * @return true if the file was written */ - private boolean saveAs(File file) { + private boolean saveAsOpenRocket(File file) { + file = FileHelper.forceExtension(file, "ork"); log.info("Saving document as " + file); - boolean saved = false; - + if (!StorageOptionChooser.verifyStorageOptions(document, this)) { // User cancelled the dialog log.info(Markers.USER_MARKER, "User cancelled saving in storage options dialog"); return false; } - - + + document.getDefaultStorageOptions().setFileType(FileType.OPENROCKET); SaveFileWorker worker = new SaveFileWorker(document, file, ROCKET_SAVER); - + if (!SwingWorkerDialog.runWorker(this, "Saving file", "Writing " + file.getName() + "...", worker)) { - + // User cancelled the save log.info(Markers.USER_MARKER, "User cancelled the save, deleting the file"); file.delete(); return false; } - + try { worker.get(); document.setFile(file); document.setSaved(true); - saved = true; setTitle(); + return true; } catch (ExecutionException e) { - + Throwable cause = e.getCause(); - + if (cause instanceof IOException) { log.warn("An I/O error occurred while saving " + file, cause); JOptionPane.showMessageDialog(this, new String[] { @@ -1447,23 +1492,23 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } else { Reflection.handleWrappedException(e); } - + } catch (InterruptedException e) { throw new BugException("EDT was interrupted", e); } - - return saved; + + return false; } - - + + private boolean closeAction() { if (!document.isSaved()) { log.info("Confirming whether to save the design"); ComponentConfigDialog.hideDialog(); int result = JOptionPane.showConfirmDialog(this, trans.get("BasicFrame.dlg.lbl1") + rocket.getName() + - trans.get("BasicFrame.dlg.lbl2") + " " + - trans.get("BasicFrame.dlg.lbl3"), + trans.get("BasicFrame.dlg.lbl2") + " " + + trans.get("BasicFrame.dlg.lbl3"), trans.get("BasicFrame.dlg.title"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); if (result == JOptionPane.YES_OPTION) { @@ -1482,14 +1527,14 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { return false; } } - + // Rocket has been saved or discarded log.debug("Disposing window"); this.dispose(); - + ComponentConfigDialog.hideDialog(); ComponentAnalysisDialog.hideDialog(); - + frames.remove(this); if (frames.isEmpty()) { log.info("Last frame closed, exiting"); @@ -1497,33 +1542,30 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { } return true; } - + public void exportDecalAction() { new ExportDecalDialog(this, document).setVisible(true); } - - + + public void printAction() { - Double rotation = rocketpanel.getFigure().getRotation(); - if (rotation == null) { - rotation = 0d; - } + double rotation = rocketpanel.getFigure().getRotation(); new PrintDialog(this, document, rotation).setVisible(true); } - + /** * Open a new design window with a basic rocket+stage. */ public static void newAction() { log.info("New action initiated"); - + OpenRocketDocument doc = OpenRocketDocumentFactory.createNewRocket(); - + BasicFrame frame = new BasicFrame(doc); frame.replaceable = true; frame.setVisible(true); } - + /** * Quit the application. Confirms saving unsaved designs. The action of File->Quit. */ @@ -1541,8 +1583,8 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { log.error("Should already have exited application"); System.exit(0); } - - + + /** * Set the title of the frame, taking into account the name of the rocket, file it * has been saved to (if any) and saved status. @@ -1551,19 +1593,19 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { File file = document.getFile(); boolean saved = document.isSaved(); String title; - + title = rocket.getName(); if (file != null) { title = title + " (" + file.getName() + ")"; } if (!saved) title = "*" + title; - + setTitle(title); } - - - + + + /** * Find a currently open BasicFrame containing the specified rocket. This method * can be used to map a Rocket to a BasicFrame from GUI methods. @@ -1581,7 +1623,7 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { log.debug("Could not find frame for rocket " + rocket); return null; } - + /** * Find a currently open document by the rocket object. This method can be used * to map a Rocket to OpenRocketDocument from GUI methods. @@ -1597,24 +1639,25 @@ public class BasicFrame extends JFrame implements PropertyChangeListener { return null; } } - + public void stateChanged(ChangeEvent e) { - JTabbedPane tabSource = (JTabbedPane) e.getSource(); - String tab = tabSource.getTitleAt(tabSource.getSelectedIndex()); - if (tab.equals(trans.get("BasicFrame.tab.Flightsim"))) { - simulationPanel.activating(); - } - } + JTabbedPane tabSource = (JTabbedPane) e.getSource(); + String tab = tabSource.getTitleAt(tabSource.getSelectedIndex()); + if (tab.equals(trans.get("BasicFrame.tab.Flightsim"))) { + simulationPanel.activating(); + } + } } class BasicFrame_changeAdapter implements javax.swing.event.ChangeListener { BasicFrame adaptee; - BasicFrame_changeAdapter(BasicFrame adaptee) { - this.adaptee = adaptee; - } - public void stateChanged(ChangeEvent e) { - adaptee.stateChanged(e); - } -} \ No newline at end of file + BasicFrame_changeAdapter(BasicFrame adaptee) { + this.adaptee = adaptee; + } + public void stateChanged(ChangeEvent e) { + adaptee.stateChanged(e); + } +} + diff --git a/swing/src/net/sf/openrocket/gui/ExportDecalDialog.java b/swing/src/net/sf/openrocket/gui/main/ExportDecalDialog.java similarity index 99% rename from swing/src/net/sf/openrocket/gui/ExportDecalDialog.java rename to swing/src/net/sf/openrocket/gui/main/ExportDecalDialog.java index 457dfa54e..b3505784d 100644 --- a/swing/src/net/sf/openrocket/gui/ExportDecalDialog.java +++ b/swing/src/net/sf/openrocket/gui/main/ExportDecalDialog.java @@ -1,4 +1,4 @@ -package net.sf.openrocket.gui; +package net.sf.openrocket.gui.main; import java.awt.Window; import java.awt.event.ActionEvent; diff --git a/swing/src/net/sf/openrocket/gui/main/SaveAsFileChooser.java b/swing/src/net/sf/openrocket/gui/main/SaveAsFileChooser.java new file mode 100644 index 000000000..80bffaaf7 --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/main/SaveAsFileChooser.java @@ -0,0 +1,113 @@ +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.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(true); + + File defaultFilename = document.getFile(); + + 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 listner = new RememberFilenamePropertyListener(); + this.addPropertyChangeListener(JFileChooser.FILE_FILTER_CHANGED_PROPERTY, listner); + this.addPropertyChangeListener(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY, listner); + this.addPropertyChangeListener(JFileChooser.SELECTED_FILES_CHANGED_PROPERTY, listner); + + 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 == event.getPropertyName()){ + if(null != event.getOldValue()){ + this.oldFileName = ((File)event.getOldValue()).getName(); + } + return; + }else if(JFileChooser.FILE_FILTER_CHANGED_PROPERTY == event.getPropertyName()){ + JFileChooser chooser = (JFileChooser)event.getSource(); + 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/StorageOptionChooser.java b/swing/src/net/sf/openrocket/gui/main/StorageOptionChooser.java similarity index 99% rename from swing/src/net/sf/openrocket/gui/StorageOptionChooser.java rename to swing/src/net/sf/openrocket/gui/main/StorageOptionChooser.java index eac318011..ad836c882 100644 --- a/swing/src/net/sf/openrocket/gui/StorageOptionChooser.java +++ b/swing/src/net/sf/openrocket/gui/main/StorageOptionChooser.java @@ -1,4 +1,4 @@ -package net.sf.openrocket.gui; +package net.sf.openrocket.gui.main; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; diff --git a/swing/src/net/sf/openrocket/gui/util/FileHelper.java b/swing/src/net/sf/openrocket/gui/util/FileHelper.java index aab125ffc..d43ceffec 100644 --- a/swing/src/net/sf/openrocket/gui/util/FileHelper.java +++ b/swing/src/net/sf/openrocket/gui/util/FileHelper.java @@ -117,7 +117,9 @@ public final class FileHelper { * @return the resulting file */ public static File forceExtension(File original, String extension) { - + if ( original == null ) { + return null; + } if (!original.getName().toLowerCase(Locale.ENGLISH).endsWith(extension.toLowerCase(Locale.ENGLISH))) { log.debug("File name does not contain extension, adding '" + extension + "'"); String name = original.getAbsolutePath();