Merge commit '42b2e5ca519766e37ce6941ba4faecc9691cc403' into upstream
[debian/openrocket] / core / src / net / sf / openrocket / gui / main / BasicFrame.java
index c05c1ba34dd82b9a39ded5737a5d2a9336445bb3..bdfcbd4a0a8a9218acacbf1c539b8d42bdc04005 100644 (file)
@@ -1,62 +1,5 @@
 package net.sf.openrocket.gui.main;
 
-import java.awt.Dimension;
-import java.awt.Font;
-import java.awt.Toolkit;
-import java.awt.Window;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.KeyEvent;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.net.URLDecoder;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.concurrent.ExecutionException;
-
-import javax.swing.Action;
-import javax.swing.BorderFactory;
-import javax.swing.InputMap;
-import javax.swing.JButton;
-import javax.swing.JComponent;
-import javax.swing.JFileChooser;
-import javax.swing.JFrame;
-import javax.swing.JMenu;
-import javax.swing.JMenuBar;
-import javax.swing.JMenuItem;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JPopupMenu;
-import javax.swing.JScrollPane;
-import javax.swing.JSpinner;
-import javax.swing.JSplitPane;
-import javax.swing.JTabbedPane;
-import javax.swing.JTextField;
-import javax.swing.KeyStroke;
-import javax.swing.ListSelectionModel;
-import javax.swing.ScrollPaneConstants;
-import javax.swing.SwingUtilities;
-import javax.swing.border.BevelBorder;
-import javax.swing.border.TitledBorder;
-import javax.swing.event.TreeSelectionEvent;
-import javax.swing.event.TreeSelectionListener;
-import javax.swing.tree.DefaultTreeSelectionModel;
-import javax.swing.tree.TreePath;
-import javax.swing.tree.TreeSelectionModel;
-
 import net.miginfocom.swing.MigLayout;
 import net.sf.openrocket.aerodynamics.WarningSet;
 import net.sf.openrocket.document.OpenRocketDocument;
@@ -68,6 +11,7 @@ import net.sf.openrocket.file.openrocket.OpenRocketSaver;
 import net.sf.openrocket.file.rocksim.export.RocksimSaver;
 import net.sf.openrocket.gui.StorageOptionChooser;
 import net.sf.openrocket.gui.configdialog.ComponentConfigDialog;
+import net.sf.openrocket.gui.customexpression.CustomExpressionDialog;
 import net.sf.openrocket.gui.dialogs.AboutDialog;
 import net.sf.openrocket.gui.dialogs.BugReportDialog;
 import net.sf.openrocket.gui.dialogs.ComponentAnalysisDialog;
@@ -105,149 +49,194 @@ import net.sf.openrocket.util.MemoryManagement.MemoryData;
 import net.sf.openrocket.util.Reflection;
 import net.sf.openrocket.util.TestRockets;
 
+import javax.swing.Action;
+import javax.swing.BorderFactory;
+import javax.swing.InputMap;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.JScrollPane;
+import javax.swing.JSpinner;
+import javax.swing.JSplitPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTextField;
+import javax.swing.KeyStroke;
+import javax.swing.ListSelectionModel;
+import javax.swing.ScrollPaneConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.border.BevelBorder;
+import javax.swing.border.TitledBorder;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.tree.DefaultTreeSelectionModel;
+import javax.swing.tree.TreePath;
+import javax.swing.tree.TreeSelectionModel;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Toolkit;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
 public class BasicFrame extends JFrame {
        private static final LogHelper log = Application.getLogger();
-       
+
        /**
         * The RocketLoader instance used for loading all rocket designs.
         */
        private static final RocketLoader ROCKET_LOADER = new GeneralRocketLoader();
-       
+
        private static final RocketSaver ROCKET_SAVER = new OpenRocketSaver();
-       
+
        private static final Translator trans = Application.getTranslator();
-       
+
        public static final int COMPONENT_TAB = 0;
        public static final int SIMULATION_TAB = 1;
-       
-       
+
+
        /**
         * List of currently open frames.  When the list goes empty
         * it is time to exit the application.
         */
        private static final ArrayList<BasicFrame> frames = new ArrayList<BasicFrame>();
-       
-       
+
+
        /**
         * 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;
-       
-       
-       
-       
+
+
+
+
        /**
         * Sole constructor.  Creates a new frame based on the supplied document
         * and adds it to the current frames list.
-        * 
+        *
         * @param document      the document to show.
         */
        public BasicFrame(OpenRocketDocument document) {
                log.debug("Instantiating new BasicFrame");
-               
+
                this.document = document;
                this.rocket = document.getRocket();
                this.rocket.getDefaultConfiguration().setAllStages();
-               
-               
-               // Set replaceable flag to false at first modification
-               rocket.addComponentChangeListener(new ComponentChangeListener() {
-                       @Override
-                       public void componentChanged(ComponentChangeEvent e) {
-                               replaceable = false;
-                               BasicFrame.this.rocket.removeComponentChangeListener(this);
-                       }
-               });
-               
-               
+
                // 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 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         
+
+               // 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
                tabbedPane.addTab(trans.get("BasicFrame.tab.Rocketdesign"), null, designTab());
                //// Flight simulations
                tabbedPane.addTab(trans.get("BasicFrame.tab.Flightsim"), null, simulationPanel);
-               
+
                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);
@@ -257,12 +246,12 @@ public class BasicFrame extends JFrame {
                                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
@@ -271,15 +260,15 @@ public class BasicFrame extends JFrame {
        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]"));
-               
+
                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, ActionEvent.CTRL_MASK), null);
