From: rodinia814 Date: Wed, 6 Jun 2012 02:22:19 +0000 (+0000) Subject: Added most recently used design file list to File menu. X-Git-Tag: upstream/12.09^2~199 X-Git-Url: https://git.gag.com/?a=commitdiff_plain;h=b14d316427e1f27271dcd34f673b14db308112f4;p=debian%2Fopenrocket Added most recently used design file list to File menu. git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@759 180e2498-e6e9-4542-8430-84ac67f01cd8 --- diff --git a/core/ChangeLog b/core/ChangeLog index d5b3de39..5f605187 100644 --- a/core/ChangeLog +++ b/core/ChangeLog @@ -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 diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 2e5ba18c..b783491f 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -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 diff --git a/core/src/net/sf/openrocket/gui/main/BasicFrame.java b/core/src/net/sf/openrocket/gui/main/BasicFrame.java index b6bb3933..cfbfec2c 100644 --- a/core/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/core/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -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 frames = new ArrayList(); - - + + /** * Whether "New" and "Open" should replace this frame. * Should be set to false on the first rocket modification. */ private boolean replaceable = false; - - - + + + private final OpenRocketDocument document; private final Rocket rocket; - + private JTabbedPane tabbedPane; private RocketPanel rocketpanel; private ComponentTree tree = null; - + private final DocumentSelectionModel selectionModel; private final TreeSelectionModel componentSelectionModel; private final ListSelectionModel simulationSelectionModel; - + /** Actions available for rocket modifications */ private final RocketActions actions; - - - - + + + + /** * 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 null 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 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 false 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 false 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 null 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 null 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 index 00000000..f32d23e8 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/main/MRUDesignFile.java @@ -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 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(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 getMRUFileList() { + return mruFileList; + } + + /** + * Set the most-recently-used list to the given parameter and fire change events. + * + * @param list + */ + public void setMRUFileList(List 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 index 00000000..8757eaa2 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/main/MRUDesignFileAction.java @@ -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 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 diff --git a/core/src/net/sf/openrocket/gui/print/components/Rule.java b/core/src/net/sf/openrocket/gui/print/components/Rule.java index 05c890f1..b26d6528 100644 --- a/core/src/net/sf/openrocket/gui/print/components/Rule.java +++ b/core/src/net/sf/openrocket/gui/print/components/Rule.java @@ -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); }