]> git.gag.com Git - debian/openrocket/commitdiff
Added most recently used design file list to File menu.
authorrodinia814 <rodinia814@180e2498-e6e9-4542-8430-84ac67f01cd8>
Wed, 6 Jun 2012 02:22:19 +0000 (02:22 +0000)
committerrodinia814 <rodinia814@180e2498-e6e9-4542-8430-84ac67f01cd8>
Wed, 6 Jun 2012 02:22:19 +0000 (02:22 +0000)
git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@759 180e2498-e6e9-4542-8430-84ac67f01cd8

core/ChangeLog
core/resources/l10n/messages.properties
core/src/net/sf/openrocket/gui/main/BasicFrame.java
core/src/net/sf/openrocket/gui/main/MRUDesignFile.java [new file with mode: 0644]
core/src/net/sf/openrocket/gui/main/MRUDesignFileAction.java [new file with mode: 0644]
core/src/net/sf/openrocket/gui/print/components/Rule.java

index d5b3de39c60d85dfa53d9c46ce1ff47713a5f2c8..5f605187bb26ec8e796f3cb3ea0f45cd421d8b20 100644 (file)
@@ -1,6 +1,11 @@
+2012-06-05  Doug Pedrick
+
+    * Most recently used design files added to File menu.
+
 2012-05-23  Doug Pedrick
 
     * Centering ring templates can now be chosen for printing.
+    * Calibration ruler added to printed templates.
 
 2012-05-09  Kevin Ruland
 
index 2e5ba18c116122f3504d029fdb82ed0860ceb877..b783491f4ec968b0514d002c4b0f416259b8a5b5 100644 (file)
@@ -454,7 +454,7 @@ customExpression.Description = Description
 
 ! Custom expression panel
 customExpressionPanel.but.NewExpression = New expression
-customExpressionPanel.lbl.UpdateNote = You must run the simulation before data will be available for plotting. 
+customExpressionPanel.lbl.UpdateNote = You must run the simulation before data will be available for plotting.
 customExpressionPanel.lbl.CalcNote = Expressions will be calculated in the order shown.
 customExpressionPanel.lbl.CustomExpressions = Custom Expressions :
 customExpression.Units.but.ttip.Remove = Remove this expression
@@ -1005,6 +1005,8 @@ 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.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.save = Save
index b6bb3933091441fbe081fe1fe3b8cad1fc8a59f2..cfbfec2cec8f0e5440327134ada5c9ea7f414344 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;
@@ -105,68 +48,124 @@ 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
@@ -175,79 +174,79 @@ public class BasicFrame extends JFrame {
                                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 +256,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 +270,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 +288,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 +308,7 @@ public class BasicFrame extends JFrame {
                        }
                };
                tree.addMouseListener(ml);
-               
+
                // Update dialog when selection is changed
                componentSelectionModel.addTreeSelectionListener(new TreeSelectionListener() {
                        @Override
@@ -319,7 +318,7 @@ public class BasicFrame extends JFrame {
                                if (path == null)
                                        return;
                                tree.scrollPathToVisible(path);
-                               
+
                                if (!ComponentConfigDialog.isDialogVisible())
                                        return;
                                RocketComponent c = (RocketComponent) path.getLastPathComponent();
@@ -327,57 +326,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 +385,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 +397,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);
@@ -425,7 +424,7 @@ public class BasicFrame extends JFrame {
                        }
                });
                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 +439,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 +468,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 +485,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 +501,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 +516,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 +534,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 +551,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 +578,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 +611,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 +627,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,8 +650,8 @@ public class BasicFrame extends JFrame {
                        }
                });
                menu.add(item);
-               
-               
+
+
                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() {
@@ -656,27 +662,27 @@ public class BasicFrame extends JFrame {
                        }
                });
                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 +694,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 +709,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 +722,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 +738,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 +752,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 +787,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 +804,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 +813,7 @@ public class BasicFrame extends JFrame {
                                } else {
                                        return;
                                }
-                               
+
                                OpenRocketDocument doc = new OpenRocketDocument(r);
                                doc.setSaved(true);
                                BasicFrame frame = new BasicFrame(doc);