@@ -289,9 +278,9 @@ public class BasicFrame extends JFrame {
                im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK), null);
                im.put(KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.CTRL_MASK), null);
                im.put(KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK), null);
-               
-               
-               
+
+
+
                // Double-click opens config dialog
                MouseListener ml = new MouseAdapter() {
                        @Override
@@ -309,7 +298,7 @@ public class BasicFrame extends JFrame {
                        }
                };
                tree.addMouseListener(ml);
-               
+
                // Update dialog when selection is changed
                componentSelectionModel.addTreeSelectionListener(new TreeSelectionListener() {
                        @Override
@@ -319,7 +308,7 @@ public class BasicFrame extends JFrame {
                                if (path == null)
                                        return;
                                tree.scrollPathToVisible(path);
-                               
+
                                if (!ComponentConfigDialog.isDialogVisible())
                                        return;
                                RocketComponent c = (RocketComponent) path.getLastPathComponent();
@@ -327,57 +316,57 @@ public class BasicFrame extends JFrame {
                                                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 <code>null</code> if none selected.
         */
@@ -386,11 +375,11 @@ public class BasicFrame extends JFrame {
                if (path == null)
                        return null;
                tree.scrollPathToVisible(path);
-               
+
                return (RocketComponent) path.getLastPathComponent();
        }
-       
-       
+
+
        /**
         * Creates the menu for the window.
         */
@@ -398,15 +387,15 @@ public class BasicFrame extends JFrame {
                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 
+
+               //// New
                item = new JMenuItem(trans.get("main.menu.file.new"), KeyEvent.VK_N);
                item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK));
                item.setMnemonic(KeyEvent.VK_N);
@@ -418,14 +407,11 @@ public class BasicFrame extends JFrame {
                        public void actionPerformed(ActionEvent e) {
                                log.user("New... selected");
                                newAction();
-                               if (replaceable) {
-                                       log.info("Closing previous window");
-                                       closeAction();
-                               }
+                               closeIfReplaceable();
                        }
                });
                menu.add(item);
-               
+
                //// Open...
                item = new JMenuItem(trans.get("main.menu.file.open"), KeyEvent.VK_O);
                item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.CTRL_MASK));
@@ -440,7 +426,14 @@ public class BasicFrame extends JFrame {
                        }
                });
                menu.add(item);
-               
+
+               //// Open Recent...
+               item = new MRUDesignFileAction(trans.get("main.menu.file.openRecent"), this);
+               //// Open a recent rocket design
+               item.getAccessibleContext().setAccessibleDescription(trans.get("BasicFrame.item.Openrecentrocketdesign"));
+               item.setIcon(Icons.FILE_OPEN);
+               menu.add(item);
+
                //// Open example...
                item = new JMenuItem(trans.get("main.menu.file.openExample"));
                //// Open an example rocket design
@@ -462,9 +455,9 @@ public class BasicFrame extends JFrame {
                        }
                });
                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, ActionEvent.CTRL_MASK));
@@ -479,7 +472,7 @@ public class BasicFrame extends JFrame {
                        }
                });
                menu.add(item);
-               
+
                //// Save as...
                item = new JMenuItem(trans.get("main.menu.file.saveAs"), KeyEvent.VK_A);
                item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
@@ -495,7 +488,7 @@ public class BasicFrame extends JFrame {
                        }
                });
                menu.add(item);
