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.util.ArrayList;
21 import java.util.Iterator;
22 import java.util.concurrent.ExecutionException;
24 import javax.swing.Action;
25 import javax.swing.InputMap;
26 import javax.swing.JButton;
27 import javax.swing.JComponent;
28 import javax.swing.JFileChooser;
29 import javax.swing.JFrame;
30 import javax.swing.JMenu;
31 import javax.swing.JMenuBar;
32 import javax.swing.JMenuItem;
33 import javax.swing.JOptionPane;
34 import javax.swing.JPanel;
35 import javax.swing.JScrollPane;
36 import javax.swing.JSeparator;
37 import javax.swing.JSplitPane;
38 import javax.swing.JTabbedPane;
39 import javax.swing.KeyStroke;
40 import javax.swing.ListSelectionModel;
41 import javax.swing.LookAndFeel;
42 import javax.swing.ScrollPaneConstants;
43 import javax.swing.SwingUtilities;
44 import javax.swing.ToolTipManager;
45 import javax.swing.UIManager;
46 import javax.swing.border.TitledBorder;
47 import javax.swing.event.TreeSelectionEvent;
48 import javax.swing.event.TreeSelectionListener;
49 import javax.swing.filechooser.FileFilter;
50 import javax.swing.tree.DefaultTreeSelectionModel;
51 import javax.swing.tree.TreePath;
52 import javax.swing.tree.TreeSelectionModel;
54 import net.miginfocom.swing.MigLayout;
55 import net.sf.openrocket.aerodynamics.Warning;
56 import net.sf.openrocket.document.OpenRocketDocument;
57 import net.sf.openrocket.file.GeneralRocketLoader;
58 import net.sf.openrocket.file.OpenRocketSaver;
59 import net.sf.openrocket.file.RocketLoadException;
60 import net.sf.openrocket.file.RocketLoader;
61 import net.sf.openrocket.file.RocketSaver;
62 import net.sf.openrocket.gui.StorageOptionChooser;
63 import net.sf.openrocket.gui.configdialog.ComponentConfigDialog;
64 import net.sf.openrocket.gui.dialogs.BugDialog;
65 import net.sf.openrocket.gui.dialogs.ComponentAnalysisDialog;
66 import net.sf.openrocket.gui.dialogs.LicenseDialog;
67 import net.sf.openrocket.gui.dialogs.PreferencesDialog;
68 import net.sf.openrocket.gui.dialogs.SwingWorkerDialog;
69 import net.sf.openrocket.gui.scalefigure.RocketPanel;
70 import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
71 import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
72 import net.sf.openrocket.rocketcomponent.Rocket;
73 import net.sf.openrocket.rocketcomponent.RocketComponent;
74 import net.sf.openrocket.rocketcomponent.Stage;
75 import net.sf.openrocket.util.Icons;
76 import net.sf.openrocket.util.OpenFileWorker;
77 import net.sf.openrocket.util.Prefs;
78 import net.sf.openrocket.util.SaveFileWorker;
80 public class BasicFrame extends JFrame {
81 private static final long serialVersionUID = 1L;
84 * The RocketLoader instance used for loading all rocket designs.
86 private static final RocketLoader ROCKET_LOADER = new GeneralRocketLoader();
88 // TODO: Always uses OpenRocketSaver
89 private static final RocketSaver ROCKET_SAVER = new OpenRocketSaver();
93 * File filter for filtering only rocket designs.
95 private static final FileFilter ROCKET_DESIGN_FILTER = new FileFilter() {
97 public String getDescription() {
98 return "OpenRocket designs (*.ork)";
101 public boolean accept(File f) {
104 String name = f.getName().toLowerCase();
105 return name.endsWith(".ork") || name.endsWith(".ork.gz");
111 public static final int COMPONENT_TAB = 0;
112 public static final int SIMULATION_TAB = 1;
116 * List of currently open frames. When the list goes empty
117 * it is time to exit the application.
119 private static final ArrayList<BasicFrame> frames = new ArrayList<BasicFrame>();
126 * Whether "New" and "Open" should replace this frame.
127 * Should be set to false on the first rocket modification.
129 private boolean replaceable = false;
133 private final OpenRocketDocument document;
134 private final Rocket rocket;
136 private JTabbedPane tabbedPane;
137 private RocketPanel rocketpanel;
138 private ComponentTree tree = null;
140 private final DocumentSelectionModel selectionModel;
141 private final TreeSelectionModel componentSelectionModel;
142 private final ListSelectionModel simulationSelectionModel;
144 /** Actions available for rocket modifications */
145 private final RocketActions actions;
150 * Sole constructor. Creates a new frame based on the supplied document
151 * and adds it to the current frames list.
153 * @param document the document to show.
155 public BasicFrame(OpenRocketDocument document) {
157 this.document = document;
158 this.rocket = document.getRocket();
159 this.rocket.getDefaultConfiguration().setAllStages();
162 // Set replaceable flag to false at first modification
163 rocket.addComponentChangeListener(new ComponentChangeListener() {
164 public void componentChanged(ComponentChangeEvent e) {
166 BasicFrame.this.rocket.removeComponentChangeListener(this);
171 // Create the component tree selection model that will be used
172 componentSelectionModel = new DefaultTreeSelectionModel();
173 componentSelectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
175 // Obtain the simulation selection model that will be used
176 SimulationPanel simulationPanel = new SimulationPanel(document);
177 simulationSelectionModel = simulationPanel.getSimulationListSelectionModel();
179 // Combine into a DocumentSelectionModel
180 selectionModel = new DocumentSelectionModel(document);
181 selectionModel.attachComponentTreeSelectionModel(componentSelectionModel);
182 selectionModel.attachSimulationListSelectionModel(simulationSelectionModel);
185 actions = new RocketActions(document, selectionModel, this);
188 // The main vertical split pane
189 JSplitPane vertical = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true);
190 vertical.setResizeWeight(0.5);
194 // The top tabbed pane
195 tabbedPane = new JTabbedPane();
196 tabbedPane.addTab("Rocket design", null, designTab());
197 tabbedPane.addTab("Flight simulations", null, simulationPanel);
199 vertical.setTopComponent(tabbedPane);
203 // Bottom segment, rocket figure
205 rocketpanel = new RocketPanel(document);
206 vertical.setBottomComponent(rocketpanel);
208 rocketpanel.setSelectionModel(tree.getSelectionModel());
214 rocket.addComponentChangeListener(new ComponentChangeListener() {
215 public void componentChanged(ComponentChangeEvent e) {
223 Dimension size = Prefs.getWindowSize(this.getClass());
225 size = Toolkit.getDefaultToolkit().getScreenSize();
226 size.width = size.width*9/10;
227 size.height = size.height*9/10;
230 this.addComponentListener(new ComponentAdapter() {
232 public void componentResized(ComponentEvent e) {
233 Prefs.setWindowSize(BasicFrame.this.getClass(), BasicFrame.this.getSize());
236 this.setLocationByPlatform(true);
239 vertical.setDividerLocation(0.4);
240 setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
241 addWindowListener(new WindowAdapter() {
243 public void windowClosing(WindowEvent e) {
252 * Construct the "Rocket design" tab. This contains a horizontal split pane
253 * with the left component the design tree and the right component buttons
254 * for adding components.
256 private JComponent designTab() {
257 JSplitPane horizontal = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,true);
258 horizontal.setResizeWeight(0.5);
261 // Upper-left segment, component tree
263 JPanel panel = new JPanel(new MigLayout("fill, flowy","","[grow]"));
265 tree = new ComponentTree(rocket);
266 tree.setSelectionModel(componentSelectionModel);
268 // Remove JTree key events that interfere with menu accelerators
269 InputMap im = SwingUtilities.getUIInputMap(tree, JComponent.WHEN_FOCUSED);
270 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, ActionEvent.CTRL_MASK), null);
271 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, ActionEvent.CTRL_MASK), null);
272 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_V, ActionEvent.CTRL_MASK), null);
273 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, ActionEvent.CTRL_MASK), null);
274 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK), null);
275 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.CTRL_MASK), null);
276 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK), null);
280 // Double-click opens config dialog
281 MouseListener ml = new MouseAdapter() {
283 public void mousePressed(MouseEvent e) {
284 int selRow = tree.getRowForLocation(e.getX(), e.getY());
285 TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
287 if(e.getClickCount() == 2) {
289 RocketComponent c = (RocketComponent)selPath.getLastPathComponent();
290 ComponentConfigDialog.showDialog(BasicFrame.this,
291 BasicFrame.this.document, c);
296 tree.addMouseListener(ml);
298 // Update dialog when selection is changed
299 componentSelectionModel.addTreeSelectionListener(new TreeSelectionListener() {
300 public void valueChanged(TreeSelectionEvent e) {
301 // Scroll tree to the selected item
302 TreePath path = componentSelectionModel.getSelectionPath();
305 tree.scrollPathToVisible(path);
307 if (!ComponentConfigDialog.isDialogVisible())
309 RocketComponent c = (RocketComponent)path.getLastPathComponent();
310 ComponentConfigDialog.showDialog(BasicFrame.this,
311 BasicFrame.this.document, c);
315 // Place tree inside scroll pane
316 JScrollPane scroll = new JScrollPane(tree);
317 panel.add(scroll,"spany, grow, wrap");
321 JButton button = new JButton(actions.getMoveUpAction());
322 panel.add(button,"sizegroup buttons, aligny 65%");
324 button = new JButton(actions.getMoveDownAction());
325 panel.add(button,"sizegroup buttons, aligny 0%");
327 button = new JButton(actions.getEditAction());
328 panel.add(button, "sizegroup buttons");
330 button = new JButton(actions.getNewStageAction());
331 panel.add(button,"sizegroup buttons");
333 button = new JButton(actions.getDeleteAction());
334 button.setIcon(null);
335 button.setMnemonic(0);
336 panel.add(button,"sizegroup buttons");
338 horizontal.setLeftComponent(panel);
341 // Upper-right segment, component addition buttons
343 panel = new JPanel(new MigLayout("fill, insets 0","[0::]"));
345 scroll = new JScrollPane(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
346 ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
347 scroll.setViewportView(new ComponentAddButtons(document, componentSelectionModel,
348 scroll.getViewport()));
349 scroll.setBorder(null);
350 scroll.setViewportBorder(null);
352 TitledBorder border = new TitledBorder("Add new component");
353 border.setTitleFont(border.getTitleFont().deriveFont(Font.BOLD));
354 scroll.setBorder(border);
356 panel.add(scroll,"grow");
358 horizontal.setRightComponent(panel);
366 * Creates the menu for the window.
368 private void createMenu() {
369 JMenuBar menubar = new JMenuBar();
374 menu = new JMenu("File");
375 menu.setMnemonic(KeyEvent.VK_F);
376 menu.getAccessibleContext().setAccessibleDescription("File-handling related tasks");
379 item = new JMenuItem("New",KeyEvent.VK_N);
380 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK));
381 item.setMnemonic(KeyEvent.VK_N);
382 item.getAccessibleContext().setAccessibleDescription("Create a new rocket design");
383 item.setIcon(Icons.FILE_NEW);
384 item.addActionListener(new ActionListener() {
385 public void actionPerformed(ActionEvent e) {
393 item = new JMenuItem("Open...",KeyEvent.VK_O);
394 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.CTRL_MASK));
395 item.getAccessibleContext().setAccessibleDescription("Open a rocket design");
396 item.setIcon(Icons.FILE_OPEN);
397 item.addActionListener(new ActionListener() {
398 public void actionPerformed(ActionEvent e) {
406 item = new JMenuItem("Save",KeyEvent.VK_S);
407 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK));
408 item.getAccessibleContext().setAccessibleDescription("Save the current rocket design");
409 item.setIcon(Icons.FILE_SAVE);
410 item.addActionListener(new ActionListener() {
411 public void actionPerformed(ActionEvent e) {
417 item = new JMenuItem("Save as...",KeyEvent.VK_A);
418 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
419 ActionEvent.CTRL_MASK | ActionEvent.SHIFT_MASK));
420 item.getAccessibleContext().setAccessibleDescription("Save the current rocket design "+
422 item.setIcon(Icons.FILE_SAVE_AS);
423 item.addActionListener(new ActionListener() {
424 public void actionPerformed(ActionEvent e) {
430 // menu.addSeparator();
431 menu.add(new JSeparator());
433 item = new JMenuItem("Close",KeyEvent.VK_C);
434 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, ActionEvent.CTRL_MASK));
435 item.getAccessibleContext().setAccessibleDescription("Close the current rocket design");
436 item.setIcon(Icons.FILE_CLOSE);
437 item.addActionListener(new ActionListener() {
438 public void actionPerformed(ActionEvent e) {
446 item = new JMenuItem("Quit",KeyEvent.VK_Q);
447 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, ActionEvent.CTRL_MASK));
448 item.getAccessibleContext().setAccessibleDescription("Quit the program");
449 item.setIcon(Icons.FILE_QUIT);
450 item.addActionListener(new ActionListener() {
451 public void actionPerformed(ActionEvent e) {
460 menu = new JMenu("Edit");
461 menu.setMnemonic(KeyEvent.VK_E);
462 menu.getAccessibleContext().setAccessibleDescription("Rocket editing");
466 Action action = document.getUndoAction();
467 item = new JMenuItem(action);
468 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, ActionEvent.CTRL_MASK));
469 item.setMnemonic(KeyEvent.VK_U);
470 item.getAccessibleContext().setAccessibleDescription("Undo the previous operation");
474 action = document.getRedoAction();
475 item = new JMenuItem(action);
476 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Y, ActionEvent.CTRL_MASK));
477 item.setMnemonic(KeyEvent.VK_R);
478 item.getAccessibleContext().setAccessibleDescription("Redo the previously undone " +
485 item = new JMenuItem(actions.getCutAction());
488 item = new JMenuItem(actions.getCopyAction());
491 item = new JMenuItem(actions.getPasteAction());
494 item = new JMenuItem(actions.getDeleteAction());
499 item = new JMenuItem("Preferences");
500 item.setIcon(Icons.PREFERENCES);
501 item.getAccessibleContext().setAccessibleDescription("Setup the application "+
503 item.addActionListener(new ActionListener() {
504 public void actionPerformed(ActionEvent e) {
505 PreferencesDialog.showPreferences();
514 menu = new JMenu("Analyze");
515 menu.setMnemonic(KeyEvent.VK_A);
516 menu.getAccessibleContext().setAccessibleDescription("Analyzing the rocket");
519 item = new JMenuItem("Component analysis",KeyEvent.VK_C);
520 item.getAccessibleContext().setAccessibleDescription("Analyze the rocket components " +
522 item.addActionListener(new ActionListener() {
523 public void actionPerformed(ActionEvent e) {
524 ComponentAnalysisDialog.showDialog(rocketpanel);
533 menu = new JMenu("Help");
534 menu.setMnemonic(KeyEvent.VK_H);
535 menu.getAccessibleContext().setAccessibleDescription("Information about OpenRocket");
540 item = new JMenuItem("License",KeyEvent.VK_L);
541 item.getAccessibleContext().setAccessibleDescription("OpenRocket license information");
542 item.addActionListener(new ActionListener() {
543 public void actionPerformed(ActionEvent e) {
544 new LicenseDialog(BasicFrame.this).setVisible(true);
549 item = new JMenuItem("Bug report",KeyEvent.VK_B);
550 item.getAccessibleContext().setAccessibleDescription("Information about reporting " +
551 "bugs in OpenRocket");
552 item.addActionListener(new ActionListener() {
553 public void actionPerformed(ActionEvent e) {
554 new BugDialog(BasicFrame.this).setVisible(true);
559 item = new JMenuItem("About",KeyEvent.VK_A);
560 item.getAccessibleContext().setAccessibleDescription("About OpenRocket");
561 item.addActionListener(new ActionListener() {
562 public void actionPerformed(ActionEvent e) {
563 new AboutDialog(BasicFrame.this).setVisible(true);
569 this.setJMenuBar(menubar);
575 * Select the tab on the main pane.
577 * @param tab one of {@link #COMPONENT_TAB} or {@link #SIMULATION_TAB}.
579 public void selectTab(int tab) {
580 tabbedPane.setSelectedIndex(tab);
585 private void openAction() {
586 JFileChooser chooser = new JFileChooser();
587 chooser.setFileFilter(ROCKET_DESIGN_FILTER);
588 chooser.setMultiSelectionEnabled(true);
589 chooser.setCurrentDirectory(Prefs.getDefaultDirectory());
590 if (chooser.showOpenDialog(this) != JFileChooser.APPROVE_OPTION)
593 Prefs.setDefaultDirectory(chooser.getCurrentDirectory());
595 File[] files = chooser.getSelectedFiles();
597 for (File file: files) {
598 System.out.println("Opening file: " + file);
599 if (open(file, this)) {
601 // Close previous window if replacing
602 if (replaceable && document.isSaved()) {
612 * Open the specified file in a new design frame. If an error occurs, an error dialog
613 * is shown and <code>false</code> is returned.
615 * @param file the file to open.
616 * @param parent the parent component for which a progress dialog is opened.
617 * @return whether the file was successfully loaded and opened.
619 private static boolean open(File file, Window parent) {
621 // Open the file in a Swing worker thread
622 OpenFileWorker worker = new OpenFileWorker(file);
623 if (!SwingWorkerDialog.runWorker(parent, "Opening file",
624 "Reading " + file.getName() + "...", worker)) {
626 // User cancelled the operation
631 // Handle the document
632 OpenRocketDocument doc = null;
637 } catch (ExecutionException e) {
639 Throwable cause = e.getCause();
641 if (cause instanceof FileNotFoundException) {
643 JOptionPane.showMessageDialog(parent,
644 "File not found: " + file.getName(),
645 "Error opening file", JOptionPane.ERROR_MESSAGE);
648 } else if (cause instanceof RocketLoadException) {
650 JOptionPane.showMessageDialog(parent,
651 "Unable to open file '" + file.getName() +"': "
652 + cause.getMessage(),
653 "Error opening file", JOptionPane.ERROR_MESSAGE);
658 throw new RuntimeException("Unknown error when opening file", e);
662 } catch (InterruptedException e) {
663 throw new RuntimeException("EDT was interrupted", e);
667 throw new RuntimeException("BUG: Document loader returned null");
672 Iterator<Warning> warns = ROCKET_LOADER.getWarnings().iterator();
673 System.out.println("Warnings:");
674 while (warns.hasNext()) {
675 System.out.println(" "+warns.next());
676 // TODO: HIGH: dialog
679 // Set document state
684 BasicFrame frame = new BasicFrame(doc);
685 frame.setVisible(true);
698 private boolean saveAction() {
699 File file = document.getFile();
701 return saveAsAction();
708 private boolean saveAsAction() {
710 while (file == null) {
711 StorageOptionChooser storageChooser =
712 new StorageOptionChooser(document, document.getDefaultStorageOptions());
713 JFileChooser chooser = new JFileChooser();
714 chooser.setFileFilter(ROCKET_DESIGN_FILTER);
715 chooser.setCurrentDirectory(Prefs.getDefaultDirectory());
716 chooser.setAccessory(storageChooser);
717 if (document.getFile() != null)
718 chooser.setSelectedFile(document.getFile());
720 if (chooser.showSaveDialog(BasicFrame.this) != JFileChooser.APPROVE_OPTION)
723 file = chooser.getSelectedFile();
727 Prefs.setDefaultDirectory(chooser.getCurrentDirectory());
728 storageChooser.storeOptions(document.getDefaultStorageOptions());
730 if (file.getName().indexOf('.') < 0) {
731 String name = file.getAbsolutePath();
732 name = name + ".ork";
733 file = new File(name);
737 int result = JOptionPane.showConfirmDialog(this,
738 "File '"+file.getName()+"' exists. Do you want to overwrite it?",
739 "File exists", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
740 if (result != JOptionPane.YES_OPTION)
749 private boolean saveAs(File file) {
750 System.out.println("Saving to file: " + file.getName());
751 boolean saved = false;
753 if (!StorageOptionChooser.verifyStorageOptions(document, this)) {
754 // User cancelled the dialog
759 SaveFileWorker worker = new SaveFileWorker(document, file, ROCKET_SAVER);
761 if (!SwingWorkerDialog.runWorker(this, "Saving file",
762 "Writing " + file.getName() + "...", worker)) {
764 // User cancelled the save
771 document.setFile(file);
772 document.setSaved(true);
775 } catch (ExecutionException e) {
776 Throwable cause = e.getCause();
778 if (cause instanceof IOException) {
779 JOptionPane.showMessageDialog(this, new String[] {
780 "An I/O error occurred while saving:",
781 e.getMessage() }, "Saving failed", JOptionPane.ERROR_MESSAGE);
784 throw new RuntimeException("Unknown error when saving file", e);
787 } catch (InterruptedException e) {
788 throw new RuntimeException("EDT was interrupted", e);
795 private boolean closeAction() {
796 if (!document.isSaved()) {
797 ComponentConfigDialog.hideDialog();
798 int result = JOptionPane.showConfirmDialog(this,
799 "Design '"+rocket.getName()+"' has not been saved. " +
800 "Do you want to save it?",
801 "Design not saved", JOptionPane.YES_NO_CANCEL_OPTION,
802 JOptionPane.QUESTION_MESSAGE);
803 if (result == JOptionPane.YES_OPTION) {
806 return false; // If save was interrupted
807 } else if (result == JOptionPane.NO_OPTION) {
815 // Rocket has been saved or discarded
818 // TODO: LOW: Close only dialogs that have this frame as their parent
819 ComponentConfigDialog.hideDialog();
820 ComponentAnalysisDialog.hideDialog();
823 if (frames.isEmpty())
830 * Closes this frame if it is replaceable.
832 public void closeIfReplaceable() {
833 if (this.replaceable && document.isSaved()) {
839 * Open a new design window with a basic rocket+stage.
841 public static void newAction() {
842 Rocket rocket = new Rocket();
843 Stage stage = new Stage();
844 stage.setName("Sustainer");
845 rocket.addChild(stage);
846 OpenRocketDocument doc = new OpenRocketDocument(rocket);
849 BasicFrame frame = new BasicFrame(doc);
850 frame.replaceable = true;
851 frame.setVisible(true);
852 ComponentConfigDialog.showDialog(frame, doc, rocket);
856 * Quit the application. Confirms saving unsaved designs. The action of File->Quit.
858 public static void quitAction() {
859 for (int i=frames.size()-1; i>=0; i--) {
860 if (!frames.get(i).closeAction()) {
865 // Should not be reached, but just in case
871 * Set the title of the frame, taking into account the name of the rocket, file it
872 * has been saved to (if any) and saved status.
874 private void setTitle() {
875 File file = document.getFile();
876 boolean saved = document.isSaved();
879 title = rocket.getName();
881 title = title + " ("+file.getName()+")";
892 * Find a currently open BasicFrame containing the specified rocket. This method
893 * can be used to map a Rocket to a BasicFrame from GUI methods.
895 * @param rocket the Rocket.
896 * @return the corresponding BasicFrame, or <code>null</code> if none found.
898 public static BasicFrame findFrame(Rocket rocket) {
899 for (BasicFrame f: frames) {
900 if (f.rocket == rocket)
907 * Find a currently open document by the rocket object. This method can be used
908 * to map a Rocket to OpenRocketDocument from GUI methods.
910 * @param rocket the Rocket.
911 * @return the corresponding OpenRocketDocument, or <code>null</code> if not found.
913 public static OpenRocketDocument findDocument(Rocket rocket) {
914 for (BasicFrame f: frames) {
915 if (f.rocket == rocket)
923 public static void main(String[] args) {
926 * Set the look-and-feel. On Linux, Motif/Metal is sometimes incorrectly used
927 * which is butt-ugly, so if the system l&f is Motif/Metal, we search for a few
928 * other alternatives.
931 UIManager.LookAndFeelInfo[] info = UIManager.getInstalledLookAndFeels();
932 // System.out.println("Available look-and-feels:");
933 // for (int i=0; i<info.length; i++) {
934 // System.out.println(" "+info[i]);
938 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
940 // Check whether we have an ugly L&F
941 LookAndFeel laf = UIManager.getLookAndFeel();
943 laf.getName().matches(".*[mM][oO][tT][iI][fF].*") ||
944 laf.getName().matches(".*[mM][eE][tT][aA][lL].*")) {
946 // Search for better LAF
947 for (UIManager.LookAndFeelInfo l: info) {
948 if (l.getName().matches(".*[gG][tT][kK].*")) {
949 UIManager.setLookAndFeel(l.getClassName());
952 if (l.getName().contains(".*[wW][iI][nN].*")) {
953 UIManager.setLookAndFeel(l.getClassName());
956 if (l.getName().contains(".*[mM][aA][cC].*")) {
957 UIManager.setLookAndFeel(l.getClassName());
962 } catch (Exception e) {
963 System.err.println("Error setting LAF: " + e);
966 // Set tooltip delay time. Tooltips are used in MotorChooserDialog extensively.
967 ToolTipManager.sharedInstance().setDismissDelay(30000);
971 Prefs.loadDefaultUnits();
974 // Check command-line for files
975 boolean opened = false;
976 for (String file: args) {
977 if (open(new File(file), null)) {