1 package net.sf.openrocket.gui.main;
3 import java.awt.Dimension;
5 import java.awt.Toolkit;
6 import java.awt.Window;
7 import java.awt.event.ActionEvent;
8 import java.awt.event.ActionListener;
9 import java.awt.event.ComponentAdapter;
10 import java.awt.event.ComponentEvent;
11 import java.awt.event.KeyEvent;
12 import java.awt.event.MouseAdapter;
13 import java.awt.event.MouseEvent;
14 import java.awt.event.MouseListener;
15 import java.awt.event.WindowAdapter;
16 import java.awt.event.WindowEvent;
18 import java.io.FileNotFoundException;
19 import java.io.IOException;
20 import java.lang.reflect.InvocationTargetException;
21 import java.util.ArrayList;
22 import java.util.Iterator;
23 import java.util.concurrent.ExecutionException;
25 import javax.swing.Action;
26 import javax.swing.InputMap;
27 import javax.swing.JButton;
28 import javax.swing.JComponent;
29 import javax.swing.JFileChooser;
30 import javax.swing.JFrame;
31 import javax.swing.JMenu;
32 import javax.swing.JMenuBar;
33 import javax.swing.JMenuItem;
34 import javax.swing.JOptionPane;
35 import javax.swing.JPanel;
36 import javax.swing.JScrollPane;
37 import javax.swing.JSeparator;
38 import javax.swing.JSplitPane;
39 import javax.swing.JTabbedPane;
40 import javax.swing.KeyStroke;
41 import javax.swing.ListSelectionModel;
42 import javax.swing.LookAndFeel;
43 import javax.swing.ScrollPaneConstants;
44 import javax.swing.SwingUtilities;
45 import javax.swing.ToolTipManager;
46 import javax.swing.UIManager;
47 import javax.swing.border.TitledBorder;
48 import javax.swing.event.TreeSelectionEvent;
49 import javax.swing.event.TreeSelectionListener;
50 import javax.swing.filechooser.FileFilter;
51 import javax.swing.tree.DefaultTreeSelectionModel;
52 import javax.swing.tree.TreePath;
53 import javax.swing.tree.TreeSelectionModel;
55 import net.miginfocom.swing.MigLayout;
56 import net.sf.openrocket.aerodynamics.Warning;
57 import net.sf.openrocket.document.OpenRocketDocument;
58 import net.sf.openrocket.file.GeneralRocketLoader;
59 import net.sf.openrocket.file.OpenRocketSaver;
60 import net.sf.openrocket.file.RocketLoadException;
61 import net.sf.openrocket.file.RocketLoader;
62 import net.sf.openrocket.file.RocketSaver;
63 import net.sf.openrocket.gui.StorageOptionChooser;
64 import net.sf.openrocket.gui.configdialog.ComponentConfigDialog;
65 import net.sf.openrocket.gui.dialogs.BugDialog;
66 import net.sf.openrocket.gui.dialogs.ComponentAnalysisDialog;
67 import net.sf.openrocket.gui.dialogs.LicenseDialog;
68 import net.sf.openrocket.gui.dialogs.PreferencesDialog;
69 import net.sf.openrocket.gui.dialogs.SwingWorkerDialog;
70 import net.sf.openrocket.gui.scalefigure.RocketPanel;
71 import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
72 import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
73 import net.sf.openrocket.rocketcomponent.Rocket;
74 import net.sf.openrocket.rocketcomponent.RocketComponent;
75 import net.sf.openrocket.rocketcomponent.Stage;
76 import net.sf.openrocket.util.Icons;
77 import net.sf.openrocket.util.OpenFileWorker;
78 import net.sf.openrocket.util.Prefs;
79 import net.sf.openrocket.util.SaveFileWorker;
81 public class BasicFrame extends JFrame {
82 private static final long serialVersionUID = 1L;
85 * The RocketLoader instance used for loading all rocket designs.
87 private static final RocketLoader ROCKET_LOADER = new GeneralRocketLoader();
89 // TODO: Always uses OpenRocketSaver
90 private static final RocketSaver ROCKET_SAVER = new OpenRocketSaver();
94 * File filter for filtering only rocket designs.
96 private static final FileFilter ROCKET_DESIGN_FILTER = new FileFilter() {
98 public String getDescription() {
99 return "OpenRocket designs (*.ork)";
102 public boolean accept(File f) {
105 String name = f.getName().toLowerCase();
106 return name.endsWith(".ork") || name.endsWith(".ork.gz");
112 public static final int COMPONENT_TAB = 0;
113 public static final int SIMULATION_TAB = 1;
117 * List of currently open frames. When the list goes empty
118 * it is time to exit the application.
120 private static final ArrayList<BasicFrame> frames = new ArrayList<BasicFrame>();
127 * Whether "New" and "Open" should replace this frame.
128 * Should be set to false on the first rocket modification.
130 private boolean replaceable = false;
134 private final OpenRocketDocument document;
135 private final Rocket rocket;
137 private JTabbedPane tabbedPane;
138 private RocketPanel rocketpanel;
139 private ComponentTree tree = null;
141 private final DocumentSelectionModel selectionModel;
142 private final TreeSelectionModel componentSelectionModel;
143 private final ListSelectionModel simulationSelectionModel;
145 /** Actions available for rocket modifications */
146 private final RocketActions actions;
151 * Sole constructor. Creates a new frame based on the supplied document
152 * and adds it to the current frames list.
154 * @param document the document to show.
156 public BasicFrame(OpenRocketDocument document) {
158 this.document = document;
159 this.rocket = document.getRocket();
160 this.rocket.getDefaultConfiguration().setAllStages();
163 // Set replaceable flag to false at first modification
164 rocket.addComponentChangeListener(new ComponentChangeListener() {
165 public void componentChanged(ComponentChangeEvent e) {
167 BasicFrame.this.rocket.removeComponentChangeListener(this);
172 // Create the component tree selection model that will be used
173 componentSelectionModel = new DefaultTreeSelectionModel();
174 componentSelectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
176 // Obtain the simulation selection model that will be used
177 SimulationPanel simulationPanel = new SimulationPanel(document);
178 simulationSelectionModel = simulationPanel.getSimulationListSelectionModel();
180 // Combine into a DocumentSelectionModel
181 selectionModel = new DocumentSelectionModel(document);
182 selectionModel.attachComponentTreeSelectionModel(componentSelectionModel);
183 selectionModel.attachSimulationListSelectionModel(simulationSelectionModel);
186 actions = new RocketActions(document, selectionModel, this);
189 // The main vertical split pane
190 JSplitPane vertical = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true);
191 vertical.setResizeWeight(0.5);
195 // The top tabbed pane
196 tabbedPane = new JTabbedPane();
197 tabbedPane.addTab("Rocket design", null, designTab());
198 tabbedPane.addTab("Flight simulations", null, simulationPanel);
200 vertical.setTopComponent(tabbedPane);
204 // Bottom segment, rocket figure
206 rocketpanel = new RocketPanel(document);
207 vertical.setBottomComponent(rocketpanel);
209 rocketpanel.setSelectionModel(tree.getSelectionModel());
215 rocket.addComponentChangeListener(new ComponentChangeListener() {
216 public void componentChanged(ComponentChangeEvent e) {
224 Dimension size = Prefs.getWindowSize(this.getClass());
226 size = Toolkit.getDefaultToolkit().getScreenSize();
227 size.width = size.width*9/10;
228 size.height = size.height*9/10;
231 this.addComponentListener(new ComponentAdapter() {
233 public void componentResized(ComponentEvent e) {
234 Prefs.setWindowSize(BasicFrame.this.getClass(), BasicFrame.this.getSize());
237 this.setLocationByPlatform(true);
240 vertical.setDividerLocation(0.4);
241 setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
242 addWindowListener(new WindowAdapter() {
244 public void windowClosing(WindowEvent e) {
253 * Construct the "Rocket design" tab. This contains a horizontal split pane
254 * with the left component the design tree and the right component buttons
255 * for adding components.
257 private JComponent designTab() {
258 JSplitPane horizontal = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,true);
259 horizontal.setResizeWeight(0.5);
262 // Upper-left segment, component tree
264 JPanel panel = new JPanel(new MigLayout("fill, flowy","","[grow]"));
266 tree = new ComponentTree(rocket);
267 tree.setSelectionModel(componentSelectionModel);
269 // Remove JTree key events that interfere with menu accelerators
270 InputMap im = SwingUtilities.getUIInputMap(tree, JComponent.WHEN_FOCUSED);
271 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, ActionEvent.CTRL_MASK), null);
272 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, ActionEvent.CTRL_MASK), null);
273 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_V, ActionEvent.CTRL_MASK), null);
274 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, ActionEvent.CTRL_MASK), null);
275 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK), null);
276 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.CTRL_MASK), null);
277 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK), null);
281 // Double-click opens config dialog
282 MouseListener ml = new MouseAdapter() {
284 public void mousePressed(MouseEvent e) {
285 int selRow = tree.getRowForLocation(e.getX(), e.getY());
286 TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
288 if(e.getClickCount() == 2) {
290 RocketComponent c = (RocketComponent)selPath.getLastPathComponent();
291 ComponentConfigDialog.showDialog(BasicFrame.this,
292 BasicFrame.this.document, c);
297 tree.addMouseListener(ml);
299 // Update dialog when selection is changed
300 componentSelectionModel.addTreeSelectionListener(new TreeSelectionListener() {
301 public void valueChanged(TreeSelectionEvent e) {
302 // Scroll tree to the selected item
303 TreePath path = componentSelectionModel.getSelectionPath();
306 tree.scrollPathToVisible(path);
308 if (!ComponentConfigDialog.isDialogVisible())
310 RocketComponent c = (RocketComponent)path.getLastPathComponent();
311 ComponentConfigDialog.showDialog(BasicFrame.this,
312 BasicFrame.this.document, c);
316 // Place tree inside scroll pane
317 JScrollPane scroll = new JScrollPane(tree);
318 panel.add(scroll,"spany, grow, wrap");
322 JButton button = new JButton(actions.getMoveUpAction());
323 panel.add(button,"sizegroup buttons, aligny 65%");
325 button = new JButton(actions.getMoveDownAction());
326 panel.add(button,"sizegroup buttons, aligny 0%");
328 button = new JButton(actions.getEditAction());
329 panel.add(button, "sizegroup buttons");
331 button = new JButton(actions.getNewStageAction());
332 panel.add(button,"sizegroup buttons");
334 button = new JButton(actions.getDeleteAction());
335 button.setIcon(null);
336 button.setMnemonic(0);
337 panel.add(button,"sizegroup buttons");
339 horizontal.setLeftComponent(panel);
342 // Upper-right segment, component addition buttons
344 panel = new JPanel(new MigLayout("fill, insets 0","[0::]"));
346 scroll = new JScrollPane(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
347 ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
348 scroll.setViewportView(new ComponentAddButtons(document, componentSelectionModel,
349 scroll.getViewport()));
350 scroll.setBorder(null);
351 scroll.setViewportBorder(null);
353 TitledBorder border = new TitledBorder("Add new component");
354 border.setTitleFont(border.getTitleFont().deriveFont(Font.BOLD));
355 scroll.setBorder(border);
357 panel.add(scroll,"grow");
359 horizontal.setRightComponent(panel);
367 * Creates the menu for the window.
369 private void createMenu() {
370 JMenuBar menubar = new JMenuBar();
375 menu = new JMenu("File");
376 menu.setMnemonic(KeyEvent.VK_F);
377 menu.getAccessibleContext().setAccessibleDescription("File-handling related tasks");
380 item = new JMenuItem("New",KeyEvent.VK_N);
381 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK));
382 item.setMnemonic(KeyEvent.VK_N);
383 item.getAccessibleContext().setAccessibleDescription("Create a new rocket design");
384 item.setIcon(Icons.FILE_NEW);
385 item.addActionListener(new ActionListener() {
386 public void actionPerformed(ActionEvent e) {
394 item = new JMenuItem("Open...",KeyEvent.VK_O);
395 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.CTRL_MASK));
396 item.getAccessibleContext().setAccessibleDescription("Open a rocket design");
397 item.setIcon(Icons.FILE_OPEN);
398 item.addActionListener(new ActionListener() {
399 public void actionPerformed(ActionEvent e) {
407 item = new JMenuItem("Save",KeyEvent.VK_S);
408 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK));
409 item.getAccessibleContext().setAccessibleDescription("Save the current rocket design");
410 item.setIcon(Icons.FILE_SAVE);
411 item.addActionListener(new ActionListener() {
412 public void actionPerformed(ActionEvent e) {
418 item = new JMenuItem("Save as...",KeyEvent.VK_A);
419 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
420 ActionEvent.CTRL_MASK | ActionEvent.SHIFT_MASK));
421 item.getAccessibleContext().setAccessibleDescription("Save the current rocket design "+
423 item.setIcon(Icons.FILE_SAVE_AS);
424 item.addActionListener(new ActionListener() {
425 public void actionPerformed(ActionEvent e) {
431 // menu.addSeparator();
432 menu.add(new JSeparator());
434 item = new JMenuItem("Close",KeyEvent.VK_C);
435 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, ActionEvent.CTRL_MASK));
436 item.getAccessibleContext().setAccessibleDescription("Close the current rocket design");
437 item.setIcon(Icons.FILE_CLOSE);
438 item.addActionListener(new ActionListener() {
439 public void actionPerformed(ActionEvent e) {
447 item = new JMenuItem("Quit",KeyEvent.VK_Q);
448 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, ActionEvent.CTRL_MASK));
449 item.getAccessibleContext().setAccessibleDescription("Quit the program");
450 item.setIcon(Icons.FILE_QUIT);
451 item.addActionListener(new ActionListener() {
452 public void actionPerformed(ActionEvent e) {
461 menu = new JMenu("Edit");
462 menu.setMnemonic(KeyEvent.VK_E);
463 menu.getAccessibleContext().setAccessibleDescription("Rocket editing");
467 Action action = document.getUndoAction();
468 item = new JMenuItem(action);
469 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, ActionEvent.CTRL_MASK));
470 item.setMnemonic(KeyEvent.VK_U);
471 item.getAccessibleContext().setAccessibleDescription("Undo the previous operation");
475 action = document.getRedoAction();
476 item = new JMenuItem(action);
477 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Y, ActionEvent.CTRL_MASK));
478 item.setMnemonic(KeyEvent.VK_R);
479 item.getAccessibleContext().setAccessibleDescription("Redo the previously undone " +
486 item = new JMenuItem(actions.getCutAction());
489 item = new JMenuItem(actions.getCopyAction());
492 item = new JMenuItem(actions.getPasteAction());
495 item = new JMenuItem(actions.getDeleteAction());
500 item = new JMenuItem("Preferences");
501 item.setIcon(Icons.PREFERENCES);
502 item.getAccessibleContext().setAccessibleDescription("Setup the application "+
504 item.addActionListener(new ActionListener() {
505 public void actionPerformed(ActionEvent e) {
506 PreferencesDialog.showPreferences();
515 menu = new JMenu("Analyze");
516 menu.setMnemonic(KeyEvent.VK_A);
517 menu.getAccessibleContext().setAccessibleDescription("Analyzing the rocket");
520 item = new JMenuItem("Component analysis",KeyEvent.VK_C);
521 item.getAccessibleContext().setAccessibleDescription("Analyze the rocket components " +
523 item.addActionListener(new ActionListener() {
524 public void actionPerformed(ActionEvent e) {
525 ComponentAnalysisDialog.showDialog(rocketpanel);
534 menu = new JMenu("Help");
535 menu.setMnemonic(KeyEvent.VK_H);
536 menu.getAccessibleContext().setAccessibleDescription("Information about OpenRocket");
541 item = new JMenuItem("License",KeyEvent.VK_L);
542 item.getAccessibleContext().setAccessibleDescription("OpenRocket license information");
543 item.addActionListener(new ActionListener() {
544 public void actionPerformed(ActionEvent e) {
545 new LicenseDialog(BasicFrame.this).setVisible(true);
550 item = new JMenuItem("Bug report",KeyEvent.VK_B);
551 item.getAccessibleContext().setAccessibleDescription("Information about reporting " +
552 "bugs in OpenRocket");
553 item.addActionListener(new ActionListener() {
554 public void actionPerformed(ActionEvent e) {
555 new BugDialog(BasicFrame.this).setVisible(true);
560 item = new JMenuItem("About",KeyEvent.VK_A);
561 item.getAccessibleContext().setAccessibleDescription("About OpenRocket");
562 item.addActionListener(new ActionListener() {
563 public void actionPerformed(ActionEvent e) {
564 new AboutDialog(BasicFrame.this).setVisible(true);
570 this.setJMenuBar(menubar);
576 * Select the tab on the main pane.
578 * @param tab one of {@link #COMPONENT_TAB} or {@link #SIMULATION_TAB}.
580 public void selectTab(int tab) {
581 tabbedPane.setSelectedIndex(tab);
586 private void openAction() {
587 JFileChooser chooser = new JFileChooser();
588 chooser.setFileFilter(ROCKET_DESIGN_FILTER);
589 chooser.setMultiSelectionEnabled(true);
590 chooser.setCurrentDirectory(Prefs.getDefaultDirectory());
591 if (chooser.showOpenDialog(this) != JFileChooser.APPROVE_OPTION)
594 Prefs.setDefaultDirectory(chooser.getCurrentDirectory());
596 File[] files = chooser.getSelectedFiles();
598 for (File file: files) {
599 System.out.println("Opening file: " + file);
600 if (open(file, this)) {
602 // Close previous window if replacing
603 if (replaceable && document.isSaved()) {
613 * Open the specified file in a new design frame. If an error occurs, an error dialog
614 * is shown and <code>false</code> is returned.
616 * @param file the file to open.
617 * @param parent the parent component for which a progress dialog is opened.
618 * @return whether the file was successfully loaded and opened.
620 private static boolean open(File file, Window parent) {
622 // Open the file in a Swing worker thread
623 OpenFileWorker worker = new OpenFileWorker(file);
624 if (!SwingWorkerDialog.runWorker(parent, "Opening file",
625 "Reading " + file.getName() + "...", worker)) {
627 // User cancelled the operation
632 // Handle the document
633 OpenRocketDocument doc = null;
638 } catch (ExecutionException e) {
640 Throwable cause = e.getCause();
642 if (cause instanceof FileNotFoundException) {
644 JOptionPane.showMessageDialog(parent,
645 "File not found: " + file.getName(),
646 "Error opening file", JOptionPane.ERROR_MESSAGE);
649 } else if (cause instanceof RocketLoadException) {
651 JOptionPane.showMessageDialog(parent,
652 "Unable to open file '" + file.getName() +"': "
653 + cause.getMessage(),
654 "Error opening file", JOptionPane.ERROR_MESSAGE);
659 throw new RuntimeException("Unknown error when opening file", e);
663 } catch (InterruptedException e) {
664 throw new RuntimeException("EDT was interrupted", e);
668 throw new RuntimeException("BUG: Document loader returned null");
673 Iterator<Warning> warns = ROCKET_LOADER.getWarnings().iterator();
674 System.out.println("Warnings:");
675 while (warns.hasNext()) {
676 System.out.println(" "+warns.next());
677 // TODO: HIGH: dialog
680 // Set document state
685 BasicFrame frame = new BasicFrame(doc);
686 frame.setVisible(true);
699 private boolean saveAction() {
700 File file = document.getFile();
702 return saveAsAction();
709 private boolean saveAsAction() {
711 while (file == null) {
712 StorageOptionChooser storageChooser =
713 new StorageOptionChooser(document, document.getDefaultStorageOptions());
714 JFileChooser chooser = new JFileChooser();
715 chooser.setFileFilter(ROCKET_DESIGN_FILTER);
716 chooser.setCurrentDirectory(Prefs.getDefaultDirectory());
717 chooser.setAccessory(storageChooser);
718 if (document.getFile() != null)
719 chooser.setSelectedFile(document.getFile());
721 if (chooser.showSaveDialog(BasicFrame.this) != JFileChooser.APPROVE_OPTION)
724 file = chooser.getSelectedFile();
728 Prefs.setDefaultDirectory(chooser.getCurrentDirectory());
729 storageChooser.storeOptions(document.getDefaultStorageOptions());
731 if (file.getName().indexOf('.') < 0) {
732 String name = file.getAbsolutePath();
733 name = name + ".ork";
734 file = new File(name);
738 int result = JOptionPane.showConfirmDialog(this,
739 "File '"+file.getName()+"' exists. Do you want to overwrite it?",
740 "File exists", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
741 if (result != JOptionPane.YES_OPTION)
750 private boolean saveAs(File file) {
751 System.out.println("Saving to file: " + file.getName());
752 boolean saved = false;
754 if (!StorageOptionChooser.verifyStorageOptions(document, this)) {
755 // User cancelled the dialog
760 SaveFileWorker worker = new SaveFileWorker(document, file, ROCKET_SAVER);
762 if (!SwingWorkerDialog.runWorker(this, "Saving file",
763 "Writing " + file.getName() + "...", worker)) {
765 // User cancelled the save
772 document.setFile(file);
773 document.setSaved(true);
776 } catch (ExecutionException e) {
777 Throwable cause = e.getCause();
779 if (cause instanceof IOException) {
780 JOptionPane.showMessageDialog(this, new String[] {
781 "An I/O error occurred while saving:",
782 e.getMessage() }, "Saving failed", JOptionPane.ERROR_MESSAGE);
785 throw new RuntimeException("Unknown error when saving file", e);
788 } catch (InterruptedException e) {
789 throw new RuntimeException("EDT was interrupted", e);
796 private boolean closeAction() {
797 if (!document.isSaved()) {
798 ComponentConfigDialog.hideDialog();
799 int result = JOptionPane.showConfirmDialog(this,
800 "Design '"+rocket.getName()+"' has not been saved. " +
801 "Do you want to save it?",
802 "Design not saved", JOptionPane.YES_NO_CANCEL_OPTION,
803 JOptionPane.QUESTION_MESSAGE);
804 if (result == JOptionPane.YES_OPTION) {
807 return false; // If save was interrupted
808 } else if (result == JOptionPane.NO_OPTION) {
816 // Rocket has been saved or discarded
819 // TODO: LOW: Close only dialogs that have this frame as their parent
820 ComponentConfigDialog.hideDialog();
821 ComponentAnalysisDialog.hideDialog();
824 if (frames.isEmpty())
831 * Closes this frame if it is replaceable.
833 public void closeIfReplaceable() {
834 if (this.replaceable && document.isSaved()) {
840 * Open a new design window with a basic rocket+stage.
842 public static void newAction() {
843 Rocket rocket = new Rocket();
844 Stage stage = new Stage();
845 stage.setName("Sustainer");
846 rocket.addChild(stage);
847 OpenRocketDocument doc = new OpenRocketDocument(rocket);
850 BasicFrame frame = new BasicFrame(doc);
851 frame.replaceable = true;
852 frame.setVisible(true);
853 ComponentConfigDialog.showDialog(frame, doc, rocket);
857 * Quit the application. Confirms saving unsaved designs. The action of File->Quit.
859 public static void quitAction() {
860 for (int i=frames.size()-1; i>=0; i--) {
861 if (!frames.get(i).closeAction()) {
866 // Should not be reached, but just in case
872 * Set the title of the frame, taking into account the name of the rocket, file it
873 * has been saved to (if any) and saved status.
875 private void setTitle() {
876 File file = document.getFile();
877 boolean saved = document.isSaved();
880 title = rocket.getName();
882 title = title + " ("+file.getName()+")";
893 * Find a currently open BasicFrame containing the specified rocket. This method
894 * can be used to map a Rocket to a BasicFrame from GUI methods.
896 * @param rocket the Rocket.
897 * @return the corresponding BasicFrame, or <code>null</code> if none found.
899 public static BasicFrame findFrame(Rocket rocket) {
900 for (BasicFrame f: frames) {
901 if (f.rocket == rocket)
908 * Find a currently open document by the rocket object. This method can be used
909 * to map a Rocket to OpenRocketDocument from GUI methods.
911 * @param rocket the Rocket.
912 * @return the corresponding OpenRocketDocument, or <code>null</code> if not found.
914 public static OpenRocketDocument findDocument(Rocket rocket) {
915 for (BasicFrame f: frames) {
916 if (f.rocket == rocket)
923 public static void main(final String[] args) {
925 // Run the actual startup method in the EDT since it can use dialogs etc.
927 SwingUtilities.invokeAndWait(new Runnable() {
933 } catch (InterruptedException e) {
935 } catch (InvocationTargetException e) {
942 private static void runMain(String[] args) {
945 * Set the look-and-feel. On Linux, Motif/Metal is sometimes incorrectly used
946 * which is butt-ugly, so if the system l&f is Motif/Metal, we search for a few
947 * other alternatives.
951 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
953 // Check whether we have an ugly L&F
954 LookAndFeel laf = UIManager.getLookAndFeel();
956 laf.getName().matches(".*[mM][oO][tT][iI][fF].*") ||
957 laf.getName().matches(".*[mM][eE][tT][aA][lL].*")) {
959 // Search for better LAF
960 UIManager.LookAndFeelInfo[] info = UIManager.getInstalledLookAndFeels();
961 String lafNames[] = {
965 ".*[aA][qQ][uU][aA].*",
966 ".*[nN][iI][mM][bB].*"
969 lf: for (String lafName: lafNames) {
970 for (UIManager.LookAndFeelInfo l: info) {
971 if (l.getName().matches(lafName)) {
972 UIManager.setLookAndFeel(l.getClassName());
978 } catch (Exception e) {
979 System.err.println("Error setting LAF: " + e);
982 // Set tooltip delay time. Tooltips are used in MotorChooserDialog extensively.
983 ToolTipManager.sharedInstance().setDismissDelay(30000);
987 Prefs.loadDefaultUnits();
990 // Check command-line for files
991 boolean opened = false;
992 for (String file: args) {
993 if (open(new File(file), null)) {