X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=core%2Fsrc%2Fnet%2Fsf%2Fopenrocket%2Fgui%2Fmain%2FBasicFrame.java;h=bdfcbd4a0a8a9218acacbf1c539b8d42bdc04005;hb=31cba834b3fb4e1043b5e897f5a2dc029a837faf;hp=b4a3833878bcb20b1f6169ccd166713113d9db54;hpb=604378e1863d68801aadf5b1376a3f544237b3e6;p=debian%2Fopenrocket diff --git a/core/src/net/sf/openrocket/gui/main/BasicFrame.java b/core/src/net/sf/openrocket/gui/main/BasicFrame.java index b4a38338..bdfcbd4a 100644 --- a/core/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/core/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -11,6 +11,7 @@ import net.sf.openrocket.file.openrocket.OpenRocketSaver; import net.sf.openrocket.file.rocksim.export.RocksimSaver; import net.sf.openrocket.gui.StorageOptionChooser; import net.sf.openrocket.gui.configdialog.ComponentConfigDialog; +import net.sf.openrocket.gui.customexpression.CustomExpressionDialog; import net.sf.openrocket.gui.dialogs.AboutDialog; import net.sf.openrocket.gui.dialogs.BugReportDialog; import net.sf.openrocket.gui.dialogs.ComponentAnalysisDialog; @@ -48,7 +49,28 @@ import net.sf.openrocket.util.MemoryManagement.MemoryData; import net.sf.openrocket.util.Reflection; import net.sf.openrocket.util.TestRockets; -import javax.swing.*; +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; @@ -56,7 +78,10 @@ import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultTreeSelectionModel; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; -import java.awt.*; +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; @@ -82,99 +107,89 @@ 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 - public void componentChanged(ComponentChangeEvent e) { - replaceable = false; - BasicFrame.this.rocket.removeComponentChangeListener(this); - } - }); - // Create the component tree selection model that will be used componentSelectionModel = new DefaultTreeSelectionModel(); componentSelectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); - + // Obtain the simulation selection model that will be used SimulationPanel simulationPanel = new SimulationPanel(document); simulationSelectionModel = simulationPanel.getSimulationListSelectionModel(); - + // Combine into a DocumentSelectionModel selectionModel = new DocumentSelectionModel(document); selectionModel.attachComponentTreeSelectionModel(componentSelectionModel); selectionModel.attachSimulationListSelectionModel(simulationSelectionModel); - + actions = new RocketActions(document, selectionModel, this); - + log.debug("Constructing the BasicFrame UI"); - - // The main vertical split pane + + // The main vertical split pane JSplitPane vertical = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true); vertical.setResizeWeight(0.5); this.add(vertical); - + // The top tabbed pane tabbedPane = new JTabbedPane(); @@ -182,21 +197,21 @@ public class BasicFrame extends JFrame { 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 @@ -204,24 +219,24 @@ public class BasicFrame extends JFrame { 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); @@ -231,12 +246,12 @@ public class BasicFrame extends JFrame { closeAction(); } }); - + frames.add(this); log.debug("BasicFrame instantiation complete"); } - - + + /** * Construct the "Rocket design" tab. This contains a horizontal split pane * with the left component the design tree and the right component buttons @@ -245,15 +260,15 @@ public class BasicFrame extends JFrame { private JComponent designTab() { JSplitPane horizontal = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true); horizontal.setResizeWeight(0.5); - + // Upper-left segment, component tree - + JPanel panel = new JPanel(new MigLayout("fill, flowy", "", "[grow]")); - + tree = new ComponentTree(document); tree.setSelectionModel(componentSelectionModel); - + // Remove JTree key events that interfere with menu accelerators InputMap im = SwingUtilities.getUIInputMap(tree, JComponent.WHEN_FOCUSED); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, ActionEvent.CTRL_MASK), null); @@ -263,7 +278,7 @@ 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 @@ -283,7 +298,7 @@ public class BasicFrame extends JFrame { } }; tree.addMouseListener(ml); - + // Update dialog when selection is changed componentSelectionModel.addTreeSelectionListener(new TreeSelectionListener() { @Override @@ -293,7 +308,7 @@ public class BasicFrame extends JFrame { if (path == null) return; tree.scrollPathToVisible(path); - + if (!ComponentConfigDialog.isDialogVisible()) return; RocketComponent c = (RocketComponent) path.getLastPathComponent(); @@ -301,56 +316,56 @@ 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. @@ -360,11 +375,11 @@ public class BasicFrame extends JFrame { if (path == null) return null; tree.scrollPathToVisible(path); - + return (RocketComponent) path.getLastPathComponent(); } - - + + /** * Creates the menu for the window. */ @@ -372,15 +387,15 @@ public class BasicFrame extends JFrame { JMenuBar menubar = new JMenuBar(); JMenu menu; JMenuItem item; - + //// File menu = new JMenu(trans.get("main.menu.file")); menu.setMnemonic(KeyEvent.VK_F); //// File-handling related tasks menu.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.desc")); menubar.add(menu); - - //// New + + //// New item = new JMenuItem(trans.get("main.menu.file.new"), KeyEvent.VK_N); item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK)); item.setMnemonic(KeyEvent.VK_N); @@ -392,14 +407,11 @@ public class BasicFrame extends JFrame { public void actionPerformed(ActionEvent e) { log.user("New... selected"); newAction(); - if (replaceable) { - log.info("Closing previous window"); - closeAction(); - } + closeIfReplaceable(); } }); menu.add(item); - + //// Open... item = new JMenuItem(trans.get("main.menu.file.open"), KeyEvent.VK_O); item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.CTRL_MASK)); @@ -414,7 +426,14 @@ public class BasicFrame extends JFrame { } }); menu.add(item); - + + //// Open Recent... + item = new MRUDesignFileAction(trans.get("main.menu.file.openRecent"), this); + //// Open a recent rocket design + item.getAccessibleContext().setAccessibleDescription(trans.get("BasicFrame.item.Openrecentrocketdesign")); + item.setIcon(Icons.FILE_OPEN); + menu.add(item); + //// Open example... item = new JMenuItem(trans.get("main.menu.file.openExample")); //// Open an example rocket design @@ -436,9 +455,9 @@ public class BasicFrame extends JFrame { } }); menu.add(item); - + menu.addSeparator(); - + //// Save item = new JMenuItem(trans.get("main.menu.file.save"), KeyEvent.VK_S); item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK)); @@ -453,7 +472,7 @@ public class BasicFrame extends JFrame { } }); menu.add(item); - + //// Save as... item = new JMenuItem(trans.get("main.menu.file.saveAs"), KeyEvent.VK_A); item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, @@ -469,7 +488,7 @@ public class BasicFrame extends JFrame { } }); menu.add(item); - + //// Print... item = new JMenuItem(trans.get("main.menu.file.print"), KeyEvent.VK_P); item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, ActionEvent.CTRL_MASK)); @@ -484,10 +503,10 @@ public class BasicFrame extends JFrame { } }); menu.add(item); - + menu.addSeparator(); - + //// Close item = new JMenuItem(trans.get("main.menu.file.close"), KeyEvent.VK_C); item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, ActionEvent.CTRL_MASK)); @@ -502,9 +521,9 @@ public class BasicFrame extends JFrame { } }); menu.add(item); - + menu.addSeparator(); - + //// Quit item = new JMenuItem(trans.get("main.menu.file.quit"), KeyEvent.VK_Q); item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, ActionEvent.CTRL_MASK)); @@ -519,7 +538,7 @@ public class BasicFrame extends JFrame { } }); menu.add(item); - + //// Edit @@ -528,7 +547,7 @@ public class BasicFrame extends JFrame { //// Rocket editing menu.getAccessibleContext().setAccessibleDescription(trans.get("BasicFrame.menu.Rocketedt")); menubar.add(menu); - + Action action = UndoRedoAction.newUndoAction(document); item = new JMenuItem(action); @@ -536,7 +555,7 @@ public class BasicFrame extends JFrame { 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); @@ -546,24 +565,24 @@ 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")); @@ -579,7 +598,7 @@ public class BasicFrame extends JFrame { } }); menu.add(item); - + //// Preferences @@ -595,7 +614,7 @@ public class BasicFrame extends JFrame { } }); menu.add(item); - + @@ -605,7 +624,7 @@ public class BasicFrame extends JFrame { //// 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 @@ -618,8 +637,8 @@ public class BasicFrame extends JFrame { } }); menu.add(item); - + //// Optimize item = new JMenuItem(trans.get("main.menu.analyze.optimization"), KeyEvent.VK_O); item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.analyze.optimization.desc")); item.addActionListener(new ActionListener() { @@ -630,58 +649,51 @@ public class BasicFrame extends JFrame { } }); menu.add(item); - + //// Custom expressions + item = new JMenuItem(trans.get("main.menu.analyze.customExpressions"), KeyEvent.VK_E); + item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.analyze.customExpressions.desc")); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + log.debug("Custom expressions selected"); + new CustomExpressionDialog(document, BasicFrame.this).setVisible(true); + } + }); + menu.add(item); //// Debug // (shown if openrocket.debug.menu is defined) if (System.getProperty("openrocket.debug.menu") != null) { menubar.add(makeDebugMenu()); } - + //// Help - + menu = new JMenu(trans.get("main.menu.help")); menu.setMnemonic(KeyEvent.VK_H); menu.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.desc")); menubar.add(menu); - + // Guided tours - + item = new JMenuItem(trans.get("main.menu.help.tours"), KeyEvent.VK_L); - // TODO: Icon + item.setIcon(Icons.HELP_TOURS); item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.tours.desc")); item.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { log.user("Guided tours selected"); - // FIXME: Singleton - new GuidedTourSelectionDialog(BasicFrame.this).setVisible(true); + GuidedTourSelectionDialog.showDialog(BasicFrame.this); } }); menu.add(item); - - menu.addSeparator(); - - //// License - item = new JMenuItem(trans.get("main.menu.help.license"), KeyEvent.VK_L); - item.setIcon(Icons.HELP_LICENSE); - item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.license.desc")); - item.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - log.user("License selected"); - new LicenseDialog(BasicFrame.this).setVisible(true); - } - }); - 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); @@ -694,7 +706,7 @@ public class BasicFrame extends JFrame { } }); menu.add(item); - + //// Debug log item = new JMenuItem(trans.get("main.menu.help.debugLog")); item.setIcon(Icons.HELP_DEBUG_LOG); @@ -707,9 +719,24 @@ 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); + item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.license.desc")); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + log.user("License selected"); + new LicenseDialog(BasicFrame.this).setVisible(true); + } + }); + menu.add(item); + + //// About item = new JMenuItem(trans.get("main.menu.help.about"), KeyEvent.VK_A); item.setIcon(Icons.HELP_ABOUT); @@ -722,15 +749,15 @@ public class BasicFrame extends JFrame { } }); menu.add(item); - + this.setJMenuBar(menubar); } - + private JMenu makeDebugMenu() { JMenu menu; JMenuItem item; - + /* * This menu is intentionally left untranslated. */ @@ -739,7 +766,7 @@ public class BasicFrame extends JFrame { 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() { @@ -757,9 +784,9 @@ public class BasicFrame extends JFrame { } }); menu.add(item); - + menu.addSeparator(); - + //// Create test rocket item = new JMenuItem("Create test rocket"); item.addActionListener(new ActionListener() { @@ -770,11 +797,11 @@ public class BasicFrame extends JFrame { int sel = JOptionPane.showOptionDialog(BasicFrame.this, new Object[] { "Input text key to generate random rocket:", field - }, "Generate random test rocket", JOptionPane.DEFAULT_OPTION, + }, "Generate random test rocket", JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, new Object[] { "Random", "OK" - }, "OK"); - + }, "OK"); + Rocket r; if (sel == 0) { r = new TestRockets(null).makeTestRocket(); @@ -783,7 +810,7 @@ public class BasicFrame extends JFrame { } else { return; } - + OpenRocketDocument doc = new OpenRocketDocument(r); doc.setSaved(true); BasicFrame frame = new BasicFrame(doc); @@ -791,7 +818,7 @@ public class BasicFrame extends JFrame { } }); menu.add(item); - + item = new JMenuItem("Create 'Iso-Haisu'"); @@ -807,7 +834,7 @@ public class BasicFrame extends JFrame { } }); menu.add(item); - + item = new JMenuItem("Create 'Big Blue'"); item.addActionListener(new ActionListener() { @@ -822,16 +849,16 @@ public class BasicFrame extends JFrame { } }); menu.add(item); - + menu.addSeparator(); - + item = new JMenuItem("Memory statistics"); item.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { log.user("Memory statistics selected"); - + // Get discarded but remaining objects (this also runs System.gc multiple times) List objects = MemoryManagement.getRemainingCollectableObjects(); StringBuilder sb = new StringBuilder(); @@ -848,7 +875,7 @@ public class BasicFrame extends JFrame { o = null; } sb.append("Total: " + count); - + // Get basic memory stats System.gc(); long max = Runtime.getRuntime().maxMemory(); @@ -859,14 +886,14 @@ public class BasicFrame extends JFrame { stats[1] = String.format(" Max memory: %.1f MB", max / 1024.0 / 1024.0); stats[2] = String.format(" Used memory: %.1f MB (%.0f%%)", used / 1024.0 / 1024.0, 100.0 * used / max); stats[3] = String.format(" Free memory: %.1f MB (%.0f%%)", free / 1024.0 / 1024.0, 100.0 * free / max); - + DetailDialog.showDetailedMessageDialog(BasicFrame.this, stats, sb.toString(), "Memory statistics", JOptionPane.INFORMATION_MESSAGE); } }); menu.add(item); - + //// Exhaust memory item = new JMenuItem("Exhaust memory"); item.addActionListener(new ActionListener() { @@ -896,10 +923,10 @@ public class BasicFrame extends JFrame { } }); menu.add(item); - + menu.addSeparator(); - + //// Exception here item = new JMenuItem("Exception here"); item.addActionListener(new ActionListener() { @@ -910,7 +937,7 @@ public class BasicFrame extends JFrame { } }); menu.add(item); - + item = new JMenuItem("Exception from EDT"); item.addActionListener(new ActionListener() { @Override @@ -926,7 +953,7 @@ public class BasicFrame extends JFrame { } }); menu.add(item); - + item = new JMenuItem("Exception from other thread"); item.addActionListener(new ActionListener() { @Override @@ -941,7 +968,7 @@ public class BasicFrame extends JFrame { } }); menu.add(item); - + item = new JMenuItem("OutOfMemoryError here"); item.addActionListener(new ActionListener() { @Override @@ -951,10 +978,10 @@ public class BasicFrame extends JFrame { } }); menu.add(item); - + menu.addSeparator(); - + item = new JMenuItem("Test popup"); item.addActionListener(new ActionListener() { @@ -971,33 +998,33 @@ public class BasicFrame extends JFrame { } }); menu.add(item); - + return menu; } - - + + /** * Select the tab on the main pane. - * + * * @param tab one of {@link #COMPONENT_TAB} or {@link #SIMULATION_TAB}. */ public void selectTab(int tab) { tabbedPane.setSelectedIndex(tab); } - - + + private void openAction() { JFileChooser chooser = new JFileChooser(); - + chooser.addChoosableFileFilter(FileHelper.ALL_DESIGNS_FILTER); chooser.addChoosableFileFilter(FileHelper.OPENROCKET_DESIGN_FILTER); chooser.addChoosableFileFilter(FileHelper.ROCKSIM_DESIGN_FILTER); chooser.setFileFilter(FileHelper.ALL_DESIGNS_FILTER); - + chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); chooser.setMultiSelectionEnabled(true); chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory()); @@ -1006,31 +1033,33 @@ public class BasicFrame extends JFrame { log.user("Decided not to open files, option=" + option); return; } - + ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(chooser.getCurrentDirectory()); - + File[] files = chooser.getSelectedFiles(); log.user("Opening files " + Arrays.toString(files)); - + for (File file : files) { log.info("Opening file: " + file); if (open(file, this)) { - - // Close previous window if replacing - if (replaceable && document.isSaved()) { - // We are replacing the frame, make new window have current location - BasicFrame newFrame = frames.get(frames.size() - 1); - newFrame.setLocation(this.getLocation()); - - log.info("Closing window because it is replaceable"); - closeAction(); - replaceable = false; - } + MRUDesignFile opts = MRUDesignFile.getInstance(); + opts.addFile(file.getAbsolutePath()); } } } - - + + void closeIfReplaceable() { + // Close previous window if replacing + if (replaceable && document.isSaved()) { + // We are replacing the frame, make new window have current location + BasicFrame newFrame = frames.get(frames.size() - 1); + newFrame.setLocation(this.getLocation()); + + log.info("Closing window because it is replaceable"); + closeAction(); + } + + } /** * Open a file based on a URL. * @param url the file to open. @@ -1039,16 +1068,16 @@ public class BasicFrame extends JFrame { */ private static boolean open(URL url, BasicFrame parent) { String filename = null; - + // First figure out the file name from the URL - + // Try using URI.getPath(); try { URI uri = url.toURI(); filename = uri.getPath(); } catch (URISyntaxException ignore) { } - + // Try URL-decoding the URL if (filename == null) { try { @@ -1056,44 +1085,38 @@ public class BasicFrame extends JFrame { } catch (UnsupportedEncodingException ignore) { } } - + // Last resort if (filename == null) { filename = ""; } - + // Remove path from filename if (filename.lastIndexOf('/') >= 0) { filename = filename.substring(filename.lastIndexOf('/') + 1); } - + // Open the file log.info("Opening file from url=" + url + " filename=" + filename); try { InputStream is = url.openStream(); - if (open(is, filename, parent)) { - // Close previous window if replacing - if (parent.replaceable && parent.document.isSaved()) { - parent.closeAction(); - parent.replaceable = false; - } - } + open(is, filename, parent); } catch (IOException e) { log.warn("Error opening file" + e); JOptionPane.showMessageDialog(parent, "An error occurred while opening the file " + filename, "Error loading file", JOptionPane.ERROR_MESSAGE); } - + return false; } - - + + /** * Open the specified file from an InputStream in a new design frame. If an error * occurs, an error dialog is shown and 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. @@ -1103,12 +1126,12 @@ public class BasicFrame extends JFrame { OpenFileWorker worker = new OpenFileWorker(stream, ROCKET_LOADER); return open(worker, filename, null, parent); } - - + + /** * Open the specified file in a new design frame. If an error occurs, an error * dialog is shown and 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. @@ -1117,11 +1140,11 @@ public class BasicFrame extends JFrame { OpenFileWorker worker = new OpenFileWorker(file, ROCKET_LOADER); return open(worker, file.getName(), file, parent); } - - + + /** * Open the specified file using the provided worker. - * + * * @param worker the OpenFileWorker that loads the file. * @param filename the file name to display in dialogs. * @param file the File to set the document to (may be null). @@ -1129,9 +1152,9 @@ public class BasicFrame extends JFrame { * @return */ private static boolean open(OpenFileWorker worker, String filename, File file, Window parent) { - + MotorDatabaseLoadingDialog.check(parent); - + // Open the file in a Swing worker thread log.info("Starting OpenFileWorker"); if (!SwingWorkerDialog.runWorker(parent, "Opening file", "Reading " + filename + "...", worker)) { @@ -1139,49 +1162,49 @@ 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(); @@ -1197,29 +1220,32 @@ public class BasicFrame extends JFrame { //// Warnings while opening file trans.get("BasicFrame.WarningDialog.title"), warnings); } - + // Set document state doc.setFile(file); doc.setSaved(true); - + // Open the frame log.debug("Opening new frame with the document"); BasicFrame frame = new BasicFrame(doc); frame.setVisible(true); - + + if ( parent != null && parent instanceof BasicFrame ) { + ((BasicFrame)parent).closeIfReplaceable(); + } return true; } - /** - * "Save" action. If the design is new, then this is identical to "Save As", with a default file filter for .ork. - * If the rocket being edited previously was opened from a .ork file, then it will be saved immediately to the same - * file. But clicking on 'Save' for an existing design file with a .rkt will bring up a confirmation dialog because - * it's potentially a destructive write (loss of some fidelity if it's truly an original Rocksim generated file). - * - * @return true if the file was saved, false otherwise - */ + /** + * "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 + * file. But clicking on 'Save' for an existing design file with a .rkt will bring up a confirmation dialog because + * it's potentially a destructive write (loss of some fidelity if it's truly an original Rocksim generated file). + * + * @return true if the file was saved, false otherwise + */ private boolean saveAction() { File file = document.getFile(); if (file == null) { @@ -1227,121 +1253,121 @@ public class BasicFrame extends JFrame { return saveAsAction(); } log.info("Saving document to " + file); - + if (FileHelper.ROCKSIM_DESIGN_FILTER.accept(file)) { - return saveAsRocksim(file); + return saveAsRocksim(file); } return saveAs(file); } - /** - * "Save As" action. - * - * Never should a .rkt file contain an OpenRocket content, or an .ork file contain a Rocksim design. Regardless of - * what extension the user has chosen, it would violate the Principle of Least Astonishment to do otherwise - * (and we want to make doing the wrong thing really hard to do). So always force the appropriate extension. - * - * This can result in some odd looking filenames (MyDesign.rkt.ork, MyDesign.rkt.ork.rkt, etc.) if the user is - * not paying attention, but the user can control that by modifying the filename in the dialog. - * - * @return true if the file was saved, false otherwise - */ + /** + * "Save As" action. + * + * Never should a .rkt file contain an OpenRocket content, or an .ork file contain a Rocksim design. Regardless of + * what extension the user has chosen, it would violate the Principle of Least Astonishment to do otherwise + * (and we want to make doing the wrong thing really hard to do). So always force the appropriate extension. + * + * This can result in some odd looking filenames (MyDesign.rkt.ork, MyDesign.rkt.ork.rkt, etc.) if the user is + * not paying attention, but the user can control that by modifying the filename in the dialog. + * + * @return true if the file was saved, false otherwise + */ 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); + 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); - } - else { - chooser.setFileFilter(FileHelper.OPENROCKET_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); + } + else { + chooser.setFileFilter(FileHelper.OPENROCKET_DESIGN_FILTER); + } chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory()); chooser.setAccessory(storageChooser); 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); - } - else { - file = FileHelper.forceExtension(file, "ork"); - return FileHelper.confirmWrite(file, this) && saveAs(file); - } + if (chooser.getFileFilter().equals(FileHelper.ROCKSIM_DESIGN_FILTER)) { + return saveAsRocksim(file); + } + else { + file = FileHelper.forceExtension(file, "ork"); + return FileHelper.confirmWrite(file, this) && saveAs(file); + } } - /** - * Perform the writing of the design to the given file in Rocksim format. - * - * @param file the chosen file - * - * @return true if the file was written - */ - private boolean saveAsRocksim(File file) { - file = FileHelper.forceExtension(file, "rkt"); - if (!FileHelper.confirmWrite(file, this)) { - return false; - } + /** + * Perform the writing of the design to the given file in Rocksim format. + * + * @param file the chosen file + * + * @return true if the file was written + */ + private boolean saveAsRocksim(File file) { + file = FileHelper.forceExtension(file, "rkt"); + if (!FileHelper.confirmWrite(file, this)) { + return false; + } - try { - new RocksimSaver().save(file, document); - return true; - } catch (IOException e) { - return false; - } - } - - /** - * Perform the writing of the design to the given file in OpenRocket format. - * - * @param file the chosen file - * - * @return true if the file was written - */ - private boolean saveAs(File file) { + try { + new RocksimSaver().save(file, document); + return true; + } catch (IOException e) { + return false; + } + } + + /** + * Perform the writing of the design to the given file in OpenRocket format. + * + * @param file the chosen file + * + * @return true if the file was written + */ + 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); @@ -1349,9 +1375,9 @@ public class BasicFrame extends JFrame { saved = true; setTitle(); } catch (ExecutionException e) { - + Throwable cause = e.getCause(); - + if (cause instanceof IOException) { log.warn("An I/O error occurred while saving " + file, cause); JOptionPane.showMessageDialog(this, new String[] { @@ -1361,15 +1387,15 @@ public class BasicFrame extends JFrame { } else { Reflection.handleWrappedException(e); } - + } catch (InterruptedException e) { throw new BugException("EDT was interrupted", e); } - + return saved; } - - + + private boolean closeAction() { if (!document.isSaved()) { log.info("Confirming whether to save the design"); @@ -1396,14 +1422,14 @@ public class BasicFrame extends JFrame { return false; } } - + // Rocket has been saved or discarded log.debug("Disposing window"); this.dispose(); - + ComponentConfigDialog.hideDialog(); ComponentAnalysisDialog.hideDialog(); - + frames.remove(this); if (frames.isEmpty()) { log.info("Last frame closed, exiting"); @@ -1411,22 +1437,26 @@ public class BasicFrame extends JFrame { } return true; } - - + + /** - * + * */ public void printAction() { - new PrintDialog(this, document).setVisible(true); + Double rotation = rocketpanel.getFigure().getRotation(); + if (rotation == null) { + rotation = 0d; + } + new PrintDialog(this, document, rotation).setVisible(true); } - + /** * Open a new design window with a basic rocket+stage. */ public static void newAction() { log.info("New action initiated"); - + Rocket rocket = new Rocket(); Stage stage = new Stage(); //// Sustainer @@ -1434,13 +1464,14 @@ public class BasicFrame extends JFrame { rocket.addChild(stage); OpenRocketDocument doc = new OpenRocketDocument(rocket); doc.setSaved(true); - + BasicFrame frame = new BasicFrame(doc); frame.replaceable = true; frame.setVisible(true); - ComponentConfigDialog.showDialog(frame, doc, rocket); + // kruland commented this out - I don't like it. + //ComponentConfigDialog.showDialog(frame, doc, rocket); } - + /** * Quit the application. Confirms saving unsaved designs. The action of File->Quit. */ @@ -1458,33 +1489,33 @@ public class BasicFrame extends JFrame { log.error("Should already have exited application"); System.exit(0); } - - + + /** - * Set the title of the frame, taking into account the name of the rocket, file it + * Set the title of the frame, taking into account the name of the rocket, file it * has been saved to (if any) and saved status. */ private void setTitle() { File file = document.getFile(); boolean saved = document.isSaved(); String title; - + title = rocket.getName(); if (file != null) { title = title + " (" + file.getName() + ")"; } if (!saved) title = "*" + title; - + setTitle(title); } - - + + /** * Find a currently open BasicFrame containing the specified rocket. This method * can be used to map a Rocket to a BasicFrame from GUI methods. - * + * * @param rocket the Rocket. * @return the corresponding BasicFrame, or null if none found. */ @@ -1498,11 +1529,11 @@ public class BasicFrame extends JFrame { log.debug("Could not find frame for rocket " + rocket); return null; } - + /** * Find a currently open document by the rocket object. This method can be used * to map a Rocket to OpenRocketDocument from GUI methods. - * + * * @param rocket the Rocket. * @return the corresponding OpenRocketDocument, or null if not found. */