Fixing various issues
authorplaa <plaa@180e2498-e6e9-4542-8430-84ac67f01cd8>
Sun, 19 Feb 2012 08:23:22 +0000 (08:23 +0000)
committerplaa <plaa@180e2498-e6e9-4542-8430-84ac67f01cd8>
Sun, 19 Feb 2012 08:23:22 +0000 (08:23 +0000)
git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@427 180e2498-e6e9-4542-8430-84ac67f01cd8

core/ChangeLog
core/datafiles-src/tours/creating_design/dialog-1.png
core/fileformat.txt
core/resources/pix/icons/copyright.txt
core/resources/pix/icons/help-tours.png [new file with mode: 0644]
core/src/net/sf/openrocket/file/motor/RockSimMotorLoader.java
core/src/net/sf/openrocket/gui/main/BasicFrame.java
core/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeTransferHandler.java
core/src/net/sf/openrocket/gui/util/Icons.java
core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java

index 871d5fbc1c4469e2b5f8e497bb0528bf0cd46e23..e86da4f10f5054ace20d851be11963ca32a9a311 100644 (file)
@@ -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
index 2345dc0c37fd729498828bc475b4f38de217eab0..39a683278fb22d78a3f6464bfdc489751872db79 100644 (file)
Binary files a/core/datafiles-src/tours/creating_design/dialog-1.png and b/core/datafiles-src/tours/creating_design/dialog-1.png differ
index 3ba93461cdad6a43e852275fa6b9e9ff96ecc64d..9d64ec88fa70a9e760989ec337b0ca1e3977eb86 100644 (file)
@@ -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 <flightdata> element.  The motor
-      digesting algorithm was changed.
+      digesting algorithm was changed.  Adds <separationevent> and
+      <separationdelay> elements to stage components (except sustainer).
index 6583a503e0b2d53c318fc77a43c061c19583ac11..7320b9501e4837033a5fbff2b5fd64220c18bbb5 100644 (file)
@@ -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 (file)
index 0000000..546d8a7
Binary files /dev/null and b/core/resources/pix/icons/help-tours.png differ
index 353695e0cc132dc32d28c9837861ba731fc22337..8627bf82f79ae3b74ade31cc15717fc8324eb021 100644 (file)
@@ -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();
                        
index b4a3833878bcb20b1f6169ccd166713113d9db54..f9884900bdd4defeb4f8dcf01d3d3fbb3f8f4932 100644 (file)
@@ -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<BasicFrame> frames = new ArrayList<BasicFrame>();
        
-
+       
        /**
         * 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 <code>null</code> 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.
index 4855112b77482f12cfe1652712dba3fb0acab3df..ff4ae578cd3b84222a3fb783218e2947719d6fe0 100644 (file)
@@ -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).
index 5c76bf24f566c7cb9680271941de8e70e8986e23..8f304b6324a9cb2b056e343b7936b733dabae96a 100644 (file)
@@ -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");
index a5c8c66a2bc101f35f0d2f06179fcf273b4ce08b..b1a57dbfc8e7445a75a8b30bd926f126b3bbc519 100644 (file)
@@ -77,7 +77,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         */
        private ArrayList<RocketComponent> children = new ArrayList<RocketComponent>();
        
-
+       
        ////////  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 <code>position</code>
-     */
-    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 <code>position</code>
+        */
+       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.
         * <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
         * 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 <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,
                        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