-               
+
                //// Print...
                item = new JMenuItem(trans.get("main.menu.file.print"), KeyEvent.VK_P);
                item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, ActionEvent.CTRL_MASK));
@@ -510,10 +503,10 @@ public class BasicFrame extends JFrame {
                        }
                });
                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, ActionEvent.CTRL_MASK));
@@ -528,9 +521,9 @@ public class BasicFrame extends JFrame {
                        }
                });
                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, ActionEvent.CTRL_MASK));
@@ -545,26 +538,26 @@ public class BasicFrame extends JFrame {
                        }
                });
                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"));
                menubar.add(menu);
-               
-               
+
+
                Action action = UndoRedoAction.newUndoAction(document);
                item = new JMenuItem(action);
                item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, ActionEvent.CTRL_MASK));
                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, ActionEvent.CTRL_MASK));
@@ -572,26 +565,26 @@ public class BasicFrame extends JFrame {
                //// 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"));
@@ -605,9 +598,9 @@ public class BasicFrame extends JFrame {
                        }
                });
                menu.add(item);
-               
-               
-               
+
+
+
                //// Preferences
                item = new JMenuItem(trans.get("main.menu.edit.preferences"));
                item.setIcon(Icons.PREFERENCES);
@@ -621,17 +614,17 @@ public class BasicFrame extends JFrame {
                        }
                });
                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
@@ -644,39 +637,49 @@ public class BasicFrame extends JFrame {
                        }
                });
                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"));
                item.addActionListener(new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
-                               log.user("Rocket optimization selected");
-                               new GeneralOptimizationDialog(document, BasicFrame.this).setVisible(true);
+                               log.user("Rocket optimization selected");
+                               new GeneralOptimizationDialog(document, BasicFrame.this).setVisible(true);
+                       }
+               });
+               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"));
+               item.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               log.debug("Custom expressions selected");
+                               new CustomExpressionDialog(document, BasicFrame.this).setVisible(true);
                        }
                });
                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"));
@@ -688,9 +691,9 @@ public class BasicFrame extends JFrame {
                        }
                });
                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);
@@ -703,7 +706,7 @@ public class BasicFrame extends JFrame {
                        }
                });
                menu.add(item);
-               
+
                //// Debug log
                item = new JMenuItem(trans.get("main.menu.help.debugLog"));
                item.setIcon(Icons.HELP_DEBUG_LOG);
@@ -716,10 +719,10 @@ public class BasicFrame extends JFrame {
                        }
                });
                menu.add(item);
-               
+
                menu.addSeparator();
-               
-               
+
+
                //// License
                item = new JMenuItem(trans.get("main.menu.help.license"), KeyEvent.VK_L);
                item.setIcon(Icons.HELP_LICENSE);
@@ -732,8 +735,8 @@ public class BasicFrame extends JFrame {
                        }
                });
                menu.add(item);
-               
-               
+
+
                //// About
                item = new JMenuItem(trans.get("main.menu.help.about"), KeyEvent.VK_A);
                item.setIcon(Icons.HELP_ABOUT);
@@ -746,24 +749,24 @@ public class BasicFrame extends JFrame {
                        }
                });
                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() {
@@ -781,9 +784,9 @@ public class BasicFrame extends JFrame {
                        }
                });
                menu.add(item);
-               
+
                menu.addSeparator();