@@ -815,9 +821,9 @@ public class BasicFrame extends JFrame {
                        }
                });
                menu.add(item);
-               
-               
-               
+
+
+
                item = new JMenuItem("Create 'Iso-Haisu'");
                item.addActionListener(new ActionListener() {
                        @Override
@@ -831,8 +837,8 @@ public class BasicFrame extends JFrame {
                        }
                });
                menu.add(item);
-               
-               
+
+
                item = new JMenuItem("Create 'Big Blue'");
                item.addActionListener(new ActionListener() {
                        @Override
@@ -846,16 +852,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 +878,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 +889,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 +926,10 @@ public class BasicFrame extends JFrame {
                        }
                });
                menu.add(item);
-               
-               
+
+
                menu.addSeparator();
-               
+
                //// Exception here
                item = new JMenuItem("Exception here");
                item.addActionListener(new ActionListener() {
@@ -934,7 +940,7 @@ public class BasicFrame extends JFrame {
                        }
                });
                menu.add(item);
-               
+
                item = new JMenuItem("Exception from EDT");
                item.addActionListener(new ActionListener() {
                        @Override
@@ -950,7 +956,7 @@ public class BasicFrame extends JFrame {
                        }
                });
                menu.add(item);
-               
+
                item = new JMenuItem("Exception from other thread");
                item.addActionListener(new ActionListener() {
                        @Override
@@ -965,7 +971,7 @@ public class BasicFrame extends JFrame {
                        }
                });
                menu.add(item);
-               
+
                item = new JMenuItem("OutOfMemoryError here");
                item.addActionListener(new ActionListener() {
                        @Override
@@ -975,11 +981,11 @@ public class BasicFrame extends JFrame {
                        }
                });
                menu.add(item);
-               
-               
+
+
                menu.addSeparator();
-               
-               
+
+
                item = new JMenuItem("Test popup");
                item.addActionListener(new ActionListener() {
                        @Override
@@ -995,33 +1001,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 +1036,32 @@ 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());
                        }
                }
        }
-       
-       
+
        /**
         * Open a file based on a URL.
         * @param url           the file to open.
@@ -1063,16 +1070,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,18 +1087,18 @@ 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 {
@@ -1109,15 +1116,15 @@ public class BasicFrame extends JFrame {
                                        "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 +1134,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 +1148,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 +1160,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 +1170,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 +1228,21 @@ 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);
-               
+
                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 +1258,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 +1279,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 +1298,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 +1322,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 +1335,7 @@ public class BasicFrame extends JFrame {
                if (!FileHelper.confirmWrite(file, this)) {
                        return false;
                }
-               
+
                try {
                        new RocksimSaver().save(file, document);
                        return true;
@@ -1336,7 +1343,7 @@ public class BasicFrame extends JFrame {
                        return false;
                }
        }
-       
+
        /**
         * Perform the writing of the design to the given file in OpenRocket format.
         *
@@ -1347,25 +1354,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 +1380,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 +1392,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 +1427,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,11 +1442,11 @@ public class BasicFrame extends JFrame {
                }
                return true;
        }
-       
-       
-       
+
+
+
        /**
-        * 
+        *
         */
        public void printAction() {
         Double rotation = rocketpanel.getFigure().getRotation();
@@ -1448,13 +1455,13 @@ public class BasicFrame extends JFrame {
         }
                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
@@ -1462,13 +1469,13 @@ 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);
        }
-       
+
        /**
         * Quit the application.  Confirms saving unsaved designs.  The action of File->Quit.
         */
@@ -1486,33 +1493,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.
         */
@@ -1526,11 +1533,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.
         */
