Fixing various issues

This commit is contained in:
Sampo Niskanen 2012-02-19 08:23:22 +00:00
parent b1891e1dc1
commit 88f359c25f
10 changed files with 337 additions and 289 deletions

View File

@ -1,3 +1,7 @@
2012-02-16 Sampo Niskanen
* [BUG] Freeze when dropping component on child component
2012-02-10 Sampo Niskanen 2012-02-10 Sampo Niskanen
* Configurable stage separation events * Configurable stage separation events

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -36,4 +36,5 @@ The following file format versions exist:
1.4: Introduced with OpenRocket 1.1.10. Adds the launchrodvelocity and 1.4: Introduced with OpenRocket 1.1.10. Adds the launchrodvelocity and
deploymentvelocity attributes to <flightdata> element. The motor deploymentvelocity attributes to <flightdata> element. The motor
digesting algorithm was changed. digesting algorithm was changed. Adds <separationevent> and
<separationdelay> elements to stage components (except sustainer).

View File

@ -34,3 +34,4 @@ help-license.png
help-log.png help-log.png
help-about.png help-about.png
help-bug.png help-bug.png
help-tours.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 837 B

View File

@ -360,7 +360,6 @@ public class RockSimMotorLoader extends AbstractMotorLoader {
if (!calculateCG) { if (!calculateCG) {
motorDigest.update(DataType.CG_PER_TIME, toArray(cg)); motorDigest.update(DataType.CG_PER_TIME, toArray(cg));
} }
// FIXME: Should this use CG_SPECIFIC ???
motorDigest.update(DataType.FORCE_PER_TIME, thrustArray); motorDigest.update(DataType.FORCE_PER_TIME, thrustArray);
final String digest = motorDigest.getDigest(); final String digest = motorDigest.getDigest();

View File

@ -1,5 +1,62 @@
package net.sf.openrocket.gui.main; 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.miginfocom.swing.MigLayout;
import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.aerodynamics.WarningSet;
import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.OpenRocketDocument;
@ -48,38 +105,6 @@ import net.sf.openrocket.util.MemoryManagement.MemoryData;
import net.sf.openrocket.util.Reflection; import net.sf.openrocket.util.Reflection;
import net.sf.openrocket.util.TestRockets; import net.sf.openrocket.util.TestRockets;
import javax.swing.*;
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.*;
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 { public class BasicFrame extends JFrame {
private static final LogHelper log = Application.getLogger(); private static final LogHelper log = Application.getLogger();
@ -95,22 +120,22 @@ public class BasicFrame extends JFrame {
public static final int COMPONENT_TAB = 0; public static final int COMPONENT_TAB = 0;
public static final int SIMULATION_TAB = 1; public static final int SIMULATION_TAB = 1;
/** /**
* List of currently open frames. When the list goes empty * List of currently open frames. When the list goes empty
* it is time to exit the application. * it is time to exit the application.
*/ */
private static final ArrayList<BasicFrame> frames = new ArrayList<BasicFrame>(); private static final ArrayList<BasicFrame> frames = new ArrayList<BasicFrame>();
/** /**
* Whether "New" and "Open" should replace this frame. * Whether "New" and "Open" should replace this frame.
* Should be set to false on the first rocket modification. * Should be set to false on the first rocket modification.
*/ */
private boolean replaceable = false; private boolean replaceable = false;
private final OpenRocketDocument document; private final OpenRocketDocument document;
private final Rocket rocket; private final Rocket rocket;
@ -126,7 +151,7 @@ public class BasicFrame extends JFrame {
private final RocketActions actions; private final RocketActions actions;
/** /**
* Sole constructor. Creates a new frame based on the supplied document * Sole constructor. Creates a new frame based on the supplied document
* and adds it to the current frames list. * and adds it to the current frames list.
@ -140,7 +165,7 @@ public class BasicFrame extends JFrame {
this.rocket = document.getRocket(); this.rocket = document.getRocket();
this.rocket.getDefaultConfiguration().setAllStages(); this.rocket.getDefaultConfiguration().setAllStages();
// Set replaceable flag to false at first modification // Set replaceable flag to false at first modification
rocket.addComponentChangeListener(new ComponentChangeListener() { rocket.addComponentChangeListener(new ComponentChangeListener() {
@Override @Override
@ -150,7 +175,7 @@ public class BasicFrame extends JFrame {
} }
}); });
// Create the component tree selection model that will be used // Create the component tree selection model that will be used
componentSelectionModel = new DefaultTreeSelectionModel(); componentSelectionModel = new DefaultTreeSelectionModel();
componentSelectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); componentSelectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
@ -164,10 +189,10 @@ public class BasicFrame extends JFrame {
selectionModel.attachComponentTreeSelectionModel(componentSelectionModel); selectionModel.attachComponentTreeSelectionModel(componentSelectionModel);
selectionModel.attachSimulationListSelectionModel(simulationSelectionModel); selectionModel.attachSimulationListSelectionModel(simulationSelectionModel);
actions = new RocketActions(document, selectionModel, this); actions = new RocketActions(document, selectionModel, this);
log.debug("Constructing the BasicFrame UI"); log.debug("Constructing the BasicFrame UI");
// The main vertical split pane // The main vertical split pane
@ -175,7 +200,7 @@ public class BasicFrame extends JFrame {
vertical.setResizeWeight(0.5); vertical.setResizeWeight(0.5);
this.add(vertical); this.add(vertical);
// The top tabbed pane // The top tabbed pane
tabbedPane = new JTabbedPane(); tabbedPane = new JTabbedPane();
//// Rocket design //// Rocket design
@ -185,8 +210,8 @@ public class BasicFrame extends JFrame {
vertical.setTopComponent(tabbedPane); vertical.setTopComponent(tabbedPane);
// Bottom segment, rocket figure // Bottom segment, rocket figure
rocketpanel = new RocketPanel(document); rocketpanel = new RocketPanel(document);
@ -194,10 +219,10 @@ public class BasicFrame extends JFrame {
rocketpanel.setSelectionModel(tree.getSelectionModel()); rocketpanel.setSelectionModel(tree.getSelectionModel());
createMenu(); createMenu();
rocket.addComponentChangeListener(new ComponentChangeListener() { rocket.addComponentChangeListener(new ComponentChangeListener() {
@Override @Override
public void componentChanged(ComponentChangeEvent e) { public void componentChanged(ComponentChangeEvent e) {
@ -208,7 +233,7 @@ public class BasicFrame extends JFrame {
setTitle(); setTitle();
this.pack(); this.pack();
// Set initial window size // Set initial window size
Dimension size = Toolkit.getDefaultToolkit().getScreenSize(); Dimension size = Toolkit.getDefaultToolkit().getScreenSize();
size.width = size.width * 9 / 10; size.width = size.width * 9 / 10;
@ -246,7 +271,7 @@ public class BasicFrame extends JFrame {
JSplitPane horizontal = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true); JSplitPane horizontal = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true);
horizontal.setResizeWeight(0.5); horizontal.setResizeWeight(0.5);
// Upper-left segment, component tree // Upper-left segment, component tree
JPanel panel = new JPanel(new MigLayout("fill, flowy", "", "[grow]")); JPanel panel = new JPanel(new MigLayout("fill, flowy", "", "[grow]"));
@ -264,8 +289,8 @@ public class BasicFrame extends JFrame {
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_O, 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); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK), null);
// Double-click opens config dialog // Double-click opens config dialog
MouseListener ml = new MouseAdapter() { MouseListener ml = new MouseAdapter() {
@Override @Override
@ -306,7 +331,7 @@ public class BasicFrame extends JFrame {
JScrollPane scroll = new JScrollPane(tree); JScrollPane scroll = new JScrollPane(tree);
panel.add(scroll, "spany, grow, wrap"); panel.add(scroll, "spany, grow, wrap");
// Buttons // Buttons
JButton button = new JButton(actions.getMoveUpAction()); JButton button = new JButton(actions.getMoveUpAction());
panel.add(button, "sizegroup buttons, aligny 65%"); panel.add(button, "sizegroup buttons, aligny 65%");
@ -327,7 +352,7 @@ public class BasicFrame extends JFrame {
horizontal.setLeftComponent(panel); horizontal.setLeftComponent(panel);
// Upper-right segment, component addition buttons // Upper-right segment, component addition buttons
panel = new JPanel(new MigLayout("fill, insets 0", "[0::]")); panel = new JPanel(new MigLayout("fill, insets 0", "[0::]"));
@ -351,7 +376,7 @@ public class BasicFrame extends JFrame {
} }
/** /**
* Return the currently selected rocket component, or <code>null</code> if none selected. * Return the currently selected rocket component, or <code>null</code> if none selected.
*/ */
@ -485,7 +510,7 @@ public class BasicFrame extends JFrame {
}); });
menu.add(item); menu.add(item);
menu.addSeparator(); menu.addSeparator();
//// Close //// Close
@ -520,8 +545,8 @@ public class BasicFrame extends JFrame {
}); });
menu.add(item); menu.add(item);
//// Edit //// Edit
menu = new JMenu(trans.get("main.menu.edit")); menu = new JMenu(trans.get("main.menu.edit"));
menu.setMnemonic(KeyEvent.VK_E); menu.setMnemonic(KeyEvent.VK_E);
@ -529,7 +554,7 @@ public class BasicFrame extends JFrame {
menu.getAccessibleContext().setAccessibleDescription(trans.get("BasicFrame.menu.Rocketedt")); menu.getAccessibleContext().setAccessibleDescription(trans.get("BasicFrame.menu.Rocketedt"));
menubar.add(menu); menubar.add(menu);
Action action = UndoRedoAction.newUndoAction(document); Action action = UndoRedoAction.newUndoAction(document);
item = new JMenuItem(action); item = new JMenuItem(action);
item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, ActionEvent.CTRL_MASK)); item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, ActionEvent.CTRL_MASK));
@ -538,7 +563,7 @@ public class BasicFrame extends JFrame {
item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.edit.undo.desc")); item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.edit.undo.desc"));
menu.add(item); menu.add(item);
action = UndoRedoAction.newRedoAction(document); action = UndoRedoAction.newRedoAction(document);
item = new JMenuItem(action); item = new JMenuItem(action);
item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Y, ActionEvent.CTRL_MASK)); item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Y, ActionEvent.CTRL_MASK));
@ -549,7 +574,7 @@ public class BasicFrame extends JFrame {
menu.addSeparator(); menu.addSeparator();
item = new JMenuItem(actions.getCutAction()); item = new JMenuItem(actions.getCutAction());
menu.add(item); menu.add(item);
@ -564,8 +589,8 @@ public class BasicFrame extends JFrame {
menu.addSeparator(); menu.addSeparator();
item = new JMenuItem(trans.get("main.menu.edit.resize")); item = new JMenuItem(trans.get("main.menu.edit.resize"));
item.setIcon(Icons.EDIT_SCALE); item.setIcon(Icons.EDIT_SCALE);
item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.edit.resize.desc")); item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.edit.resize.desc"));
@ -580,8 +605,8 @@ public class BasicFrame extends JFrame {
}); });
menu.add(item); menu.add(item);
//// Preferences //// Preferences
item = new JMenuItem(trans.get("main.menu.edit.preferences")); item = new JMenuItem(trans.get("main.menu.edit.preferences"));
item.setIcon(Icons.PREFERENCES); item.setIcon(Icons.PREFERENCES);
@ -596,9 +621,9 @@ public class BasicFrame extends JFrame {
}); });
menu.add(item); menu.add(item);
//// Analyze //// Analyze
menu = new JMenu(trans.get("main.menu.analyze")); menu = new JMenu(trans.get("main.menu.analyze"));
menu.setMnemonic(KeyEvent.VK_A); menu.setMnemonic(KeyEvent.VK_A);
@ -619,7 +644,7 @@ public class BasicFrame extends JFrame {
}); });
menu.add(item); menu.add(item);
item = new JMenuItem(trans.get("main.menu.analyze.optimization"), KeyEvent.VK_O); item = new JMenuItem(trans.get("main.menu.analyze.optimization"), KeyEvent.VK_O);
item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.analyze.optimization.desc")); item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.analyze.optimization.desc"));
item.addActionListener(new ActionListener() { item.addActionListener(new ActionListener() {
@ -631,16 +656,16 @@ public class BasicFrame extends JFrame {
}); });
menu.add(item); menu.add(item);
//// Debug //// Debug
// (shown if openrocket.debug.menu is defined) // (shown if openrocket.debug.menu is defined)
if (System.getProperty("openrocket.debug.menu") != null) { if (System.getProperty("openrocket.debug.menu") != null) {
menubar.add(makeDebugMenu()); menubar.add(makeDebugMenu());
} }
//// Help //// Help
menu = new JMenu(trans.get("main.menu.help")); menu = new JMenu(trans.get("main.menu.help"));
@ -648,11 +673,11 @@ public class BasicFrame extends JFrame {
menu.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.desc")); menu.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.desc"));
menubar.add(menu); menubar.add(menu);
// Guided tours // Guided tours
item = new JMenuItem(trans.get("main.menu.help.tours"), KeyEvent.VK_L); 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.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.tours.desc"));
item.addActionListener(new ActionListener() { item.addActionListener(new ActionListener() {
@Override @Override
@ -666,22 +691,6 @@ public class BasicFrame extends JFrame {
menu.addSeparator(); 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 //// Bug report
item = new JMenuItem(trans.get("main.menu.help.bugReport"), KeyEvent.VK_B); item = new JMenuItem(trans.get("main.menu.help.bugReport"), KeyEvent.VK_B);
item.setIcon(Icons.HELP_BUG_REPORT); item.setIcon(Icons.HELP_BUG_REPORT);
@ -710,6 +719,21 @@ public class BasicFrame extends JFrame {
menu.addSeparator(); 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 //// About
item = new JMenuItem(trans.get("main.menu.help.about"), KeyEvent.VK_A); item = new JMenuItem(trans.get("main.menu.help.about"), KeyEvent.VK_A);
item.setIcon(Icons.HELP_ABOUT); item.setIcon(Icons.HELP_ABOUT);
@ -723,7 +747,7 @@ public class BasicFrame extends JFrame {
}); });
menu.add(item); menu.add(item);
this.setJMenuBar(menubar); this.setJMenuBar(menubar);
} }
@ -734,7 +758,7 @@ public class BasicFrame extends JFrame {
/* /*
* This menu is intentionally left untranslated. * This menu is intentionally left untranslated.
*/ */
//// Debug menu //// Debug menu
menu = new JMenu("Debug"); menu = new JMenu("Debug");
//// OpenRocket debugging tasks //// OpenRocket debugging tasks
@ -770,10 +794,10 @@ public class BasicFrame extends JFrame {
int sel = JOptionPane.showOptionDialog(BasicFrame.this, new Object[] { int sel = JOptionPane.showOptionDialog(BasicFrame.this, new Object[] {
"Input text key to generate random rocket:", "Input text key to generate random rocket:",
field field
}, "Generate random test rocket", JOptionPane.DEFAULT_OPTION, }, "Generate random test rocket", JOptionPane.DEFAULT_OPTION,
JOptionPane.QUESTION_MESSAGE, null, new Object[] { JOptionPane.QUESTION_MESSAGE, null, new Object[] {
"Random", "OK" "Random", "OK"
}, "OK"); }, "OK");
Rocket r; Rocket r;
if (sel == 0) { if (sel == 0) {
@ -792,8 +816,8 @@ public class BasicFrame extends JFrame {
}); });
menu.add(item); menu.add(item);
item = new JMenuItem("Create 'Iso-Haisu'"); item = new JMenuItem("Create 'Iso-Haisu'");
item.addActionListener(new ActionListener() { item.addActionListener(new ActionListener() {
@Override @Override
@ -808,7 +832,7 @@ public class BasicFrame extends JFrame {
}); });
menu.add(item); menu.add(item);
item = new JMenuItem("Create 'Big Blue'"); item = new JMenuItem("Create 'Big Blue'");
item.addActionListener(new ActionListener() { item.addActionListener(new ActionListener() {
@Override @Override
@ -825,7 +849,7 @@ public class BasicFrame extends JFrame {
menu.addSeparator(); menu.addSeparator();
item = new JMenuItem("Memory statistics"); item = new JMenuItem("Memory statistics");
item.addActionListener(new ActionListener() { item.addActionListener(new ActionListener() {
@Override @Override
@ -860,7 +884,7 @@ public class BasicFrame extends JFrame {
stats[2] = String.format(" Used memory: %.1f MB (%.0f%%)", used / 1024.0 / 1024.0, 100.0 * used / max); 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); 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(), DetailDialog.showDetailedMessageDialog(BasicFrame.this, stats, sb.toString(),
"Memory statistics", JOptionPane.INFORMATION_MESSAGE); "Memory statistics", JOptionPane.INFORMATION_MESSAGE);
} }
@ -897,7 +921,7 @@ public class BasicFrame extends JFrame {
}); });
menu.add(item); menu.add(item);
menu.addSeparator(); menu.addSeparator();
//// Exception here //// Exception here
@ -952,10 +976,10 @@ public class BasicFrame extends JFrame {
}); });
menu.add(item); menu.add(item);
menu.addSeparator(); menu.addSeparator();
item = new JMenuItem("Test popup"); item = new JMenuItem("Test popup");
item.addActionListener(new ActionListener() { item.addActionListener(new ActionListener() {
@Override @Override
@ -972,9 +996,9 @@ public class BasicFrame extends JFrame {
}); });
menu.add(item); menu.add(item);
return menu; return menu;
} }
@ -989,7 +1013,7 @@ public class BasicFrame extends JFrame {
} }
private void openAction() { private void openAction() {
JFileChooser chooser = new JFileChooser(); JFileChooser chooser = new JFileChooser();
@ -1067,7 +1091,7 @@ public class BasicFrame extends JFrame {
filename = filename.substring(filename.lastIndexOf('/') + 1); filename = filename.substring(filename.lastIndexOf('/') + 1);
} }
// Open the file // Open the file
log.info("Opening file from url=" + url + " filename=" + filename); log.info("Opening file from url=" + url + " filename=" + filename);
try { try {
@ -1140,7 +1164,7 @@ public class BasicFrame extends JFrame {
return false; return false;
} }
// Handle the document // Handle the document
OpenRocketDocument doc = null; OpenRocketDocument doc = null;
try { try {
@ -1182,7 +1206,7 @@ public class BasicFrame extends JFrame {
throw new BugException("Document loader returned null"); throw new BugException("Document loader returned null");
} }
// Show warnings // Show warnings
WarningSet warnings = worker.getRocketLoader().getWarnings(); WarningSet warnings = worker.getRocketLoader().getWarnings();
if (!warnings.isEmpty()) { if (!warnings.isEmpty()) {
@ -1198,12 +1222,12 @@ public class BasicFrame extends JFrame {
trans.get("BasicFrame.WarningDialog.title"), warnings); trans.get("BasicFrame.WarningDialog.title"), warnings);
} }
// Set document state // Set document state
doc.setFile(file); doc.setFile(file);
doc.setSaved(true); doc.setSaved(true);
// Open the frame // Open the frame
log.debug("Opening new frame with the document"); log.debug("Opening new frame with the document");
BasicFrame frame = new BasicFrame(doc); BasicFrame frame = new BasicFrame(doc);
@ -1211,15 +1235,15 @@ public class BasicFrame extends JFrame {
return true; return true;
} }
/** /**
* "Save" action. If the design is new, then this is identical to "Save As", with a default file filter for .ork. * "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 * 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 * 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). * 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 * @return true if the file was saved, false otherwise
*/ */
private boolean saveAction() { private boolean saveAction() {
File file = document.getFile(); File file = document.getFile();
if (file == null) { if (file == null) {
@ -1229,45 +1253,45 @@ public class BasicFrame extends JFrame {
log.info("Saving document to " + file); log.info("Saving document to " + file);
if (FileHelper.ROCKSIM_DESIGN_FILTER.accept(file)) { if (FileHelper.ROCKSIM_DESIGN_FILTER.accept(file)) {
return saveAsRocksim(file); return saveAsRocksim(file);
} }
return saveAs(file); return saveAs(file);
} }
/** /**
* "Save As" action. * "Save As" action.
* *
* Never should a .rkt file contain an OpenRocket content, or an .ork file contain a Rocksim design. Regardless of * 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 * 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. * (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 * 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. * 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 * @return true if the file was saved, false otherwise
*/ */
private boolean saveAsAction() { private boolean saveAsAction() {
File file = null; File file = null;
StorageOptionChooser storageChooser = StorageOptionChooser storageChooser =
new StorageOptionChooser(document, document.getDefaultStorageOptions()); new StorageOptionChooser(document, document.getDefaultStorageOptions());
final JFileChooser chooser = new JFileChooser(); final JFileChooser chooser = new JFileChooser();
chooser.addChoosableFileFilter(FileHelper.OPENROCKET_DESIGN_FILTER); chooser.addChoosableFileFilter(FileHelper.OPENROCKET_DESIGN_FILTER);
chooser.addChoosableFileFilter(FileHelper.ROCKSIM_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. //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())) { if (FileHelper.ROCKSIM_DESIGN_FILTER.accept(document.getFile())) {
chooser.setFileFilter(FileHelper.ROCKSIM_DESIGN_FILTER); chooser.setFileFilter(FileHelper.ROCKSIM_DESIGN_FILTER);
} }
else { else {
chooser.setFileFilter(FileHelper.OPENROCKET_DESIGN_FILTER); chooser.setFileFilter(FileHelper.OPENROCKET_DESIGN_FILTER);
} }
chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory()); chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory());
chooser.setAccessory(storageChooser); chooser.setAccessory(storageChooser);
if (document.getFile() != null) { if (document.getFile() != null) {
chooser.setSelectedFile(document.getFile()); chooser.setSelectedFile(document.getFile());
} }
int option = chooser.showSaveDialog(BasicFrame.this); int option = chooser.showSaveDialog(BasicFrame.this);
if (option != JFileChooser.APPROVE_OPTION) { if (option != JFileChooser.APPROVE_OPTION) {
log.user("User decided not to save, option=" + option); log.user("User decided not to save, option=" + option);
@ -1282,45 +1306,45 @@ public class BasicFrame extends JFrame {
((SwingPreferences) Application.getPreferences()).setDefaultDirectory(chooser.getCurrentDirectory()); ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(chooser.getCurrentDirectory());
storageChooser.storeOptions(document.getDefaultStorageOptions()); storageChooser.storeOptions(document.getDefaultStorageOptions());
if (chooser.getFileFilter().equals(FileHelper.ROCKSIM_DESIGN_FILTER)) { if (chooser.getFileFilter().equals(FileHelper.ROCKSIM_DESIGN_FILTER)) {
return saveAsRocksim(file); return saveAsRocksim(file);
} }
else { else {
file = FileHelper.forceExtension(file, "ork"); file = FileHelper.forceExtension(file, "ork");
return FileHelper.confirmWrite(file, this) && saveAs(file); return FileHelper.confirmWrite(file, this) && saveAs(file);
} }
} }
/** /**
* Perform the writing of the design to the given file in Rocksim format. * Perform the writing of the design to the given file in Rocksim format.
* *
* @param file the chosen file * @param file the chosen file
* *
* @return true if the file was written * @return true if the file was written
*/ */
private boolean saveAsRocksim(File file) { private boolean saveAsRocksim(File file) {
file = FileHelper.forceExtension(file, "rkt"); file = FileHelper.forceExtension(file, "rkt");
if (!FileHelper.confirmWrite(file, this)) { if (!FileHelper.confirmWrite(file, this)) {
return false; return false;
} }
try { try {
new RocksimSaver().save(file, document); new RocksimSaver().save(file, document);
return true; return true;
} catch (IOException e) { } catch (IOException e) {
return false; return false;
} }
} }
/** /**
* Perform the writing of the design to the given file in OpenRocket format. * Perform the writing of the design to the given file in OpenRocket format.
* *
* @param file the chosen file * @param file the chosen file
* *
* @return true if the file was written * @return true if the file was written
*/ */
private boolean saveAs(File file) { private boolean saveAs(File file) {
log.info("Saving document as " + file); log.info("Saving document as " + file);
boolean saved = false; boolean saved = false;
@ -1330,7 +1354,7 @@ public class BasicFrame extends JFrame {
return false; return false;
} }
SaveFileWorker worker = new SaveFileWorker(document, file, ROCKET_SAVER); SaveFileWorker worker = new SaveFileWorker(document, file, ROCKET_SAVER);
if (!SwingWorkerDialog.runWorker(this, "Saving file", if (!SwingWorkerDialog.runWorker(this, "Saving file",
@ -1413,7 +1437,7 @@ public class BasicFrame extends JFrame {
} }
/** /**
* *
*/ */
@ -1480,7 +1504,7 @@ public class BasicFrame extends JFrame {
} }
/** /**
* Find a currently open BasicFrame containing the specified rocket. This method * Find a currently open BasicFrame containing the specified rocket. This method
* can be used to map a Rocket to a BasicFrame from GUI methods. * can be used to map a Rocket to a BasicFrame from GUI methods.

View File

@ -32,6 +32,7 @@ public class ComponentTreeTransferHandler extends TransferHandler {
private final OpenRocketDocument document; private final OpenRocketDocument document;
/** /**
* Sole constructor. * Sole constructor.
* *
@ -69,15 +70,15 @@ public class ComponentTreeTransferHandler extends TransferHandler {
} }
@Override @Override
public void exportDone(JComponent comp, Transferable trans, int action) { public void exportDone(JComponent comp, Transferable trans, int action) {
// Removal from the old place is implemented already in import, so do nothing // Removal from the old place is implemented already in import, so do nothing
} }
@Override @Override
public boolean canImport(TransferHandler.TransferSupport support) { public boolean canImport(TransferHandler.TransferSupport support) {
SourceTarget data = getSourceAndTarget(support); SourceTarget data = getSourceAndTarget(support);
@ -89,6 +90,17 @@ public class ComponentTreeTransferHandler extends TransferHandler {
boolean allowed = data.destParent.isCompatible(data.child); boolean allowed = data.destParent.isCompatible(data.child);
log.verbose("Checking validity of drag-drop " + data.toString() + " allowed:" + allowed); log.verbose("Checking validity of drag-drop " + data.toString() + " allowed:" + allowed);
// Ensure we're not dropping a component onto a child component
RocketComponent path = data.destParent;
while (path != null) {
if (path.equals(data.child)) {
log.verbose("Drop would cause cycle in tree, disallowing.");
allowed = false;
break;
}
path = path.getParent();
}
// If drag-dropping to another rocket always copy // If drag-dropping to another rocket always copy
if (support.getDropAction() == MOVE && data.srcParent.getRoot() != data.destParent.getRoot()) { if (support.getDropAction() == MOVE && data.srcParent.getRoot() != data.destParent.getRoot()) {
support.setDropAction(COPY); support.setDropAction(COPY);
@ -98,7 +110,6 @@ public class ComponentTreeTransferHandler extends TransferHandler {
} }
@Override @Override
public boolean importData(TransferHandler.TransferSupport support) { public boolean importData(TransferHandler.TransferSupport support) {
@ -121,7 +132,7 @@ public class ComponentTreeTransferHandler extends TransferHandler {
action = TransferHandler.COPY; action = TransferHandler.COPY;
} }
// Check whether move action would be a no-op // Check whether move action would be a no-op
if ((action == MOVE) && (data.srcParent == data.destParent) && if ((action == MOVE) && (data.srcParent == data.destParent) &&
(data.destIndex == data.srcIndex || data.destIndex == data.srcIndex + 1)) { (data.destIndex == data.srcIndex || data.destIndex == data.srcIndex + 1)) {
@ -129,7 +140,7 @@ public class ComponentTreeTransferHandler extends TransferHandler {
return false; return false;
} }
switch (action) { switch (action) {
case MOVE: case MOVE:
log.user("Performing DnD move operation: " + data); log.user("Performing DnD move operation: " + data);
@ -184,7 +195,7 @@ public class ComponentTreeTransferHandler extends TransferHandler {
} }
/** /**
* Fetch the source and target for the DnD action. This method does not perform * Fetch the source and target for the DnD action. This method does not perform
* checks on whether this action is allowed based on component positioning rules. * checks on whether this action is allowed based on component positioning rules.
@ -214,7 +225,7 @@ public class ComponentTreeTransferHandler extends TransferHandler {
} }
MyDropLocation location = convertDropLocation((JTree) support.getComponent(), dl); MyDropLocation location = convertDropLocation((JTree) support.getComponent(), dl);
// Fetch the transferred component (child component) // Fetch the transferred component (child component)
Transferable transferable = support.getTransferable(); Transferable transferable = support.getTransferable();
RocketComponent child; RocketComponent child;
@ -228,7 +239,7 @@ public class ComponentTreeTransferHandler extends TransferHandler {
throw new BugException(e); throw new BugException(e);
} }
// Get the source component & index // Get the source component & index
RocketComponent srcParent = child.getParent(); RocketComponent srcParent = child.getParent();
if (srcParent == null) { if (srcParent == null) {
@ -237,7 +248,7 @@ public class ComponentTreeTransferHandler extends TransferHandler {
} }
int srcIndex = srcParent.getChildPosition(child); int srcIndex = srcParent.getChildPosition(child);
// Get destination component & index // Get destination component & index
RocketComponent destParent = ComponentTreeModel.componentFromPath(location.path); RocketComponent destParent = ComponentTreeModel.componentFromPath(location.path);
int destIndex = location.index; int destIndex = location.index;
@ -278,7 +289,7 @@ public class ComponentTreeTransferHandler extends TransferHandler {
} }
/** /**
* Convert the JTree drop location in order to work around bug 6560955 * Convert the JTree drop location in order to work around bug 6560955
* (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6560955). * (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6560955).

View File

@ -43,7 +43,7 @@ public class Icons {
SIMULATION_LISTENER_ERROR = SIMULATION_STATUS_ICON_MAP.get(Simulation.Status.OUTDATED); SIMULATION_LISTENER_ERROR = SIMULATION_STATUS_ICON_MAP.get(Simulation.Status.OUTDATED);
} }
public static final Icon FILE_NEW = loadImageIcon("pix/icons/document-new.png", "New document"); public static final Icon FILE_NEW = loadImageIcon("pix/icons/document-new.png", "New document");
public static final Icon FILE_OPEN = loadImageIcon("pix/icons/document-open.png", "Open document"); public static final Icon FILE_OPEN = loadImageIcon("pix/icons/document-open.png", "Open document");
public static final Icon FILE_OPEN_EXAMPLE = loadImageIcon("pix/icons/document-open-example.png", "Open example document"); public static final Icon FILE_OPEN_EXAMPLE = loadImageIcon("pix/icons/document-open-example.png", "Open example document");
@ -62,9 +62,10 @@ public class Icons {
public static final Icon EDIT_SCALE = loadImageIcon("pix/icons/edit-scale.png", "Scale"); public static final Icon EDIT_SCALE = loadImageIcon("pix/icons/edit-scale.png", "Scale");
public static final Icon HELP_ABOUT = loadImageIcon("pix/icons/help-about.png", "About"); public static final Icon HELP_ABOUT = loadImageIcon("pix/icons/help-about.png", "About");
public static final Icon HELP_LICENSE = loadImageIcon("pix/icons/help-license.png", "License");
public static final Icon HELP_BUG_REPORT = loadImageIcon("pix/icons/help-bug.png", "Bug report"); public static final Icon HELP_BUG_REPORT = loadImageIcon("pix/icons/help-bug.png", "Bug report");
public static final Icon HELP_DEBUG_LOG = loadImageIcon("pix/icons/help-log.png", "Debug log"); public static final Icon HELP_DEBUG_LOG = loadImageIcon("pix/icons/help-log.png", "Debug log");
public static final Icon HELP_LICENSE = loadImageIcon("pix/icons/help-license.png", "License"); public static final Icon HELP_TOURS = loadImageIcon("pix/icons/help-tours.png", "Guided tours");
public static final Icon ZOOM_IN = loadImageIcon("pix/icons/zoom-in.png", "Zoom in"); public static final Icon ZOOM_IN = loadImageIcon("pix/icons/zoom-in.png", "Zoom in");
public static final Icon ZOOM_OUT = loadImageIcon("pix/icons/zoom-out.png", "Zoom out"); public static final Icon ZOOM_OUT = loadImageIcon("pix/icons/zoom-out.png", "Zoom out");

View File

@ -77,7 +77,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
*/ */
private ArrayList<RocketComponent> children = new ArrayList<RocketComponent>(); private ArrayList<RocketComponent> children = new ArrayList<RocketComponent>();
//////// Parameters common to all components: //////// Parameters common to all components:
/** /**
@ -99,12 +99,12 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
*/ */
protected double position = 0; protected double position = 0;
// Color of the component, null means to use the default color // Color of the component, null means to use the default color
private Color color = null; private Color color = null;
private LineStyle lineStyle = null; private LineStyle lineStyle = null;
// Override mass/CG // Override mass/CG
private double overrideMass = 0; private double overrideMass = 0;
private boolean massOverriden = false; private boolean massOverriden = false;
@ -113,7 +113,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
private boolean overrideSubcomponents = false; private boolean overrideSubcomponents = false;
// User-given name of the component // User-given name of the component
private String name = null; private String name = null;
@ -126,7 +126,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
// Preset component this component is based upon // Preset component this component is based upon
private ComponentPreset presetComponent = null; private ComponentPreset presetComponent = null;
/** /**
* Used to invalidate the component after calling {@link #copyFrom(RocketComponent)}. * Used to invalidate the component after calling {@link #copyFrom(RocketComponent)}.
*/ */
@ -135,8 +135,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
//// NOTE !!! All fields must be copied in the method copyFrom()! //// //// NOTE !!! All fields must be copied in the method copyFrom()! ////
/** /**
* Default constructor. Sets the name of the component to the component's static name * Default constructor. Sets the name of the component to the component's static name
* and the relative position of the component. * and the relative position of the component.
@ -150,7 +150,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
//////////// Methods that must be implemented //////////// //////////// Methods that must be implemented ////////////
/** /**
* Static component name. The name may not vary of the parameters, it must be static. * Static component name. The name may not vary of the parameters, it must be static.
*/ */
@ -166,7 +166,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
*/ */
public abstract Coordinate getComponentCG(); // CG of non-overridden component public abstract Coordinate getComponentCG(); // CG of non-overridden component
/** /**
* Return the longitudinal (around the y- or z-axis) unitary moment of inertia. * Return the longitudinal (around the y- or z-axis) unitary moment of inertia.
* The unitary moment of inertia is the moment of inertia with the assumption that * The unitary moment of inertia is the moment of inertia with the assumption that
@ -225,7 +225,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
} }
/** /**
* Return a collection of bounding coordinates. The coordinates must be such that * Return a collection of bounding coordinates. The coordinates must be such that
* the component is fully enclosed in their convex hull. * the component is fully enclosed in their convex hull.
@ -245,12 +245,12 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
public abstract boolean isMassive(); public abstract boolean isMassive();
//////////// Methods that may be overridden //////////// //////////// Methods that may be overridden ////////////
/** /**
* Shift the coordinates in the array corresponding to radial movement. A component * Shift the coordinates in the array corresponding to radial movement. A component
* that has a radial position must shift the coordinates in this array suitably. * that has a radial position must shift the coordinates in this array suitably.
@ -283,8 +283,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
} }
/** /**
* Return the user-provided name of the component, or the component base * Return the user-provided name of the component, or the component base
* name if the user-provided name is empty. This can be used in the UI. * name if the user-provided name is empty. This can be used in the UI.
@ -345,7 +345,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
} }
/** /**
* Make a deep copy of the rocket component tree structure from this component * Make a deep copy of the rocket component tree structure from this component
* downwards while maintaining the component ID's. The purpose of this method is * downwards while maintaining the component ID's. The purpose of this method is
@ -400,8 +400,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
////////////// Methods that may not be overridden //////////// ////////////// Methods that may not be overridden ////////////
////////// Common parameter setting/getting ////////// ////////// Common parameter setting/getting //////////
/** /**
@ -441,8 +441,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
} }
/** /**
* Get the current override mass. The mass is not necessarily in use * Get the current override mass. The mass is not necessarily in use
* at the moment. * at the moment.
@ -495,9 +495,9 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
} }
/** /**
* Return the current override CG. The CG is not necessarily overridden. * Return the current override CG. The CG is not necessarily overridden.
* *
@ -559,7 +559,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
} }
/** /**
* Return whether the mass and/or CG override overrides all subcomponent values * Return whether the mass and/or CG override overrides all subcomponent values
* as well. The default implementation is a normal getter/setter implementation, * as well. The default implementation is a normal getter/setter implementation,
@ -607,8 +607,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
} }
/** /**
* Get the user-defined name of the component. * Get the user-defined name of the component.
*/ */
@ -662,7 +662,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
} }
/** /**
* Return the preset component that this component is based upon. * Return the preset component that this component is based upon.
* *
@ -690,7 +690,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
if (preset.getComponentClass() != this.getClass()) { if (preset.getComponentClass() != this.getClass()) {
throw new IllegalArgumentException("Attempting to load preset of type " + preset.getComponentClass() throw new IllegalArgumentException("Attempting to load preset of type " + preset.getComponentClass()
+ " into component of type " + this.getClass()); + " into component of type " + this.getClass());
} }
RocketComponent root = getRoot(); RocketComponent root = getRoot();
@ -748,7 +748,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
} }
/** /**
* Returns the unique ID of the component. * Returns the unique ID of the component.
* *
@ -767,8 +767,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
} }
/** /**
* Get the characteristic length of the component, for example the length of a body tube * Get the characteristic length of the component, for example the length of a body tube
* of the length of the root chord of a fin. This is used in positioning the component * of the length of the root chord of a fin. This is used in positioning the component
@ -838,42 +838,42 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
this.relativePosition = position; this.relativePosition = position;
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
} }
/** /**
* Determine position relative to given position argument. Note: This is a side-effect free method. No state * Determine position relative to given position argument. Note: This is a side-effect free method. No state
* is modified. * is modified.
* *
* @param thePosition the relative position to be used as the basis for the computation * @param thePosition the relative position to be used as the basis for the computation
* @param relativeTo the position is computed relative the the given component * @param relativeTo the position is computed relative the the given component
* *
* @return double position of the component relative to the parent, with respect to <code>position</code> * @return double position of the component relative to the parent, with respect to <code>position</code>
*/ */
public double asPositionValue (Position thePosition, RocketComponent relativeTo) { public double asPositionValue(Position thePosition, RocketComponent relativeTo) {
double result = this.position; double result = this.position;
if (relativeTo != null) { if (relativeTo != null) {
double thisPos = this.toRelative(Coordinate.NUL, relativeTo)[0].x; double thisPos = this.toRelative(Coordinate.NUL, relativeTo)[0].x;
switch (thePosition) { switch (thePosition) {
case ABSOLUTE: case ABSOLUTE:
result = this.toAbsolute(Coordinate.NUL)[0].x; result = this.toAbsolute(Coordinate.NUL)[0].x;
break; break;
case TOP: case TOP:
result = thisPos; result = thisPos;
break; break;
case MIDDLE: case MIDDLE:
result = thisPos - (relativeTo.length - this.length) / 2; result = thisPos - (relativeTo.length - this.length) / 2;
break; break;
case BOTTOM: case BOTTOM:
result = thisPos - (relativeTo.length - this.length); result = thisPos - (relativeTo.length - this.length);
break; break;
default: default:
throw new BugException("Unknown position type: " + thePosition); throw new BugException("Unknown position type: " + thePosition);
} }
} }
return result; return result;
} }
/** /**
* Get the position value of the component. The exact meaning of the value is * Get the position value of the component. The exact meaning of the value is
* dependent on the current relative positioning. * dependent on the current relative positioning.
@ -905,7 +905,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
} }
/////////// Coordinate changes /////////// /////////// Coordinate changes ///////////
/** /**
@ -1044,7 +1044,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
} }
/////////// Total mass and CG calculation //////////// /////////// Total mass and CG calculation ////////////
/** /**
@ -1104,10 +1104,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
} }
/////////// Children handling /////////// /////////// Children handling ///////////
/** /**
* Adds a child to the rocket component tree. The component is added to the end * Adds a child to the rocket component tree. The component is added to the end
* of the component's child list. This is a helper method that calls * of the component's child list. This is a helper method that calls
@ -1138,10 +1138,18 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
*/ */
public void addChild(RocketComponent component, int index) { public void addChild(RocketComponent component, int index) {
checkState(); checkState();
if (component.parent != null) { if (component.parent != null) {
throw new IllegalArgumentException("component " + component.getComponentName() + throw new IllegalArgumentException("component " + component.getComponentName() +
" is already in a tree"); " is already in a tree");
} }
// Ensure that the no loops are created in component tree [A -> X -> Y -> B, B.addChild(A)]
if (this.getRoot().equals(component)) {
throw new IllegalStateException("Component " + component.getComponentName() +
" is a parent of " + this.getComponentName() + ", attempting to create cycle in tree.");
}
if (!isCompatible(component)) { if (!isCompatible(component)) {
throw new IllegalStateException("Component " + component.getComponentName() + throw new IllegalStateException("Component " + component.getComponentName() +
" not currently compatible with component " + getComponentName()); " not currently compatible with component " + getComponentName());
@ -1156,7 +1164,6 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
fireAddRemoveEvent(component); fireAddRemoveEvent(component);
} }
/** /**
* Removes a child from the rocket component tree. * Removes a child from the rocket component tree.
* *
@ -1199,8 +1206,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
} }
/** /**
* Move a child to another position. * Move a child to another position.
* *
@ -1614,7 +1621,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
} }
/** /**
* Returns an iterator that iterates over all children and sub-children. * Returns an iterator that iterates over all children and sub-children.
* <p> * <p>
@ -1648,9 +1655,9 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
} }
/** /**
* Compare component equality based on the ID of this component. Only the * Compare component equality based on the ID of this component. Only the
* ID and class type is used for a basis of comparison. * ID and class type is used for a basis of comparison.
@ -1668,19 +1675,19 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
} }
@Override @Override
public int hashCode() { public int hashCode() {
return id.hashCode(); return id.hashCode();
} }
//////////// Helper methods for subclasses //////////// Helper methods for subclasses
/** /**
* Helper method to add rotationally symmetric bounds at the specified coordinates. * Helper method to add rotationally symmetric bounds at the specified coordinates.
* The X-axis value is <code>x</code> and the radius at the specified position is * The X-axis value is <code>x</code> and the radius at the specified position is
@ -1703,7 +1710,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
protected static final double ringMass(double outerRadius, double innerRadius, protected static final double ringMass(double outerRadius, double innerRadius,
double length, double density) { double length, double density) {
return Math.PI * (MathUtil.pow2(outerRadius) - MathUtil.pow2(innerRadius)) * return Math.PI * (MathUtil.pow2(outerRadius) - MathUtil.pow2(innerRadius)) *
length * density; length * density;
} }
protected static final double ringLongitudinalUnitInertia(double outerRadius, protected static final double ringLongitudinalUnitInertia(double outerRadius,
@ -1719,10 +1726,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
} }
//////////// OTHER //////////// OTHER
/** /**
* Loads the RocketComponent fields from the given component. This method is meant * Loads the RocketComponent fields from the given component. This method is meant
* for in-place replacement of a component. It is used with the undo/redo * for in-place replacement of a component. It is used with the undo/redo