-               
+
                //// Create test rocket
                item = new JMenuItem("Create test rocket");
                item.addActionListener(new ActionListener() {
@@ -798,7 +801,7 @@ public class BasicFrame extends JFrame {
                                                JOptionPane.QUESTION_MESSAGE, null, new Object[] {
                                                                "Random", "OK"
                                                }, "OK");
-                               
+
                                Rocket r;
                                if (sel == 0) {
                                        r = new TestRockets(null).makeTestRocket();
@@ -807,7 +810,7 @@ public class BasicFrame extends JFrame {
                                } else {
                                        return;
                                }
-                               
+
                                OpenRocketDocument doc = new OpenRocketDocument(r);
                                doc.setSaved(true);
                                BasicFrame frame = new BasicFrame(doc);
@@ -815,9 +818,9 @@ public class BasicFrame extends JFrame {
                        }
                });
                menu.add(item);
-               
-               
-               
+
+
+
                item = new JMenuItem("Create 'Iso-Haisu'");
                item.addActionListener(new ActionListener() {
                        @Override
@@ -831,8 +834,8 @@ public class BasicFrame extends JFrame {
                        }
                });
                menu.add(item);
-               
-               
+
+
                item = new JMenuItem("Create 'Big Blue'");
                item.addActionListener(new ActionListener() {
                        @Override
@@ -846,16 +849,16 @@ public class BasicFrame extends JFrame {
                        }
                });
                menu.add(item);
-               
+
                menu.addSeparator();
-               
-               
+
+
                item = new JMenuItem("Memory statistics");
                item.addActionListener(new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
                                log.user("Memory statistics selected");
-                               
+
                                // Get discarded but remaining objects (this also runs System.gc multiple times)
                                List<MemoryData> objects = MemoryManagement.getRemainingCollectableObjects();
                                StringBuilder sb = new StringBuilder();
@@ -872,7 +875,7 @@ public class BasicFrame extends JFrame {
                                        o = null;
                                }
                                sb.append("Total: " + count);
-                               
+
                                // Get basic memory stats
                                System.gc();
                                long max = Runtime.getRuntime().maxMemory();
@@ -883,14 +886,14 @@ public class BasicFrame extends JFrame {
                                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() {
@@ -920,10 +923,10 @@ public class BasicFrame extends JFrame {
                        }
                });
                menu.add(item);
-               
-               
+
+
                menu.addSeparator();
-               
+
                //// Exception here
                item = new JMenuItem("Exception here");
                item.addActionListener(new ActionListener() {
@@ -934,7 +937,7 @@ public class BasicFrame extends JFrame {
                        }
                });
                menu.add(item);
-               
+
                item = new JMenuItem("Exception from EDT");
                item.addActionListener(new ActionListener() {
                        @Override
@@ -950,7 +953,7 @@ public class BasicFrame extends JFrame {
                        }
                });
                menu.add(item);
-               
+
                item = new JMenuItem("Exception from other thread");
                item.addActionListener(new ActionListener() {
                        @Override
@@ -965,7 +968,7 @@ public class BasicFrame extends JFrame {
                        }
                });
                menu.add(item);
-               
+
                item = new JMenuItem("OutOfMemoryError here");
                item.addActionListener(new ActionListener() {
                        @Override
@@ -975,11 +978,11 @@ public class BasicFrame extends JFrame {
                        }
                });
                menu.add(item);
-               
-               
+
+
                menu.addSeparator();
-               
-               
+
+
                item = new JMenuItem("Test popup");
                item.addActionListener(new ActionListener() {
                        @Override
@@ -995,33 +998,33 @@ public class BasicFrame extends JFrame {
                        }
                });
                menu.add(item);
-               
-               
-               
-               
+
+
+
+
                return menu;
        }
-       
-       
+
+
        /**
         * Select the tab on the main pane.
-        * 
+        *
         * @param tab   one of {@link #COMPONENT_TAB} or {@link #SIMULATION_TAB}.
         */
        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.setFileSelectionMode(JFileChooser.FILES_ONLY);
                chooser.setMultiSelectionEnabled(true);
                chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory());
@@ -1030,31 +1033,33 @@ public class BasicFrame extends JFrame {
                        log.user("Decided not to open files, option=" + option);
                        return;
                }
-               
+
                ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(chooser.getCurrentDirectory());
-               
+
                File[] files = chooser.getSelectedFiles();
                log.user("Opening files " + Arrays.toString(files));