diff --git a/core/src/net/sf/openrocket/gui/main/MRUDesignFile.java b/core/src/net/sf/openrocket/gui/main/MRUDesignFile.java
new file mode 100644 (file)
index 0000000..f32d23e
--- /dev/null
@@ -0,0 +1,179 @@
+package net.sf.openrocket.gui.main;
+
+import net.sf.openrocket.startup.Application;
+import net.sf.openrocket.startup.Preferences;
+
+import javax.swing.event.EventListenerList;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implements a most-recently-used list backed by preferences.  This is modified/adapted from an example on the
+ * Netbeans wiki.
+ */
+public class MRUDesignFile {
+
+    public static final String MRU_FILE_LIST_PROPERTY = "MRUFileList";
+
+    public static final int MAX_SIZE = 9;
+
+    private List<String> mruFileList;
+
+    private static MRUDesignFile instance; // The single instance
+
+    private EventListenerList listenerList;
+
+    static {
+        instance = new MRUDesignFile();
+    }
+
+    /**
+     * Returns the single instance, creating one if it's the first time this method is called.
+     *
+     * @return The single instance.
+     */
+    public static MRUDesignFile getInstance() {
+        return instance;
+    }
+
+    /**
+     * Constructor.
+     */
+    protected MRUDesignFile() {
+        mruFileList = new ArrayList<String>(MAX_SIZE);
+        listenerList = new EventListenerList();
+        retrieve();
+    }
+
+    /**
+     * Get the current most-recently-used list of design files.
+     *
+     * @return a list that contains absolute file names
+     */
+    public List<String> getMRUFileList() {
+        return mruFileList;
+    }
+
+    /**
+     * Set the most-recently-used list to the given parameter and fire change events.
+     *
+     * @param list
+     */
+    public void setMRUFileList(List<String> list) {
+        this.mruFileList.clear();
+        this.mruFileList.addAll(list.subList(0, Math.min(list.size(), MAX_SIZE)));
+        firePropertyChange(MRU_FILE_LIST_PROPERTY, null, mruFileList);
+        store();
+    }
+
+    /**
+     * Remove a file from the MRU list and fire change events.
+     *
+     * @param absolutePath the filename to be removed
+     */
+    public void removeFile(String absolutePath) {
+        mruFileList.remove(absolutePath);
+        firePropertyChange(MRU_FILE_LIST_PROPERTY, null, mruFileList);
+        store();
+    }
+
+    /**
+     * Add a file to the MRU list and fire change events.
+     *
+     * @param absolutePath the filename to be added
+     */
+    public void addFile(String absolutePath) {
+        // remove the old
+        mruFileList.remove(absolutePath);
+
+        // add to the top
+        mruFileList.add(0, absolutePath);
+        while (mruFileList.size() > MAX_SIZE) {
+            mruFileList.remove(mruFileList.size() - 1);
+        }
+        firePropertyChange(MRU_FILE_LIST_PROPERTY, null, mruFileList);
+        store();
+    }
+
+    /**
+     * Clear all existing MRU references in the preferences backing store.
+     */
+    private void clear() {
+        Preferences prefs = getPreferences();
+
+        for (int i = 0; i < MAX_SIZE; i++) {
+            prefs.putString(MRU_FILE_LIST_PROPERTY + i, null);
+        }
+    }
+
+    /**
+     * Store the MRU list into the preferences backing store.
+     */
+    protected void store() {
+        Preferences prefs = getPreferences();
+
+        // clear the backing store
+        clear();
+
+        for (int i = 0; i < mruFileList.size(); i++) {
+            String str = mruFileList.get(i);
+            prefs.putString(MRU_FILE_LIST_PROPERTY + i, str);
+        }
+    }
+
+    /**
+     * Load the MRU list from the backing store.
+     */
+    protected void retrieve() {
+        mruFileList.clear();
+        Preferences prefs = getPreferences();
+
+        for (int i = 0; i < MAX_SIZE; i++) {
+            String str = prefs.getString(MRU_FILE_LIST_PROPERTY + i, null);
+            if (str != null) {
+                mruFileList.add(str);
+            }
+            else {
+                break;
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+        listenerList.add(PropertyChangeListener.class, listener);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+        listenerList.remove(PropertyChangeListener.class, listener);
+    }
+
+    protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
+        // Guaranteed to return a non-null array
+        Object[] listeners = listenerList.getListenerList();
+        // Process the listeners last to first, notifying
+        // those that are interested in this event
+        PropertyChangeEvent event = new PropertyChangeEvent(this, propertyName, oldValue, newValue);
+        for (int i = listeners.length - 2; i >= 0; i -= 2) {
+            if (listeners[i] == PropertyChangeListener.class) {
+                ((PropertyChangeListener) listeners[i + 1]).propertyChange(event);
+            }
+        }
+    }
+
+    /**
+     * Return the Preferences
+     *
+     * @return Preferences
+     */
+    protected final Preferences getPreferences() {
+        return Application.getPreferences();
+    }
+}
\ No newline at end of file
diff --git a/core/src/net/sf/openrocket/gui/main/MRUDesignFileAction.java b/core/src/net/sf/openrocket/gui/main/MRUDesignFileAction.java
new file mode 100644 (file)
index 0000000..8757eaa
--- /dev/null
@@ -0,0 +1,86 @@
+package net.sf.openrocket.gui.main;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.util.List;
+
+/**
+ * Implements a menu for the Most-Recently-Used Open Rocket design files.
+ */
+public final class MRUDesignFileAction extends JMenu {
+
+    /**
+     * The window to which an open design file action will be parented to (typically an instance of BasicFrame).
+     */
+    private Window parent;
+
+    /**
+     * Constructor.
+     *
+     * @param s         the I18N menu string
+     * @param theParent the window to which an open design file action will be parented to (typically an instance of
+     *                  BasicFrame).
+     */
+    public MRUDesignFileAction(String s, Window theParent) {
+        super(s);
+
+        parent = theParent;
+        MRUDesignFile opts = MRUDesignFile.getInstance();
+        opts.addPropertyChangeListener(new PropertyChangeListener() {
+            public void propertyChange(PropertyChangeEvent evt) {
+                if (!evt.getPropertyName().equals(MRUDesignFile.MRU_FILE_LIST_PROPERTY)) {
+                    return;
+                }
+                updateMenu();
+            }
+        });
+
+        updateMenu();
+    }
+
+    /**
+     * Create menu items.
+     */
+    private void updateMenu() {
+        removeAll();
+        List<String> list = MRUDesignFile.getInstance().getMRUFileList();
+        for (String name : list) {
+            Action action = createAction(name);
+            action.putValue(Action.NAME, name);
+            JMenuItem menuItem = new JMenuItem(action);
+            add(menuItem);
+        }
+    }
+
+    /**
+     * When a user clicks on one of the recently used design files, open it.
+     *
+     * @param file the design file name (absolute path)
+     *
+     * @return the action to open a design file
+     */
+    private Action createAction(String file) {
+        Action action = new AbstractAction() {
+            public void actionPerformed(ActionEvent e) {
+                String command = e.getActionCommand();
+                if (BasicFrame.open(new File(command), parent)) {
+                    MRUDesignFile.getInstance().addFile(command);
+                }
+                else {
+                    MRUDesignFile.getInstance().removeFile(command);
+                }
+            }
+        };
+
+        action.putValue(Action.ACTION_COMMAND_KEY, file);
+        return action;
+    }
+
+}
\ No newline at end of file
index 05c890f12a00bad31ceb491f85d2ba6c20d1548a..b26d65280cdf3ab9e833a6cc25811bcd532e2ccd 100644 (file)
@@ -76,10 +76,11 @@ public class Rule extends PrintableComponent {
             g.drawString("in", x + width + 6, y + width + 18);
             y += 6;
 
-            //Draw Inches first.
+            //Draw Inches first, with 1/2", 1/4", and 1/8" tick marks.
             drawVerticalRule(g2, false, inchOutSide, x, y, width, 0, div * 2, div * 4, div * 8);
             drawHorizontalRule(g2, true, inchOutSide, x, y + width, width, 0, div * 2, div * 4, div * 8);
             div = PrintUnit.MILLIMETERS.toPoints(1);  //mm increment
+            //Draw cm (10mm) and 1/2 cm (5mm) marks
             drawVerticalRule(g2, false, !inchOutSide, x, y, width, 0, 0, div * 5, div * 10);
             drawHorizontalRule(g2, true, !inchOutSide, x, y + width, width, 0, 0, div * 5, div * 10);
         }