From: plaa Date: Sun, 19 Feb 2012 08:23:22 +0000 (+0000) Subject: Fixing various issues X-Git-Tag: upstream/12.03~1^2~44 X-Git-Url: https://git.gag.com/?a=commitdiff_plain;h=587569f41cf4c0eefee10339bdb324d366280cd5;p=debian%2Fopenrocket Fixing various issues git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@427 180e2498-e6e9-4542-8430-84ac67f01cd8 --- diff --git a/core/ChangeLog b/core/ChangeLog index 871d5fbc..e86da4f1 100644 --- a/core/ChangeLog +++ b/core/ChangeLog @@ -1,3 +1,7 @@ +2012-02-16 Sampo Niskanen + + * [BUG] Freeze when dropping component on child component + 2012-02-10 Sampo Niskanen * Configurable stage separation events diff --git a/core/datafiles-src/tours/creating_design/dialog-1.png b/core/datafiles-src/tours/creating_design/dialog-1.png index 2345dc0c..39a68327 100644 Binary files a/core/datafiles-src/tours/creating_design/dialog-1.png and b/core/datafiles-src/tours/creating_design/dialog-1.png differ diff --git a/core/fileformat.txt b/core/fileformat.txt index 3ba93461..9d64ec88 100644 --- a/core/fileformat.txt +++ b/core/fileformat.txt @@ -36,4 +36,5 @@ The following file format versions exist: 1.4: Introduced with OpenRocket 1.1.10. Adds the launchrodvelocity and deploymentvelocity attributes to element. The motor - digesting algorithm was changed. + digesting algorithm was changed. Adds and + elements to stage components (except sustainer). diff --git a/core/resources/pix/icons/copyright.txt b/core/resources/pix/icons/copyright.txt index 6583a503..7320b950 100644 --- a/core/resources/pix/icons/copyright.txt +++ b/core/resources/pix/icons/copyright.txt @@ -34,3 +34,4 @@ help-license.png help-log.png help-about.png help-bug.png +help-tours.png diff --git a/core/resources/pix/icons/help-tours.png b/core/resources/pix/icons/help-tours.png new file mode 100644 index 00000000..546d8a79 Binary files /dev/null and b/core/resources/pix/icons/help-tours.png differ diff --git a/core/src/net/sf/openrocket/file/motor/RockSimMotorLoader.java b/core/src/net/sf/openrocket/file/motor/RockSimMotorLoader.java index 353695e0..8627bf82 100644 --- a/core/src/net/sf/openrocket/file/motor/RockSimMotorLoader.java +++ b/core/src/net/sf/openrocket/file/motor/RockSimMotorLoader.java @@ -360,7 +360,6 @@ public class RockSimMotorLoader extends AbstractMotorLoader { if (!calculateCG) { motorDigest.update(DataType.CG_PER_TIME, toArray(cg)); } - // FIXME: Should this use CG_SPECIFIC ??? motorDigest.update(DataType.FORCE_PER_TIME, thrustArray); final String digest = motorDigest.getDigest(); diff --git a/core/src/net/sf/openrocket/gui/main/BasicFrame.java b/core/src/net/sf/openrocket/gui/main/BasicFrame.java index b4a38338..f9884900 100644 --- a/core/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/core/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -1,5 +1,62 @@ package net.sf.openrocket.gui.main; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Toolkit; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.ExecutionException; + +import javax.swing.Action; +import javax.swing.BorderFactory; +import javax.swing.InputMap; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.JSpinner; +import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; +import javax.swing.JTextField; +import javax.swing.KeyStroke; +import javax.swing.ListSelectionModel; +import javax.swing.ScrollPaneConstants; +import javax.swing.SwingUtilities; +import javax.swing.border.BevelBorder; +import javax.swing.border.TitledBorder; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.tree.DefaultTreeSelectionModel; +import javax.swing.tree.TreePath; +import javax.swing.tree.TreeSelectionModel; + import net.miginfocom.swing.MigLayout; import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.document.OpenRocketDocument; @@ -48,38 +105,6 @@ import net.sf.openrocket.util.MemoryManagement.MemoryData; import net.sf.openrocket.util.Reflection; import net.sf.openrocket.util.TestRockets; -import javax.swing.*; -import javax.swing.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 { 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 SIMULATION_TAB = 1; - + /** * List of currently open frames. When the list goes empty * it is time to exit the application. */ private static final ArrayList frames = new ArrayList(); - + /** * Whether "New" and "Open" should replace this frame. * Should be set to false on the first rocket modification. */ private boolean replaceable = false; - - + + private final OpenRocketDocument document; private final Rocket rocket; @@ -126,7 +151,7 @@ public class BasicFrame extends JFrame { private final RocketActions actions; - + /** * Sole constructor. Creates a new frame based on the supplied document * and adds it to the current frames list. @@ -140,7 +165,7 @@ public class BasicFrame extends JFrame { this.rocket = document.getRocket(); this.rocket.getDefaultConfiguration().setAllStages(); - + // Set replaceable flag to false at first modification rocket.addComponentChangeListener(new ComponentChangeListener() { @Override @@ -150,7 +175,7 @@ public class BasicFrame extends JFrame { } }); - + // Create the component tree selection model that will be used componentSelectionModel = new DefaultTreeSelectionModel(); componentSelectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); @@ -164,10 +189,10 @@ public class BasicFrame extends JFrame { selectionModel.attachComponentTreeSelectionModel(componentSelectionModel); selectionModel.attachSimulationListSelectionModel(simulationSelectionModel); - + actions = new RocketActions(document, selectionModel, this); - + log.debug("Constructing the BasicFrame UI"); // The main vertical split pane @@ -175,7 +200,7 @@ public class BasicFrame extends JFrame { vertical.setResizeWeight(0.5); this.add(vertical); - + // The top tabbed pane tabbedPane = new JTabbedPane(); //// Rocket design @@ -185,8 +210,8 @@ public class BasicFrame extends JFrame { vertical.setTopComponent(tabbedPane); - - + + // Bottom segment, rocket figure rocketpanel = new RocketPanel(document); @@ -194,10 +219,10 @@ public class BasicFrame extends JFrame { rocketpanel.setSelectionModel(tree.getSelectionModel()); - + createMenu(); - + rocket.addComponentChangeListener(new ComponentChangeListener() { @Override public void componentChanged(ComponentChangeEvent e) { @@ -208,7 +233,7 @@ public class BasicFrame extends JFrame { setTitle(); this.pack(); - + // Set initial window size Dimension size = Toolkit.getDefaultToolkit().getScreenSize(); size.width = size.width * 9 / 10; @@ -246,7 +271,7 @@ public class BasicFrame extends JFrame { JSplitPane horizontal = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true); horizontal.setResizeWeight(0.5); - + // Upper-left segment, component tree JPanel panel = new JPanel(new MigLayout("fill, flowy", "", "[grow]")); @@ -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_N, ActionEvent.CTRL_MASK), null); - - + + // Double-click opens config dialog MouseListener ml = new MouseAdapter() { @Override @@ -306,7 +331,7 @@ public class BasicFrame extends JFrame { JScrollPane scroll = new JScrollPane(tree); panel.add(scroll, "spany, grow, wrap"); - + // Buttons JButton button = new JButton(actions.getMoveUpAction()); panel.add(button, "sizegroup buttons, aligny 65%"); @@ -327,7 +352,7 @@ public class BasicFrame extends JFrame { horizontal.setLeftComponent(panel); - + // Upper-right segment, component addition buttons 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 null if none selected. */ @@ -485,7 +510,7 @@ public class BasicFrame extends JFrame { }); menu.add(item); - + menu.addSeparator(); //// Close @@ -520,8 +545,8 @@ public class BasicFrame extends JFrame { }); menu.add(item); - - + + //// Edit menu = new JMenu(trans.get("main.menu.edit")); menu.setMnemonic(KeyEvent.VK_E); @@ -529,7 +554,7 @@ public class BasicFrame extends JFrame { menu.getAccessibleContext().setAccessibleDescription(trans.get("BasicFrame.menu.Rocketedt")); menubar.add(menu); - + Action action = UndoRedoAction.newUndoAction(document); item = new JMenuItem(action); item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, ActionEvent.CTRL_MASK)); @@ -538,7 +563,7 @@ public class BasicFrame extends JFrame { item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.edit.undo.desc")); menu.add(item); - + action = UndoRedoAction.newRedoAction(document); item = new JMenuItem(action); item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Y, ActionEvent.CTRL_MASK)); @@ -549,7 +574,7 @@ public class BasicFrame extends JFrame { menu.addSeparator(); - + item = new JMenuItem(actions.getCutAction()); menu.add(item); @@ -564,8 +589,8 @@ public class BasicFrame extends JFrame { menu.addSeparator(); - - + + item = new JMenuItem(trans.get("main.menu.edit.resize")); item.setIcon(Icons.EDIT_SCALE); item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.edit.resize.desc")); @@ -580,8 +605,8 @@ public class BasicFrame extends JFrame { }); menu.add(item); - - + + //// Preferences item = new JMenuItem(trans.get("main.menu.edit.preferences")); item.setIcon(Icons.PREFERENCES); @@ -596,9 +621,9 @@ public class BasicFrame extends JFrame { }); menu.add(item); - - - + + + //// Analyze menu = new JMenu(trans.get("main.menu.analyze")); menu.setMnemonic(KeyEvent.VK_A); @@ -619,7 +644,7 @@ public class BasicFrame extends JFrame { }); menu.add(item); - + item = new JMenuItem(trans.get("main.menu.analyze.optimization"), KeyEvent.VK_O); item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.analyze.optimization.desc")); item.addActionListener(new ActionListener() { @@ -631,16 +656,16 @@ public class BasicFrame extends JFrame { }); menu.add(item); - - + + //// Debug // (shown if openrocket.debug.menu is defined) if (System.getProperty("openrocket.debug.menu") != null) { menubar.add(makeDebugMenu()); } - - + + //// Help menu = new JMenu(trans.get("main.menu.help")); @@ -648,11 +673,11 @@ public class BasicFrame extends JFrame { menu.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.desc")); menubar.add(menu); - + // Guided tours item = new JMenuItem(trans.get("main.menu.help.tours"), KeyEvent.VK_L); - // TODO: Icon + item.setIcon(Icons.HELP_TOURS); item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.tours.desc")); item.addActionListener(new ActionListener() { @Override @@ -666,22 +691,6 @@ public class BasicFrame extends JFrame { menu.addSeparator(); - - //// License - item = new JMenuItem(trans.get("main.menu.help.license"), KeyEvent.VK_L); - item.setIcon(Icons.HELP_LICENSE); - item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.license.desc")); - item.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - log.user("License selected"); - new LicenseDialog(BasicFrame.this).setVisible(true); - } - }); - menu.add(item); - - menu.addSeparator(); - //// Bug report item = new JMenuItem(trans.get("main.menu.help.bugReport"), KeyEvent.VK_B); item.setIcon(Icons.HELP_BUG_REPORT); @@ -710,6 +719,21 @@ public class BasicFrame extends JFrame { menu.addSeparator(); + + //// License + item = new JMenuItem(trans.get("main.menu.help.license"), KeyEvent.VK_L); + item.setIcon(Icons.HELP_LICENSE); + item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.license.desc")); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + log.user("License selected"); + new LicenseDialog(BasicFrame.this).setVisible(true); + } + }); + menu.add(item); + + //// About item = new JMenuItem(trans.get("main.menu.help.about"), KeyEvent.VK_A); item.setIcon(Icons.HELP_ABOUT); @@ -723,7 +747,7 @@ public class BasicFrame extends JFrame { }); menu.add(item); - + this.setJMenuBar(menubar); } @@ -734,7 +758,7 @@ public class BasicFrame extends JFrame { /* * This menu is intentionally left untranslated. */ - + //// Debug menu menu = new JMenu("Debug"); //// OpenRocket debugging tasks @@ -770,10 +794,10 @@ public class BasicFrame extends JFrame { int sel = JOptionPane.showOptionDialog(BasicFrame.this, new Object[] { "Input text key to generate random rocket:", field - }, "Generate random test rocket", JOptionPane.DEFAULT_OPTION, + }, "Generate random test rocket", JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, new Object[] { "Random", "OK" - }, "OK"); + }, "OK"); Rocket r; if (sel == 0) { @@ -792,8 +816,8 @@ public class BasicFrame extends JFrame { }); menu.add(item); - - + + item = new JMenuItem("Create 'Iso-Haisu'"); item.addActionListener(new ActionListener() { @Override @@ -808,7 +832,7 @@ public class BasicFrame extends JFrame { }); menu.add(item); - + item = new JMenuItem("Create 'Big Blue'"); item.addActionListener(new ActionListener() { @Override @@ -825,7 +849,7 @@ public class BasicFrame extends JFrame { menu.addSeparator(); - + item = new JMenuItem("Memory statistics"); item.addActionListener(new ActionListener() { @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[3] = String.format(" Free memory: %.1f MB (%.0f%%)", free / 1024.0 / 1024.0, 100.0 * free / max); - + DetailDialog.showDetailedMessageDialog(BasicFrame.this, stats, sb.toString(), "Memory statistics", JOptionPane.INFORMATION_MESSAGE); } @@ -897,7 +921,7 @@ public class BasicFrame extends JFrame { }); menu.add(item); - + menu.addSeparator(); //// Exception here @@ -952,10 +976,10 @@ public class BasicFrame extends JFrame { }); menu.add(item); - + menu.addSeparator(); - + item = new JMenuItem("Test popup"); item.addActionListener(new ActionListener() { @Override @@ -972,9 +996,9 @@ public class BasicFrame extends JFrame { }); menu.add(item); - - - + + + return menu; } @@ -989,7 +1013,7 @@ public class BasicFrame extends JFrame { } - + private void openAction() { JFileChooser chooser = new JFileChooser(); @@ -1067,7 +1091,7 @@ public class BasicFrame extends JFrame { filename = filename.substring(filename.lastIndexOf('/') + 1); } - + // Open the file log.info("Opening file from url=" + url + " filename=" + filename); try { @@ -1140,7 +1164,7 @@ public class BasicFrame extends JFrame { return false; } - + // Handle the document OpenRocketDocument doc = null; try { @@ -1182,7 +1206,7 @@ public class BasicFrame extends JFrame { throw new BugException("Document loader returned null"); } - + // Show warnings WarningSet warnings = worker.getRocketLoader().getWarnings(); if (!warnings.isEmpty()) { @@ -1198,12 +1222,12 @@ public class BasicFrame extends JFrame { trans.get("BasicFrame.WarningDialog.title"), warnings); } - + // Set document state doc.setFile(file); doc.setSaved(true); - + // Open the frame log.debug("Opening new frame with the document"); BasicFrame frame = new BasicFrame(doc); @@ -1211,15 +1235,15 @@ public class BasicFrame extends JFrame { return true; } - - /** - * "Save" action. If the design is new, then this is identical to "Save As", with a default file filter for .ork. - * If the rocket being edited previously was opened from a .ork file, then it will be saved immediately to the same - * file. But clicking on 'Save' for an existing design file with a .rkt will bring up a confirmation dialog because - * it's potentially a destructive write (loss of some fidelity if it's truly an original Rocksim generated file). - * - * @return true if the file was saved, false otherwise - */ + + /** + * "Save" action. If the design is new, then this is identical to "Save As", with a default file filter for .ork. + * If the rocket being edited previously was opened from a .ork file, then it will be saved immediately to the same + * file. But clicking on 'Save' for an existing design file with a .rkt will bring up a confirmation dialog because + * it's potentially a destructive write (loss of some fidelity if it's truly an original Rocksim generated file). + * + * @return true if the file was saved, false otherwise + */ private boolean saveAction() { File file = document.getFile(); if (file == null) { @@ -1229,45 +1253,45 @@ public class BasicFrame extends JFrame { log.info("Saving document to " + file); if (FileHelper.ROCKSIM_DESIGN_FILTER.accept(file)) { - return saveAsRocksim(file); + return saveAsRocksim(file); } return saveAs(file); } - - /** - * "Save As" action. - * - * Never should a .rkt file contain an OpenRocket content, or an .ork file contain a Rocksim design. Regardless of - * what extension the user has chosen, it would violate the Principle of Least Astonishment to do otherwise - * (and we want to make doing the wrong thing really hard to do). So always force the appropriate extension. - * - * This can result in some odd looking filenames (MyDesign.rkt.ork, MyDesign.rkt.ork.rkt, etc.) if the user is - * not paying attention, but the user can control that by modifying the filename in the dialog. - * - * @return true if the file was saved, false otherwise - */ + + /** + * "Save As" action. + * + * Never should a .rkt file contain an OpenRocket content, or an .ork file contain a Rocksim design. Regardless of + * what extension the user has chosen, it would violate the Principle of Least Astonishment to do otherwise + * (and we want to make doing the wrong thing really hard to do). So always force the appropriate extension. + * + * This can result in some odd looking filenames (MyDesign.rkt.ork, MyDesign.rkt.ork.rkt, etc.) if the user is + * not paying attention, but the user can control that by modifying the filename in the dialog. + * + * @return true if the file was saved, false otherwise + */ private boolean saveAsAction() { File file = null; - + StorageOptionChooser storageChooser = new StorageOptionChooser(document, document.getDefaultStorageOptions()); final JFileChooser chooser = new JFileChooser(); - chooser.addChoosableFileFilter(FileHelper.OPENROCKET_DESIGN_FILTER); - chooser.addChoosableFileFilter(FileHelper.ROCKSIM_DESIGN_FILTER); - - //Force the file filter to match the file extension that was opened. Will default to OR if the file is null. - if (FileHelper.ROCKSIM_DESIGN_FILTER.accept(document.getFile())) { - chooser.setFileFilter(FileHelper.ROCKSIM_DESIGN_FILTER); - } - else { - chooser.setFileFilter(FileHelper.OPENROCKET_DESIGN_FILTER); - } + chooser.addChoosableFileFilter(FileHelper.OPENROCKET_DESIGN_FILTER); + chooser.addChoosableFileFilter(FileHelper.ROCKSIM_DESIGN_FILTER); + + //Force the file filter to match the file extension that was opened. Will default to OR if the file is null. + if (FileHelper.ROCKSIM_DESIGN_FILTER.accept(document.getFile())) { + chooser.setFileFilter(FileHelper.ROCKSIM_DESIGN_FILTER); + } + else { + chooser.setFileFilter(FileHelper.OPENROCKET_DESIGN_FILTER); + } chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory()); chooser.setAccessory(storageChooser); if (document.getFile() != null) { chooser.setSelectedFile(document.getFile()); - } - + } + int option = chooser.showSaveDialog(BasicFrame.this); if (option != JFileChooser.APPROVE_OPTION) { log.user("User decided not to save, option=" + option); @@ -1282,45 +1306,45 @@ public class BasicFrame extends JFrame { ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(chooser.getCurrentDirectory()); storageChooser.storeOptions(document.getDefaultStorageOptions()); - - if (chooser.getFileFilter().equals(FileHelper.ROCKSIM_DESIGN_FILTER)) { - return saveAsRocksim(file); - } - else { - file = FileHelper.forceExtension(file, "ork"); - return FileHelper.confirmWrite(file, this) && saveAs(file); - } + + if (chooser.getFileFilter().equals(FileHelper.ROCKSIM_DESIGN_FILTER)) { + return saveAsRocksim(file); + } + else { + file = FileHelper.forceExtension(file, "ork"); + return FileHelper.confirmWrite(file, this) && saveAs(file); + } } - - /** - * Perform the writing of the design to the given file in Rocksim format. - * - * @param file the chosen file - * - * @return true if the file was written - */ - private boolean saveAsRocksim(File file) { - file = FileHelper.forceExtension(file, "rkt"); - if (!FileHelper.confirmWrite(file, this)) { - return false; - } - - try { - new RocksimSaver().save(file, document); - return true; - } catch (IOException e) { - return false; - } - } - - /** - * Perform the writing of the design to the given file in OpenRocket format. - * - * @param file the chosen file - * - * @return true if the file was written - */ - private boolean saveAs(File file) { + + /** + * Perform the writing of the design to the given file in Rocksim format. + * + * @param file the chosen file + * + * @return true if the file was written + */ + private boolean saveAsRocksim(File file) { + file = FileHelper.forceExtension(file, "rkt"); + if (!FileHelper.confirmWrite(file, this)) { + return false; + } + + try { + new RocksimSaver().save(file, document); + return true; + } catch (IOException e) { + return false; + } + } + + /** + * Perform the writing of the design to the given file in OpenRocket format. + * + * @param file the chosen file + * + * @return true if the file was written + */ + private boolean saveAs(File file) { log.info("Saving document as " + file); boolean saved = false; @@ -1330,7 +1354,7 @@ public class BasicFrame extends JFrame { return false; } - + SaveFileWorker worker = new SaveFileWorker(document, file, ROCKET_SAVER); 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 * can be used to map a Rocket to a BasicFrame from GUI methods. diff --git a/core/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeTransferHandler.java b/core/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeTransferHandler.java index 4855112b..ff4ae578 100644 --- a/core/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeTransferHandler.java +++ b/core/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeTransferHandler.java @@ -32,6 +32,7 @@ public class ComponentTreeTransferHandler extends TransferHandler { private final OpenRocketDocument document; + /** * Sole constructor. * @@ -69,15 +70,15 @@ public class ComponentTreeTransferHandler extends TransferHandler { } - - + + @Override public void exportDone(JComponent comp, Transferable trans, int action) { // Removal from the old place is implemented already in import, so do nothing } - + @Override public boolean canImport(TransferHandler.TransferSupport support) { SourceTarget data = getSourceAndTarget(support); @@ -89,6 +90,17 @@ public class ComponentTreeTransferHandler extends TransferHandler { boolean allowed = data.destParent.isCompatible(data.child); 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 (support.getDropAction() == MOVE && data.srcParent.getRoot() != data.destParent.getRoot()) { support.setDropAction(COPY); @@ -98,7 +110,6 @@ public class ComponentTreeTransferHandler extends TransferHandler { } - @Override public boolean importData(TransferHandler.TransferSupport support) { @@ -121,7 +132,7 @@ public class ComponentTreeTransferHandler extends TransferHandler { action = TransferHandler.COPY; } - + // Check whether move action would be a no-op if ((action == MOVE) && (data.srcParent == data.destParent) && (data.destIndex == data.srcIndex || data.destIndex == data.srcIndex + 1)) { @@ -129,7 +140,7 @@ public class ComponentTreeTransferHandler extends TransferHandler { return false; } - + switch (action) { case MOVE: 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 * 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); - + // Fetch the transferred component (child component) Transferable transferable = support.getTransferable(); RocketComponent child; @@ -228,7 +239,7 @@ public class ComponentTreeTransferHandler extends TransferHandler { throw new BugException(e); } - + // Get the source component & index RocketComponent srcParent = child.getParent(); if (srcParent == null) { @@ -237,7 +248,7 @@ public class ComponentTreeTransferHandler extends TransferHandler { } int srcIndex = srcParent.getChildPosition(child); - + // Get destination component & index RocketComponent destParent = ComponentTreeModel.componentFromPath(location.path); 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 * (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6560955). diff --git a/core/src/net/sf/openrocket/gui/util/Icons.java b/core/src/net/sf/openrocket/gui/util/Icons.java index 5c76bf24..8f304b63 100644 --- a/core/src/net/sf/openrocket/gui/util/Icons.java +++ b/core/src/net/sf/openrocket/gui/util/Icons.java @@ -43,7 +43,7 @@ public class Icons { 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_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"); @@ -62,9 +62,10 @@ public class Icons { 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_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_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_OUT = loadImageIcon("pix/icons/zoom-out.png", "Zoom out"); diff --git a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java index a5c8c66a..b1a57dbf 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java @@ -77,7 +77,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab */ private ArrayList children = new ArrayList(); - + //////// Parameters common to all components: /** @@ -99,12 +99,12 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab */ protected double position = 0; - + // Color of the component, null means to use the default color private Color color = null; private LineStyle lineStyle = null; - + // Override mass/CG private double overrideMass = 0; private boolean massOverriden = false; @@ -113,7 +113,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab private boolean overrideSubcomponents = false; - + // User-given name of the component private String name = null; @@ -126,7 +126,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab // Preset component this component is based upon private ComponentPreset presetComponent = null; - + /** * 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()! //// - - + + /** * Default constructor. Sets the name of the component to the component's static name * and the relative position of the component. @@ -150,7 +150,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab //////////// Methods that must be implemented //////////// - + /** * 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 - + /** * 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 @@ -225,7 +225,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } - + /** * Return a collection of bounding coordinates. The coordinates must be such that * 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(); - - - + + + //////////// Methods that may be overridden //////////// - + /** * 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. @@ -283,8 +283,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } - - + + /** * 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. @@ -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 * 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 //////////// - - + + ////////// 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 * 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. * @@ -559,7 +559,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } - + /** * Return whether the mass and/or CG override overrides all subcomponent values * 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. */ @@ -662,7 +662,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } - + /** * 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()) { 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(); @@ -748,7 +748,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } - + /** * 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 * 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; fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - - - /** - * Determine position relative to given position argument. Note: This is a side-effect free method. No state - * is modified. - * - * @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 - * - * @return double position of the component relative to the parent, with respect to position - */ - public double asPositionValue (Position thePosition, RocketComponent relativeTo) { - double result = this.position; - if (relativeTo != null) { - double thisPos = this.toRelative(Coordinate.NUL, relativeTo)[0].x; - - switch (thePosition) { - case ABSOLUTE: - result = this.toAbsolute(Coordinate.NUL)[0].x; - break; - case TOP: - result = thisPos; - break; - case MIDDLE: - result = thisPos - (relativeTo.length - this.length) / 2; - break; - case BOTTOM: - result = thisPos - (relativeTo.length - this.length); - break; - default: - throw new BugException("Unknown position type: " + thePosition); - } - } - return result; - } - + + + /** + * Determine position relative to given position argument. Note: This is a side-effect free method. No state + * is modified. + * + * @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 + * + * @return double position of the component relative to the parent, with respect to position + */ + public double asPositionValue(Position thePosition, RocketComponent relativeTo) { + double result = this.position; + if (relativeTo != null) { + double thisPos = this.toRelative(Coordinate.NUL, relativeTo)[0].x; + + switch (thePosition) { + case ABSOLUTE: + result = this.toAbsolute(Coordinate.NUL)[0].x; + break; + case TOP: + result = thisPos; + break; + case MIDDLE: + result = thisPos - (relativeTo.length - this.length) / 2; + break; + case BOTTOM: + result = thisPos - (relativeTo.length - this.length); + break; + default: + throw new BugException("Unknown position type: " + thePosition); + } + } + return result; + } + /** * Get the position value of the component. The exact meaning of the value is * dependent on the current relative positioning. @@ -905,7 +905,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } - + /////////// Coordinate changes /////////// /** @@ -1044,7 +1044,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } - + /////////// Total mass and CG calculation //////////// /** @@ -1104,10 +1104,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } - + /////////// Children handling /////////// - + /** * 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 @@ -1138,10 +1138,18 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab */ public void addChild(RocketComponent component, int index) { checkState(); + if (component.parent != null) { throw new IllegalArgumentException("component " + component.getComponentName() + " 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)) { throw new IllegalStateException("Component " + component.getComponentName() + " not currently compatible with component " + getComponentName()); @@ -1156,7 +1164,6 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab fireAddRemoveEvent(component); } - /** * 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. * @@ -1614,7 +1621,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } - + /** * Returns an iterator that iterates over all children and sub-children. *

@@ -1648,9 +1655,9 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } - - - + + + /** * Compare component equality based on the ID of this component. Only the * ID and class type is used for a basis of comparison. @@ -1668,19 +1675,19 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } - + @Override public int hashCode() { return id.hashCode(); } - + //////////// Helper methods for subclasses - - - + + + /** * Helper method to add rotationally symmetric bounds at the specified coordinates. * The X-axis value is x 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, double length, double density) { return Math.PI * (MathUtil.pow2(outerRadius) - MathUtil.pow2(innerRadius)) * - length * density; + length * density; } protected static final double ringLongitudinalUnitInertia(double outerRadius, @@ -1719,10 +1726,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } - + //////////// OTHER - + /** * 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