-               
+
                for (File file : files) {
                        log.info("Opening file: " + file);
                        if (open(file, this)) {
-                               
-                               // 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();
-                                       replaceable = false;
-                               }
+                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.
@@ -1063,16 +1068,16 @@ public class BasicFrame extends JFrame {
         */
        private static boolean open(URL url, BasicFrame parent) {
                String filename = null;
-               
+
                // First figure out the file name from the URL
-               
+
                // Try using URI.getPath();
                try {
                        URI uri = url.toURI();
                        filename = uri.getPath();
                } catch (URISyntaxException ignore) {
                }
-               
+
                // Try URL-decoding the URL
                if (filename == null) {
                        try {
@@ -1080,44 +1085,38 @@ public class BasicFrame extends JFrame {
                        } catch (UnsupportedEncodingException ignore) {
                        }
                }
-               
+
                // Last resort
                if (filename == null) {
                        filename = "";
                }
-               
+
                // Remove path from filename
                if (filename.lastIndexOf('/') >= 0) {
                        filename = filename.substring(filename.lastIndexOf('/') + 1);
                }
-               
-               
+
+
                // Open the file
                log.info("Opening file from url=" + url + " filename=" + filename);
                try {
                        InputStream is = url.openStream();
-                       if (open(is, filename, parent)) {
-                               // Close previous window if replacing
-                               if (parent.replaceable && parent.document.isSaved()) {
-                                       parent.closeAction();
-                                       parent.replaceable = false;
-                               }
-                       }
+                       open(is, filename, parent);
                } catch (IOException e) {
                        log.warn("Error opening file" + e);
                        JOptionPane.showMessageDialog(parent,
                                        "An error occurred while opening the file " + filename,
                                        "Error loading file", JOptionPane.ERROR_MESSAGE);
                }
-               
+
                return false;
        }
-       
-       
+
+
        /**
         * Open the specified file from an InputStream in a new design frame.  If an error
         * occurs, an error dialog is shown and <code>false</code> is returned.
-        * 
+        *
         * @param stream        the stream to load from.
         * @param filename      the file name to display in dialogs (not set to the document).
         * @param parent        the parent component for which a progress dialog is opened.
@@ -1127,12 +1126,12 @@ public class BasicFrame extends JFrame {
                OpenFileWorker worker = new OpenFileWorker(stream, ROCKET_LOADER);
                return open(worker, filename, null, parent);
        }
-       
-       
+
+
        /**
         * Open the specified file in a new design frame.  If an error occurs, an error
         * dialog is shown and <code>false</code> is returned.
-        * 
+        *
         * @param file          the file to open.
         * @param parent        the parent component for which a progress dialog is opened.
         * @return                      whether the file was successfully loaded and opened.
@@ -1141,11 +1140,11 @@ public class BasicFrame extends JFrame {
                OpenFileWorker worker = new OpenFileWorker(file, ROCKET_LOADER);
                return open(worker, file.getName(), file, parent);
        }
-       
-       
+
+
        /**
         * Open the specified file using the provided worker.
-        * 
+        *
         * @param worker        the OpenFileWorker that loads the file.
         * @param filename      the file name to display in dialogs.
         * @param file          the File to set the document to (may be null).
@@ -1153,9 +1152,9 @@ public class BasicFrame extends JFrame {
         * @return
         */
        private static boolean open(OpenFileWorker worker, String filename, File file, Window parent) {
-               
+
                MotorDatabaseLoadingDialog.check(parent);
-               
+
                // Open the file in a Swing worker thread
                log.info("Starting OpenFileWorker");
                if (!SwingWorkerDialog.runWorker(parent, "Opening file", "Reading " + filename + "...", worker)) {
@@ -1163,50 +1162,50 @@ public class BasicFrame extends JFrame {
                        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: " + filename,
                                                "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 '" + filename + "': "
                                                                + cause.getMessage(),
                                                "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()) {
@@ -1221,21 +1220,24 @@ public class BasicFrame extends JFrame {
                                        //// Warnings while opening file
                                        trans.get("BasicFrame.WarningDialog.title"), warnings);
                }
-               
-               
+
+
                // Set document state
                doc.setFile(file);
                doc.setSaved(true);
-               
-               
+
+
                // 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();
+               }
                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
@@ -1251,13 +1253,13 @@ public class BasicFrame extends JFrame {
                        return saveAsAction();
                }
                log.info("Saving document to " + file);
-               
+
                if (FileHelper.ROCKSIM_DESIGN_FILTER.accept(file)) {
                        return saveAsRocksim(file);
                }
                return saveAs(file);
        }
-       
+
        /**
         * "Save As" action.
         *
@@ -1272,13 +1274,13 @@ public class BasicFrame extends JFrame {
         */
        private boolean saveAsAction() {
                File file = null;
-               
+
                StorageOptionChooser storageChooser =
                                new StorageOptionChooser(document, document.getDefaultStorageOptions());
                final JFileChooser chooser = new JFileChooser();
                chooser.addChoosableFileFilter(FileHelper.OPENROCKET_DESIGN_FILTER);
                chooser.addChoosableFileFilter(FileHelper.ROCKSIM_DESIGN_FILTER);
-               
+
                //Force the file filter to match the file extension that was opened.  Will default to OR if the file is null.
                if (FileHelper.ROCKSIM_DESIGN_FILTER.accept(document.getFile())) {
                        chooser.setFileFilter(FileHelper.ROCKSIM_DESIGN_FILTER);
@@ -1291,22 +1293,22 @@ public class BasicFrame extends JFrame {
                if (document.getFile() != null) {
                        chooser.setSelectedFile(document.getFile());
                }
-               
+
                int option = chooser.showSaveDialog(BasicFrame.this);
                if (option != JFileChooser.APPROVE_OPTION) {
                        log.user("User decided not to save, option=" + option);
                        return false;
                }
-               
+
                file = chooser.getSelectedFile();
                if (file == null) {
                        log.user("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)) {
                        return saveAsRocksim(file);
                }
@@ -1315,7 +1317,7 @@ public class BasicFrame extends JFrame {
                        return FileHelper.confirmWrite(file, this) && saveAs(file);
                }
        }
-       
+
        /**
         * Perform the writing of the design to the given file in Rocksim format.
         *
@@ -1328,7 +1330,7 @@ public class BasicFrame extends JFrame {
                if (!FileHelper.confirmWrite(file, this)) {
                        return false;
                }
-               
+
                try {
                        new RocksimSaver().save(file, document);
                        return true;
@@ -1336,7 +1338,7 @@ public class BasicFrame extends JFrame {
                        return false;
                }
        }
-       
+
        /**
         * Perform the writing of the design to the given file in OpenRocket format.
         *
@@ -1347,25 +1349,25 @@ public class BasicFrame extends JFrame {
        private boolean saveAs(File file) {
                log.info("Saving document as " + file);
                boolean saved = false;
-               
+
                if (!StorageOptionChooser.verifyStorageOptions(document, this)) {
                        // User cancelled the dialog
                        log.user("User cancelled saving in storage options dialog");
                        return false;
                }
-               
-               
+
+
                SaveFileWorker worker = new SaveFileWorker(document, file, ROCKET_SAVER);
-               
+
                if (!SwingWorkerDialog.runWorker(this, "Saving file",
                                "Writing " + file.getName() + "...", worker)) {
-                       
+
                        // User cancelled the save
                        log.user("User cancelled the save, deleting the file");
                        file.delete();
                        return false;
                }
-               
+
                try {
                        worker.get();
                        document.setFile(file);
@@ -1373,9 +1375,9 @@ public class BasicFrame extends JFrame {
                        saved = true;
                        setTitle();
                } 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[] {
@@ -1385,15 +1387,15 @@ public class BasicFrame extends JFrame {
                        } else {
                                Reflection.handleWrappedException(e);
                        }
-                       
+
                } catch (InterruptedException e) {
                        throw new BugException("EDT was interrupted", e);
                }
-               
+
                return saved;
        }
-       
-       
+
+
        private boolean closeAction() {
                if (!document.isSaved()) {
                        log.info("Confirming whether to save the design");
@@ -1420,14 +1422,14 @@ public class BasicFrame extends JFrame {
                                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");
@@ -1435,22 +1437,26 @@ public class BasicFrame extends JFrame {
                }
                return true;
        }
-       
-       
-       
+
+
+
        /**
-        * 
+        *
         */
        public void printAction() {
-               new PrintDialog(this, document).setVisible(true);
+        Double rotation = rocketpanel.getFigure().getRotation();
+        if (rotation == null) {
+            rotation = 0d;
+        }
+               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");
-               
+
                Rocket rocket = new Rocket();
                Stage stage = new Stage();
                //// Sustainer
@@ -1458,13 +1464,14 @@ public class BasicFrame extends JFrame {
                rocket.addChild(stage);
                OpenRocketDocument doc = new OpenRocketDocument(rocket);
                doc.setSaved(true);
-               
+
                BasicFrame frame = new BasicFrame(doc);
                frame.replaceable = true;
                frame.setVisible(true);
-               ComponentConfigDialog.showDialog(frame, doc, rocket);
+               // kruland commented this out - I don't like it.
+               //ComponentConfigDialog.showDialog(frame, doc, rocket);
        }
-       
+
        /**
         * Quit the application.  Confirms saving unsaved designs.  The action of File->Quit.
         */
@@ -1482,33 +1489,33 @@ public class BasicFrame extends JFrame {
                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 
+        * 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.
         */
        private void setTitle() {
                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.
-        * 
+        *
         * @param rocket the Rocket.
         * @return               the corresponding BasicFrame, or <code>null</code> if none found.
         */
@@ -1522,11 +1529,11 @@ public class BasicFrame extends JFrame {
                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.
-        * 
+        *
         * @param rocket the Rocket.
         * @return               the corresponding OpenRocketDocument, or <code>null</code> if not found.
         */