Motor chooser search field and new edit motor config dialog
authorplaa <plaa@180e2498-e6e9-4542-8430-84ac67f01cd8>
Sun, 21 Jun 2009 18:24:33 +0000 (18:24 +0000)
committerplaa <plaa@180e2498-e6e9-4542-8430-84ac67f01cd8>
Sun, 21 Jun 2009 18:24:33 +0000 (18:24 +0000)
git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@10 180e2498-e6e9-4542-8430-84ac67f01cd8

30 files changed:
ChangeLog
ReleaseNotes
TODO
build.properties
src/net/sf/openrocket/document/OpenRocketDocument.java
src/net/sf/openrocket/file/OpenRocketLoader.java
src/net/sf/openrocket/file/RocketLoader.java
src/net/sf/openrocket/gui/ComponentAnalysisDialog.java [deleted file]
src/net/sf/openrocket/gui/DetailDialog.java [deleted file]
src/net/sf/openrocket/gui/PreferencesDialog.java [deleted file]
src/net/sf/openrocket/gui/adaptors/MotorConfigurationModel.java
src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java
src/net/sf/openrocket/gui/configdialog/InnerTubeConfig.java
src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java [new file with mode: 0644]
src/net/sf/openrocket/gui/dialogs/DetailDialog.java [new file with mode: 0644]
src/net/sf/openrocket/gui/dialogs/EditMotorConfigurationDialog.java [new file with mode: 0644]
src/net/sf/openrocket/gui/dialogs/LicenseDialog.java [new file with mode: 0644]
src/net/sf/openrocket/gui/dialogs/PreferencesDialog.java [new file with mode: 0644]
src/net/sf/openrocket/gui/dialogs/SwingWorkerDialog.java [new file with mode: 0644]
src/net/sf/openrocket/gui/main/BasicFrame.java
src/net/sf/openrocket/gui/main/LicenseDialog.java [deleted file]
src/net/sf/openrocket/gui/main/MotorChooserDialog.java
src/net/sf/openrocket/gui/main/SimulationRunDialog.java
src/net/sf/openrocket/rocketcomponent/ClusterConfiguration.java
src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java
src/net/sf/openrocket/rocketcomponent/IllegalFinPointException.java [new file with mode: 0644]
src/net/sf/openrocket/rocketcomponent/Rocket.java
src/net/sf/openrocket/util/ConcurrentProgressMonitor.java [new file with mode: 0644]
src/net/sf/openrocket/util/ConcurrentProgressMonitorInputStream.java [new file with mode: 0644]
src/net/sf/openrocket/util/Test.java

index 9808b616db4909f1d823aa9fe5fd5830d9feb7e9..f54156a778039ae172f84e2c8dbf9744fcefa0fd 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2009-06-20  Sampo Niskanen
+
+       * New edit motor configurations dialog
+       * Changed FreeformFinSet to throw checked exceptions
+
+2009-06-11  Sampo Niskanen
+
+       * Added search field to motor chooser dialog
+
+2009-06-09  Sampo Niskanen
+
+       * Release 0.9.1
+
 2009-06-08  Sampo Niskanen
 
        * Fixed loading of icons from JAR
index c802e1abbd9d20d4764cfd6acafe745114b417ec..2703d97174849b44667f461d92492ed5ba5dca17 100644 (file)
@@ -1,4 +1,11 @@
 
+OpenRocket 0.9.2  (future):
+---------------------------
+
+- a new and enhanced "Edit motor configurations" dialog
+- a search field in the motor selection dialog
+
+
 OpenRocket 0.9.1  (2009-06-09):
 -------------------------------
 
diff --git a/TODO b/TODO
index a39c66479410db9237b6d0c74920972819d590de..e05d6f848e158050a64262510cd1c4d4fe4b0c70 100644 (file)
--- a/TODO
+++ b/TODO
 
-GUI:
+Feature roadmap for OpenRocket 1.0
 
-- Preferences dialog
 
+Must-have:
 
-BUGS:
+- Exporting flight data
+- Store custom materials
+- Read more thrust curve formats / go through thrust curves and correct errors
+- Create application icon and take into use
+- Fix engine block icons
+- Progress and error dialogs when reading/writing files
 
 
-COMPUTATION:
+Maybe:
 
-
-FILE/STORAGE:
-
-
-OTHER:
-
-- web-sivut
-
-
-DIPPA:
-
-
-
-
--------------------
-
-LATER:
-
-- Simulation delete/copy/paste hotkeys
-  (either component or simulation selected, but not both)
-- Add BodyComponent at end of rocket when no component is selected
-- Showing events in plot (maybe future)
-- Search field in motor selection dialog
+- Reading (writing) .RKT format
+- Showing events in plots
 - Through-the-wall fins
-- Store materials
-
-- Streamer CD estimation
-
-- exporting (maybe later)
-
 - Make ThicknessRingComponent implement RadialParent and allow
   attaching components to a TubeCoupler
+- Reading thrust curves from external directory
 
 
+Postponed:
 
+- Importing flight data
 
-DONE:
 
-- Automatic diameters of body components
-- Copy/paste
+Done:
 
-18.4.:
-- Esc, Ctrl-Z and Y etc.
-- Look and feel
-
-19.4.:
-- Nose cone and transition shoulders in GUI
-- zoom, cut/copy/paste etc. icons
-
-23.4.:
-- Figure or rocket not updating when using a new BasicFrame
-
-24.4.:
-- File save and load
-- Motor configuration editing          (pre-alpha)
-- Save simulations
-
-25.4.:
-- Multi-stages simulation              (pre-alpha)
-- Make sure simulations end
-- Mass and CG overrides                        (pre-alpha)
-- General loader
-
-26.4.:
-- Centering ring inner diameter automatics     (pre-alpha)
-- Landing simulation                   (pre-alpha ??)
-- Parachute/Streamer editing in GUI    (pre-alpha)
-- Launch lug editing in GUI            (pre-alpha)
-
-29.4.:
-- Actual plotting done
-- Refactored source code packages
-
-2.5.:
-- Plotting                             (pre-alpha)
-- Gravity model
-- More units and specific custom units (angle, temperature, ...)
-- Transition/Nose cone description text wrapping
-- Fin set CP jumps at Mach 0.9
-
-- Error dialogs for load/save/etc
-
-3.5.:
-- More materials                       (pre-alpha)
-- File opening from command line
-
-9.5.:
-- Rocket configuration dialog
-- Warnings in poor conditions (transition supersonic)
-- New or old fin-body interference?
-- poista tiedot laminaarisesta vastuksesta
-- vertailuosio
-
-11.5.:
-- Better default values for components
-- Component analysis dialog show zero total mass and CG
-- Compression support in save
-- Simulation storage options
-
-12.5.:
-- Load simulations
-- Update file version to 1.0
-
-13.5.:
-- statistiikat softasta
-
-17.5.:
-- jonkin verran TODOja
-- conclusion
-- viitteet
-- Draw the component icons
-- splashscreen
+- Search field in motor selection dialog
+- Motor selection/editing from Edit configurations dialog
+- Change FreeformFinSet to throw checked exceptions
 
-18.5.:
-- About dialog + version number
index 3a32f3389527c374704e19c02d6397c80717ee03..9fd21c5aa379a87cad4903d1633183b8f463a250 100644 (file)
@@ -1,6 +1,6 @@
 
 # The OpenRocket build version
-build.version=0.9.1
+build.version=0.9.2pre
 
 # The source of the package.  When building a package for a specific
 # distribution (Debian, Fedora etc.), this should be changed appropriately!
index 24c144cdfb755ad4ec791541689eecfa2ec34cdb..164f76da6282d9d1f630c6710f47f5de4cf94a98 100644 (file)
@@ -45,6 +45,7 @@ public class OpenRocketDocument implements ComponentChangeListener {
        private LinkedList<String> undoDescription = new LinkedList<String>();
        
        private String nextDescription = null;
+       private String storedDescription = null;
        
        
        private File file = null;
@@ -70,9 +71,7 @@ public class OpenRocketDocument implements ComponentChangeListener {
                this.configuration = configuration;
                this.rocket = configuration.getRocket();
                
-               undoHistory.add(rocket.copy());
-               undoDescription.add(null);
-               undoPosition = 0;
+               clearUndo();
                
                undoAction = new UndoRedoAction(UndoRedoAction.UNDO);
                redoAction = new UndoRedoAction(UndoRedoAction.REDO);
@@ -234,6 +233,31 @@ public class OpenRocketDocument implements ComponentChangeListener {
        }
 
        
+       /**
+        * Start a time-limited undoable operation.  After the operation {@link #stopUndo()}
+        * must be called, which will restore the previous undo description into effect.
+        * Only one level of start-stop undo descriptions is supported, i.e. start-stop
+        * undo cannot be nested, and no other undo operations may be called between
+        * the start and stop calls.
+        * 
+        * @param description   Description of the following undoable operations.
+        */
+       public void startUndo(String description) {
+               storedDescription = nextDescription;
+               addUndoPosition(description);
+       }
+       
+       /**
+        * End the previous time-limited undoable operation.  This must be called after
+        * {@link #startUndo(String)} has been called before any other undo operations are
+        * performed.
+        */
+       public void stopUndo() {
+               addUndoPosition(storedDescription);
+               storedDescription = null;
+       }
+       
+       
        public Action getUndoAction() {
                return undoAction;
        }
@@ -244,6 +268,24 @@ public class OpenRocketDocument implements ComponentChangeListener {
        }
        
        
+       /**
+        * Clear the undo history.
+        */
+       public void clearUndo() {
+               undoHistory.clear();
+               undoDescription.clear();
+               
+               undoHistory.add(rocket.copy());
+               undoDescription.add(null);
+               undoPosition = 0;
+               
+               if (undoAction != null)
+                       undoAction.setAllValues();
+               if (redoAction != null)
+                       redoAction.setAllValues();
+       }
+       
+       
        @Override
        public void componentChanged(ComponentChangeEvent e) {
                
index e1978a7f5f9327fc2dabf9238aeddd8a3ce85d65..81edde845ce77ce056ded8bc9441222b61fe87ee 100644 (file)
@@ -29,6 +29,7 @@ import net.sf.openrocket.rocketcomponent.EngineBlock;
 import net.sf.openrocket.rocketcomponent.ExternalComponent;
 import net.sf.openrocket.rocketcomponent.FinSet;
 import net.sf.openrocket.rocketcomponent.FreeformFinSet;
+import net.sf.openrocket.rocketcomponent.IllegalFinPointException;
 import net.sf.openrocket.rocketcomponent.InnerTube;
 import net.sf.openrocket.rocketcomponent.InternalComponent;
 import net.sf.openrocket.rocketcomponent.LaunchLug;
@@ -137,6 +138,8 @@ public class OpenRocketLoader extends RocketLoader {
                doc.getDefaultStorageOptions().setSimulationTimeSkip(timeSkip);
                doc.getDefaultStorageOptions().setCompressionEnabled(false); // Set by caller if compressed
                doc.getDefaultStorageOptions().setExplicitlySet(false);
+               
+               doc.clearUndo();
                return doc;
        }
 
@@ -977,7 +980,7 @@ class FinSetPointHandler extends ElementHandler {
                        String content, WarningSet warnings) {
                try {
                        finset.setPoints(coordinates.toArray(new Coordinate[0]));
-               } catch (IllegalArgumentException e) {
+               } catch (IllegalFinPointException e) {
                        warnings.add(Warning.fromString("Freeform fin set point definitions illegal, ignoring."));
                }
        }
index 13f888ca18e0ec999f4fef001636ffffbf5c5cb3..0e8e051e6bfed7a4bf1c054def8df3af5e712c41 100644 (file)
@@ -1,5 +1,6 @@
 package net.sf.openrocket.file;
 
+import java.awt.Component;
 import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileInputStream;
@@ -7,6 +8,8 @@ import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 
+import javax.swing.ProgressMonitorInputStream;
+
 import net.sf.openrocket.aerodynamics.WarningSet;
 import net.sf.openrocket.document.OpenRocketDocument;
 
@@ -14,6 +17,19 @@ import net.sf.openrocket.document.OpenRocketDocument;
 public abstract class RocketLoader {
        protected final WarningSet warnings = new WarningSet();
 
+       
+       public final OpenRocketDocument load(File source, Component parent) 
+       throws RocketLoadException {
+               warnings.clear();
+               
+               try {
+                       return load(new BufferedInputStream(new ProgressMonitorInputStream(
+                                       parent, "Loading " + source.getName(),
+                                       new FileInputStream(source))));
+               } catch (FileNotFoundException e) {
+                       throw new RocketLoadException("File not found: " + source);
+               }
+       }
 
        /**
         * Loads a rocket from the specified File object.
diff --git a/src/net/sf/openrocket/gui/ComponentAnalysisDialog.java b/src/net/sf/openrocket/gui/ComponentAnalysisDialog.java
deleted file mode 100644 (file)
index 7fa39a7..0000000
+++ /dev/null
@@ -1,587 +0,0 @@
-package net.sf.openrocket.gui;
-
-import static net.sf.openrocket.unit.Unit.NOUNIT2;
-
-import java.awt.Color;
-import java.awt.Component;
-import java.awt.Dimension;
-import java.awt.Font;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Vector;
-
-import javax.swing.BorderFactory;
-import javax.swing.JButton;
-import javax.swing.JComboBox;
-import javax.swing.JDialog;
-import javax.swing.JFrame;
-import javax.swing.JLabel;
-import javax.swing.JList;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.JTabbedPane;
-import javax.swing.JTable;
-import javax.swing.JToggleButton;
-import javax.swing.ListSelectionModel;
-import javax.swing.SwingConstants;
-import javax.swing.SwingUtilities;
-import javax.swing.event.ChangeEvent;
-import javax.swing.event.ChangeListener;
-import javax.swing.table.TableCellRenderer;
-
-import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.aerodynamics.AerodynamicCalculator;
-import net.sf.openrocket.aerodynamics.AerodynamicForces;
-import net.sf.openrocket.aerodynamics.FlightConditions;
-import net.sf.openrocket.aerodynamics.Warning;
-import net.sf.openrocket.aerodynamics.WarningSet;
-import net.sf.openrocket.gui.adaptors.Column;
-import net.sf.openrocket.gui.adaptors.ColumnTableModel;
-import net.sf.openrocket.gui.adaptors.DoubleModel;
-import net.sf.openrocket.gui.adaptors.MotorConfigurationModel;
-import net.sf.openrocket.gui.components.BasicSlider;
-import net.sf.openrocket.gui.components.ResizeLabel;
-import net.sf.openrocket.gui.components.StageSelector;
-import net.sf.openrocket.gui.components.UnitSelector;
-import net.sf.openrocket.gui.scalefigure.RocketPanel;
-import net.sf.openrocket.rocketcomponent.Configuration;
-import net.sf.openrocket.rocketcomponent.FinSet;
-import net.sf.openrocket.rocketcomponent.Rocket;
-import net.sf.openrocket.rocketcomponent.RocketComponent;
-import net.sf.openrocket.unit.Unit;
-import net.sf.openrocket.unit.UnitGroup;
-import net.sf.openrocket.util.GUIUtil;
-import net.sf.openrocket.util.MathUtil;
-import net.sf.openrocket.util.Prefs;
-
-public class ComponentAnalysisDialog extends JDialog implements ChangeListener {
-
-       private static ComponentAnalysisDialog singletonDialog = null;
-
-       
-       private final FlightConditions conditions;
-       private final Configuration configuration;
-       private final DoubleModel theta, aoa, mach, roll;
-       private final JToggleButton worstToggle;
-       private boolean fakeChange = false;
-       private AerodynamicCalculator calculator;
-       
-       private final ColumnTableModel cpTableModel;
-       private final ColumnTableModel dragTableModel;
-       private final ColumnTableModel rollTableModel;
-       
-       private final JList warningList;
-       
-       
-       private final List<AerodynamicForces> cpData = new ArrayList<AerodynamicForces>();
-       private final List<AerodynamicForces> dragData = new ArrayList<AerodynamicForces>();
-       private double totalCD = 0;
-       private final List<AerodynamicForces> rollData = new ArrayList<AerodynamicForces>();
-       
-       
-       public ComponentAnalysisDialog(final RocketPanel rocketPanel) {
-               super(SwingUtilities.getWindowAncestor(rocketPanel), "Component analysis");
-
-               JTable table;
-
-               JPanel panel = new JPanel(new MigLayout("fill","[][35lp::][fill][fill]"));
-               add(panel);
-               
-               this.configuration = rocketPanel.getConfiguration();
-               this.calculator = rocketPanel.getCalculator().newInstance();
-               this.calculator.setConfiguration(configuration);
-
-               
-               conditions = new FlightConditions(configuration);
-               
-               rocketPanel.setCPAOA(0);
-               aoa = new DoubleModel(rocketPanel, "CPAOA", UnitGroup.UNITS_ANGLE, 0, Math.PI);
-               rocketPanel.setCPMach(Prefs.getDefaultMach());
-               mach = new DoubleModel(rocketPanel, "CPMach", UnitGroup.UNITS_COEFFICIENT, 0);
-               rocketPanel.setCPTheta(rocketPanel.getFigure().getRotation());
-               theta = new DoubleModel(rocketPanel, "CPTheta", UnitGroup.UNITS_ANGLE, 0, 2*Math.PI);
-               rocketPanel.setCPRoll(0);
-               roll = new DoubleModel(rocketPanel, "CPRoll", UnitGroup.UNITS_ROLL);
-               
-               
-               panel.add(new JLabel("Wind direction:"),"width 100lp!");
-               panel.add(new UnitSelector(theta,true),"width 50lp!");
-               BasicSlider slider = new BasicSlider(theta.getSliderModel(0, 2*Math.PI));
-               panel.add(slider,"growx, split 2");
-               worstToggle = new JToggleButton("Worst");
-               worstToggle.setSelected(true);
-               worstToggle.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               stateChanged(null);
-                       }
-               });
-               slider.addChangeListener(new ChangeListener() {
-                       @Override
-                       public void stateChanged(ChangeEvent e) {
-                               if (!fakeChange)
-                                       worstToggle.setSelected(false);
-                       }
-               });
-               panel.add(worstToggle,"");
-               
-               
-               warningList = new JList();
-               JScrollPane scrollPane = new JScrollPane(warningList);
-               scrollPane.setBorder(BorderFactory.createTitledBorder("Warnings:"));
-               panel.add(scrollPane,"gap paragraph, spany 4, width 300lp!, growy 1, height :100lp:, wrap");
-               
-               
-               panel.add(new JLabel("Angle of attack:"),"width 100lp!");
-               panel.add(new UnitSelector(aoa,true),"width 50lp!");
-               panel.add(new BasicSlider(aoa.getSliderModel(0, Math.PI)),"growx, wrap");
-               
-               panel.add(new JLabel("Mach number:"),"width 100lp!");
-               panel.add(new UnitSelector(mach,true),"width 50lp!");
-               panel.add(new BasicSlider(mach.getSliderModel(0, 3)),"growx, wrap");
-               
-               panel.add(new JLabel("Roll rate:"), "width 100lp!");
-               panel.add(new UnitSelector(roll,true),"width 50lp!");
-               panel.add(new BasicSlider(roll.getSliderModel(-20*2*Math.PI, 20*2*Math.PI)),
-                               "growx, wrap paragraph");
-               
-               
-               // Stage and motor selection:
-               
-               panel.add(new JLabel("Active stages:"),"spanx, split, gapafter rel");
-               panel.add(new StageSelector(configuration),"gapafter paragraph");
-                               
-               JLabel label = new JLabel("Motor configuration:");
-               label.setHorizontalAlignment(JLabel.RIGHT);
-               panel.add(label,"growx, right");
-               panel.add(new JComboBox(new MotorConfigurationModel(configuration)),"wrap");
-
-               
-               
-               // Tabbed pane
-               
-               JTabbedPane tabbedPane = new JTabbedPane();
-               panel.add(tabbedPane, "spanx, growx, growy");
-               
-               
-               // Create the CP data table
-               cpTableModel = new ColumnTableModel(
-                               
-                               new Column("Component") {
-                                       @Override public Object getValueAt(int row) {
-                                               RocketComponent c = cpData.get(row).component;
-                                               if (c instanceof Rocket) {
-                                                       return "Total";
-                                               }
-                                               return c.toString();
-                                       }
-                                       @Override public int getDefaultWidth() {
-                                               return 200;
-                                       }
-                               },
-                               new Column("CG / " + UnitGroup.UNITS_LENGTH.getDefaultUnit().getUnit()) {
-                                       private Unit unit = UnitGroup.UNITS_LENGTH.getDefaultUnit();
-                                       @Override public Object getValueAt(int row) {
-                                               return unit.toString(cpData.get(row).cg.x);
-                                       }
-                               },
-                               new Column("Mass / " + UnitGroup.UNITS_MASS.getDefaultUnit().getUnit()) {
-                                       private Unit unit = UnitGroup.UNITS_MASS.getDefaultUnit();
-                                       @Override
-                                       public Object getValueAt(int row) {
-                                               return unit.toString(cpData.get(row).cg.weight);
-                                       }
-                               },
-                               new Column("CP / " + UnitGroup.UNITS_LENGTH.getDefaultUnit().getUnit()) {
-                                       private Unit unit = UnitGroup.UNITS_LENGTH.getDefaultUnit();
-                                       @Override public Object getValueAt(int row) {
-                                               return unit.toString(cpData.get(row).cp.x);
-                                       }
-                               },
-                               new Column("<html>C<sub>N<sub>\u03b1</sub></sub>") {
-                                       @Override public Object getValueAt(int row) {
-                                               return NOUNIT2.toString(cpData.get(row).cp.weight);
-                                       }
-                               }
-                               
-               ) {
-                       @Override public int getRowCount() {
-                               return cpData.size();
-                       }
-               };
-               
-               table = new JTable(cpTableModel);
-               table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
-               table.setSelectionBackground(Color.LIGHT_GRAY);
-               table.setSelectionForeground(Color.BLACK);
-               cpTableModel.setColumnWidths(table.getColumnModel());
-               
-               table.setDefaultRenderer(Object.class, new CustomCellRenderer());
-//             table.setShowHorizontalLines(false);
-//             table.setShowVerticalLines(true);
-               
-               JScrollPane scrollpane = new JScrollPane(table);
-               scrollpane.setPreferredSize(new Dimension(600,200));
-               
-               tabbedPane.addTab("Stability", null, scrollpane, "Stability information");
-               
-               
-               
-               // Create the drag data table
-               dragTableModel = new ColumnTableModel(
-                               new Column("Component") {
-                                       @Override public Object getValueAt(int row) {
-                                               RocketComponent c = dragData.get(row).component;
-                                               if (c instanceof Rocket) {
-                                                       return "Total";
-                                               }
-                                               return c.toString();
-                                       }
-                                       @Override public int getDefaultWidth() {
-                                               return 200;
-                                       }
-                               },
-                               new Column("<html>Pressure C<sub>D</sub>") {
-                                       @Override public Object getValueAt(int row) {
-                                               return dragData.get(row).pressureCD;
-                                       }
-                               },
-                               new Column("<html>Base C<sub>D</sub>") {
-                                       @Override public Object getValueAt(int row) {
-                                               return dragData.get(row).baseCD;
-                                       }
-                               },
-                               new Column("<html>Friction C<sub>D</sub>") {
-                                       @Override public Object getValueAt(int row) {
-                                               return dragData.get(row).frictionCD;
-                                       }
-                               },
-                               new Column("<html>Total C<sub>D</sub>") {
-                                       @Override public Object getValueAt(int row) {
-                                               return dragData.get(row).CD;
-                                       }
-                               }
-               ) {
-                       @Override public int getRowCount() {
-                               return dragData.size();
-                       }                       
-               };
-               
-
-               table = new JTable(dragTableModel);
-               table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
-               table.setSelectionBackground(Color.LIGHT_GRAY);
-               table.setSelectionForeground(Color.BLACK);
-               dragTableModel.setColumnWidths(table.getColumnModel());
-               
-               table.setDefaultRenderer(Object.class, new DragCellRenderer(new Color(0.5f,1.0f,0.5f)));
-//             table.setShowHorizontalLines(false);
-//             table.setShowVerticalLines(true);
-               
-               scrollpane = new JScrollPane(table);
-               scrollpane.setPreferredSize(new Dimension(600,200));
-               
-               tabbedPane.addTab("Drag characteristics", null, scrollpane, "Drag characteristics");
-               
-               
-               
-               
-               // Create the roll data table
-               rollTableModel = new ColumnTableModel(
-                               new Column("Component") {
-                                       @Override public Object getValueAt(int row) {
-                                               RocketComponent c = rollData.get(row).component;
-                                               if (c instanceof Rocket) {
-                                                       return "Total";
-                                               }
-                                               return c.toString();
-                                       }
-                               },
-                               new Column("Roll forcing coefficient") {
-                                       @Override public Object getValueAt(int row) {
-                                               return rollData.get(row).CrollForce;
-                                       }
-                               },
-                               new Column("Roll damping coefficient") {
-                                       @Override public Object getValueAt(int row) {
-                                               return rollData.get(row).CrollDamp;
-                                       }
-                               },
-                               new Column("<html>Total C<sub>l</sub>") {
-                                       @Override public Object getValueAt(int row) {
-                                               return rollData.get(row).Croll;
-                                       }
-                               }
-               ) {
-                       @Override public int getRowCount() {
-                               return rollData.size();
-                       }                       
-               };
-               
-
-               table = new JTable(rollTableModel);
-               table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
-               table.setSelectionBackground(Color.LIGHT_GRAY);
-               table.setSelectionForeground(Color.BLACK);
-               rollTableModel.setColumnWidths(table.getColumnModel());
-               
-               scrollpane = new JScrollPane(table);
-               scrollpane.setPreferredSize(new Dimension(600,200));
-               
-               tabbedPane.addTab("Roll dynamics", null, scrollpane, "Roll dynamics");
-               
-               
-               
-               
-               
-               
-               // Add the data updater to listen to changes in aoa and theta
-               mach.addChangeListener(this);
-               theta.addChangeListener(this);
-               aoa.addChangeListener(this);
-               roll.addChangeListener(this);
-               configuration.addChangeListener(this);
-               this.stateChanged(null);
-               
-               
-               
-               // Remove listeners when closing window
-               this.addWindowListener(new WindowAdapter() {
-                       @Override
-                       public void windowClosed(WindowEvent e) {
-                               System.out.println("Closing method called: "+this);
-                               theta.removeChangeListener(ComponentAnalysisDialog.this);
-                               aoa.removeChangeListener(ComponentAnalysisDialog.this);
-                               mach.removeChangeListener(ComponentAnalysisDialog.this);
-                               roll.removeChangeListener(ComponentAnalysisDialog.this);
-                               configuration.removeChangeListener(ComponentAnalysisDialog.this);
-                               System.out.println("SETTING NAN VALUES");
-                               rocketPanel.setCPAOA(Double.NaN);
-                               rocketPanel.setCPTheta(Double.NaN);
-                               rocketPanel.setCPMach(Double.NaN);
-                               rocketPanel.setCPRoll(Double.NaN);
-                               singletonDialog = null;
-                       }
-               });
-               
-
-               panel.add(new ResizeLabel("Reference length: ", -1), 
-                               "span, split, gapleft para, gapright rel");
-               DoubleModel dm = new DoubleModel(conditions, "RefLength", UnitGroup.UNITS_LENGTH);
-               UnitSelector sel = new UnitSelector(dm, true);
-               sel.resizeFont(-1);
-               panel.add(sel, "gapright para");
-               
-               panel.add(new ResizeLabel("Reference area: ", -1), "gapright rel");
-               dm = new DoubleModel(conditions, "RefArea", UnitGroup.UNITS_AREA);
-               sel = new UnitSelector(dm, true);
-               sel.resizeFont(-1);
-               panel.add(sel, "wrap");
-               
-               
-
-               // Buttons
-               JButton button;
-               
-               // TODO: LOW: printing
-//             button = new JButton("Print");
-//             button.addActionListener(new ActionListener() {
-//                     public void actionPerformed(ActionEvent e) {
-//                             try {
-//                                     table.print();
-//                             } catch (PrinterException e1) {
-//                                     JOptionPane.showMessageDialog(ComponentAnalysisDialog.this, 
-//                                                     "An error occurred while printing.", "Print error",
-//                                                     JOptionPane.ERROR_MESSAGE);
-//                             }
-//                     }
-//             });
-//             panel.add(button,"tag ok");
-               
-               button = new JButton("Close");
-               button.addActionListener(new ActionListener() {
-                       public void actionPerformed(ActionEvent e) {
-                               ComponentAnalysisDialog.this.dispose();
-                       }
-               });
-               panel.add(button,"span, split, tag cancel");
-               
-
-               setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
-               GUIUtil.installEscapeCloseOperation(this);
-               pack();
-       }
-       
-       
-       
-       /**
-        * Updates the data in the table and fires a table data change event.
-        */
-       @Override
-       public void stateChanged(ChangeEvent e) {
-               AerodynamicForces forces;
-               WarningSet set = new WarningSet();
-               conditions.setAOA(aoa.getValue());
-               conditions.setTheta(theta.getValue());
-               conditions.setMach(mach.getValue());
-               conditions.setRollRate(roll.getValue());
-               conditions.setReference(configuration);
-               
-               if (worstToggle.isSelected()) {
-                       calculator.getWorstCP(conditions, null);
-                       if (!MathUtil.equals(conditions.getTheta(), theta.getValue())) {
-                               fakeChange = true;
-                               theta.setValue(conditions.getTheta());  // Fires a stateChanged event
-                               fakeChange = false;
-                               return;
-                       }
-               }
-               
-               Map<RocketComponent, AerodynamicForces> data = calculator.getForceAnalysis(conditions, set);
-               
-               cpData.clear();
-               dragData.clear();
-               rollData.clear();
-               for (RocketComponent c: configuration) {
-                       forces = data.get(c);
-                       if (forces == null)
-                               continue;
-                       if (forces.cp != null) {
-                               cpData.add(forces);
-                       }
-                       if (!Double.isNaN(forces.CD)) {
-                               dragData.add(forces);
-                       }
-                       if (c instanceof FinSet) {
-                               rollData.add(forces);
-                       }
-               }
-               forces = data.get(configuration.getRocket());
-               if (forces != null) {
-                       cpData.add(forces);
-                       dragData.add(forces);
-                       rollData.add(forces);
-                       totalCD = forces.CD;
-               } else {
-                       totalCD = 0;
-               }
-               
-               // Set warnings
-               if (set.isEmpty()) {
-                       warningList.setListData(new String[] {
-                                       "<html><i><font color=\"gray\">No warnings.</font></i>"
-                       });
-               } else {
-                       warningList.setListData(new Vector<Warning>(set));
-               }
-               
-               cpTableModel.fireTableDataChanged();
-               dragTableModel.fireTableDataChanged();
-               rollTableModel.fireTableDataChanged();
-       }
-       
-       
-       private class CustomCellRenderer extends JLabel implements TableCellRenderer {
-               private final Font normalFont;
-               private final Font boldFont;
-               
-               public CustomCellRenderer() {
-                       super();
-                       normalFont = getFont();
-                       boldFont = normalFont.deriveFont(Font.BOLD);
-               }
-               @Override
-               public Component getTableCellRendererComponent(JTable table, Object value, 
-                               boolean isSelected, boolean hasFocus, int row, int column) {
-                       
-                       this.setText(value.toString());
-                       
-                       if ((row < 0) || (row >= cpData.size()))
-                                       return this;
-                       
-                       if (cpData.get(row).component instanceof Rocket) {
-                               this.setFont(boldFont);
-                       } else {
-                               this.setFont(normalFont);
-                       }
-                       return this;
-               }
-       }
-       
-
-       
-       private class DragCellRenderer extends JLabel implements TableCellRenderer {
-               private final Font normalFont;
-               private final Font boldFont;
-               
-               private final float[] start = { 0.3333f, 0.2f, 1.0f };
-               private final float[] end = { 0.0f, 0.8f, 1.0f };
-               
-               
-               public DragCellRenderer(Color baseColor) {
-                       super();
-                       normalFont = getFont();
-                       boldFont = normalFont.deriveFont(Font.BOLD);
-               }
-               @Override
-               public Component getTableCellRendererComponent(JTable table, Object value, 
-                               boolean isSelected, boolean hasFocus, int row, int column) {
-                       
-                       if (value instanceof Double) {
-                               
-                               // A drag coefficient
-                               double cd = (Double)value;
-                               this.setText(String.format("%.2f (%.0f%%)", cd, 100*cd/totalCD));
-
-                               float r = (float)(cd/1.5);
-                               
-                               float hue = MathUtil.clamp(0.3333f * (1-2.0f*r), 0, 0.3333f);
-                               float sat = MathUtil.clamp(0.8f*r + 0.1f*(1-r), 0, 1);
-                               float val = 1.0f;
-                               
-                               this.setBackground(Color.getHSBColor(hue, sat, val));
-                               this.setOpaque(true);
-                               this.setHorizontalAlignment(SwingConstants.CENTER);
-                               
-                       } else {
-                               
-                               // Other
-                               this.setText(value.toString());
-                               this.setOpaque(false);
-                               this.setHorizontalAlignment(SwingConstants.LEFT);
-                               
-                       }
-                       
-                       if ((row < 0) || (row >= dragData.size()))
-                                       return this;
-                       
-                       if ((dragData.get(row).component instanceof Rocket) || (column == 4)){
-                               this.setFont(boldFont);
-                       } else {
-                               this.setFont(normalFont);
-                       }
-                       return this;
-               }
-       }
-       
-       
-       /////////  Singleton implementation
-       
-       public static void showDialog(RocketPanel rocketpanel) {
-               if (singletonDialog != null)
-                       singletonDialog.dispose();
-               singletonDialog = new ComponentAnalysisDialog(rocketpanel);
-               singletonDialog.setVisible(true);
-       }
-       
-       public static void hideDialog() {
-               if (singletonDialog != null)
-                       singletonDialog.dispose();
-       }
-       
-}
diff --git a/src/net/sf/openrocket/gui/DetailDialog.java b/src/net/sf/openrocket/gui/DetailDialog.java
deleted file mode 100644 (file)
index f0c0830..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-package net.sf.openrocket.gui;
-
-import java.awt.Component;
-
-import javax.swing.JOptionPane;
-
-public class DetailDialog {
-
-       public static void showDetailedMessageDialog(Component parentComponent, Object message, 
-                       String details, String title, int messageType)  {
-               
-               // TODO: HIGH: Detailed dialog
-               JOptionPane.showMessageDialog(parentComponent, message, title, messageType, null);
-               
-       }
-       
-       
-}
diff --git a/src/net/sf/openrocket/gui/PreferencesDialog.java b/src/net/sf/openrocket/gui/PreferencesDialog.java
deleted file mode 100644 (file)
index 2cdb4c4..0000000
+++ /dev/null
@@ -1,390 +0,0 @@
-package net.sf.openrocket.gui;
-
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.swing.AbstractListModel;
-import javax.swing.ComboBoxModel;
-import javax.swing.JButton;
-import javax.swing.JComboBox;
-import javax.swing.JDialog;
-import javax.swing.JFrame;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.JTabbedPane;
-
-import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.gui.components.ResizeLabel;
-import net.sf.openrocket.unit.Unit;
-import net.sf.openrocket.unit.UnitGroup;
-import net.sf.openrocket.util.GUIUtil;
-import net.sf.openrocket.util.Prefs;
-
-public class PreferencesDialog extends JDialog {
-       
-       private final List<DefaultUnitSelector> unitSelectors = new ArrayList<DefaultUnitSelector>();
-
-       private PreferencesDialog() {
-               super((JFrame)null, "Preferences", true);
-               
-               JPanel panel = new JPanel(new MigLayout("fill, gap unrel","[grow]","[grow][]"));
-                               
-               JTabbedPane tabbedPane = new JTabbedPane();
-               panel.add(tabbedPane,"grow, wrap");
-               
-
-               tabbedPane.addTab("Units", null, unitsPane(), "Default units");
-               tabbedPane.addTab("Confirmation", null, confirmationPane(), "Confirmation dialog settings");
-               
-               
-               
-               JButton close = new JButton("Close");
-               close.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent arg0) {
-                               PreferencesDialog.this.setVisible(false);
-                               PreferencesDialog.this.dispose();
-                       }
-               });
-               panel.add(close,"span, right, tag close");
-               
-               this.setContentPane(panel);
-               pack();
-               setAlwaysOnTop(true);
-               this.setLocationRelativeTo(null);
-               
-               this.addWindowListener(new WindowAdapter() {
-                       @Override
-                       public void windowClosed(WindowEvent e) {
-                               Prefs.storeDefaultUnits();
-                       }
-               });
-
-               GUIUtil.setDefaultButton(close);
-               GUIUtil.installEscapeCloseOperation(this);
-       }
-       
-       
-       private JPanel confirmationPane() {
-               JPanel panel = new JPanel(new MigLayout("fill"));
-               
-               panel.add(new JLabel("Position to insert new body components:"));
-               panel.add(new JComboBox(new PrefChoiseSelector(Prefs.BODY_COMPONENT_INSERT_POSITION_KEY,
-                               "Always ask", "Insert in middle", "Add to end")), "wrap para, sg combos");
-               
-               panel.add(new JLabel("Confirm deletion of simulations:"));
-               panel.add(new JComboBox(new PrefBooleanSelector(Prefs.CONFIRM_DELETE_SIMULATION,
-                               "Delete", "Confirm", true)), "wrap para, sg combos");
-               
-               return panel;
-       }
-       
-       private JPanel unitsPane() {
-               JPanel panel = new JPanel(new MigLayout("", "[][]40lp[][]"));
-               JComboBox combo;
-               
-               panel.add(new JLabel("Select your preferred units:"), "span, wrap paragraph");
-               
-/*
-               public static final UnitGroup UNITS_LENGTH;
-               public static final UnitGroup UNITS_MOTOR_DIMENSIONS;
-               public static final UnitGroup UNITS_DISTANCE;
-               
-               public static final UnitGroup UNITS_VELOCITY;
-               public static final UnitGroup UNITS_ACCELERATION;
-               public static final UnitGroup UNITS_MASS;
-               public static final UnitGroup UNITS_FORCE;
-               public static final UnitGroup UNITS_IMPULSE;
-
-               public static final UnitGroup UNITS_STABILITY;
-               public static final UnitGroup UNITS_FLIGHT_TIME;
-               public static final UnitGroup UNITS_ROLL;
-               
-               public static final UnitGroup UNITS_AREA;
-               public static final UnitGroup UNITS_DENSITY_LINE;
-               public static final UnitGroup UNITS_DENSITY_SURFACE;
-               public static final UnitGroup UNITS_DENSITY_BULK;
-               public static final UnitGroup UNITS_ROUGHNESS;
-               
-               public static final UnitGroup UNITS_TEMPERATURE;
-               public static final UnitGroup UNITS_PRESSURE;
-               public static final UnitGroup UNITS_ANGLE;
-*/
-               
-               panel.add(new JLabel("Rocket dimensions:"));
-               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_LENGTH));
-               panel.add(combo, "sizegroup boxes");
-               
-               panel.add(new JLabel("Line density:"));
-               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_LINE));
-               panel.add(combo, "sizegroup boxes, wrap");
-               
-               
-               
-               panel.add(new JLabel("Motor dimensions:"));
-               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_MOTOR_DIMENSIONS));
-               panel.add(combo, "sizegroup boxes");
-               
-               panel.add(new JLabel("Surface density:"));
-               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_SURFACE));
-               panel.add(combo, "sizegroup boxes, wrap");
-               
-
-               
-               panel.add(new JLabel("Distance:"));
-               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DISTANCE));
-               panel.add(combo, "sizegroup boxes");
-               
-               panel.add(new JLabel("Bulk density::"));
-               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_BULK));
-               panel.add(combo, "sizegroup boxes, wrap");
-               
-
-               
-               panel.add(new JLabel("Velocity:"));
-               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_VELOCITY));
-               panel.add(combo, "sizegroup boxes");
-
-               panel.add(new JLabel("Surface roughness:"));
-               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ROUGHNESS));
-               panel.add(combo, "sizegroup boxes, wrap");
-               
-               
-               
-               panel.add(new JLabel("Acceleration:"));
-               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ACCELERATION));
-               panel.add(combo, "sizegroup boxes");
-
-               panel.add(new JLabel("Area:"));
-               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_AREA));
-               panel.add(combo, "sizegroup boxes, wrap");
-               
-               
-
-               panel.add(new JLabel("Mass:"));
-               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_MASS));
-               panel.add(combo, "sizegroup boxes");
-               
-               panel.add(new JLabel("Angle:"));
-               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ANGLE));
-               panel.add(combo, "sizegroup boxes, wrap");
-               
-
-               
-               panel.add(new JLabel("Force:"));
-               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_FORCE));
-               panel.add(combo, "sizegroup boxes");
-               
-               panel.add(new JLabel("Roll rate:"));
-               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ROLL));
-               panel.add(combo, "sizegroup boxes, wrap");
-               
-
-               
-               panel.add(new JLabel("Total impulse:"));
-               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_IMPULSE));
-               panel.add(combo, "sizegroup boxes");
-               
-               panel.add(new JLabel("Temperature:"));
-               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_TEMPERATURE));
-               panel.add(combo, "sizegroup boxes, wrap");
-               
-
-               
-               panel.add(new JLabel("Stability:"));
-               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_STABILITY));
-               panel.add(combo, "sizegroup boxes");
-
-               panel.add(new JLabel("Pressure:"));
-               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_PRESSURE));
-               panel.add(combo, "sizegroup boxes, wrap para");
-               
-               
-               
-               JButton button = new JButton("Default metric");
-               button.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               UnitGroup.setDefaultMetricUnits();
-                               for (DefaultUnitSelector s: unitSelectors)
-                                       s.fireChange();
-                       }
-               });
-               panel.add(button, "spanx, split 2, grow");
-               
-               button = new JButton("Default imperial");
-               button.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               UnitGroup.setDefaultImperialUnits();
-                               for (DefaultUnitSelector s: unitSelectors)
-                                       s.fireChange();
-                       }
-               });
-               panel.add(button, "grow, wrap para");
-               
-               
-               panel.add(new ResizeLabel("The effects will take place the next time you open a window.",-2),
-                               "spanx, wrap");
-               
-
-               return panel;
-       }
-       
-       
-       
-       
-       private class DefaultUnitSelector extends AbstractListModel implements ComboBoxModel {
-               
-               private final UnitGroup group;
-               public DefaultUnitSelector(UnitGroup group) {
-                       this.group = group;
-                       unitSelectors.add(this);
-               }
-               
-               @Override
-               public Object getSelectedItem() {
-                       return group.getDefaultUnit();
-               }
-               @Override
-               public void setSelectedItem(Object item) {
-                       if (!(item instanceof Unit)) {
-                               throw new IllegalArgumentException("Illegal argument "+item);
-                       }
-                       group.setDefaultUnit(group.getUnitIndex((Unit)item));
-               }
-               @Override
-               public Object getElementAt(int index) {
-                       return group.getUnit(index);
-               }
-               @Override
-               public int getSize() {
-                       return group.getUnitCount();
-               }
-               
-               
-               public void fireChange() {
-                       this.fireContentsChanged(this, 0, this.getSize());
-               }
-       }
-       
-
-       
-       private class PrefChoiseSelector extends AbstractListModel implements ComboBoxModel {
-               private final String preference;
-               private final String[] descriptions;
-               
-               public PrefChoiseSelector(String preference, String ... descriptions) {
-                       this.preference = preference;
-                       this.descriptions = descriptions;
-               }
-               
-               @Override
-               public Object getSelectedItem() {
-                       return descriptions[Prefs.getChoise(preference, descriptions.length, 0)];
-               }
-               
-               @Override
-               public void setSelectedItem(Object item) {
-                       if (!(item instanceof String)) {
-                               throw new IllegalArgumentException("Illegal argument "+item);
-                       }
-                       int index;
-                       for (index = 0; index < descriptions.length; index++) {
-                               if (((String)item).equalsIgnoreCase(descriptions[index]))
-                                       break;
-                       }
-                       if (index >= descriptions.length) {
-                               throw new IllegalArgumentException("Illegal argument "+item);
-                       }
-                       
-                       Prefs.putChoise(preference, index);
-               }
-               
-               @Override
-               public Object getElementAt(int index) {
-                       return descriptions[index];
-               }
-               @Override
-               public int getSize() {
-                       return descriptions.length;
-               }
-       }
-       
-
-       private class PrefBooleanSelector extends AbstractListModel implements ComboBoxModel {
-               private final String preference;
-               private final String trueDesc, falseDesc;
-               private final boolean def;
-               
-               public PrefBooleanSelector(String preference, String falseDescription, 
-                               String trueDescription, boolean defaultState) {
-                       this.preference = preference;
-                       this.trueDesc = trueDescription;
-                       this.falseDesc = falseDescription;
-                       this.def = defaultState;
-               }
-               
-               @Override
-               public Object getSelectedItem() {
-                       if (Prefs.NODE.getBoolean(preference, def)) {
-                               return trueDesc;
-                       } else {
-                               return falseDesc;
-                       }
-               }
-               
-               @Override
-               public void setSelectedItem(Object item) {
-                       if (!(item instanceof String)) {
-                               throw new IllegalArgumentException("Illegal argument "+item);
-                       }
-                       
-                       if (trueDesc.equals(item)) {
-                               Prefs.NODE.putBoolean(preference, true);
-                       } else if (falseDesc.equals(item)) {
-                               Prefs.NODE.putBoolean(preference, false);
-                       } else {
-                               throw new IllegalArgumentException("Illegal argument "+item);
-                       }
-               }
-               
-               @Override
-               public Object getElementAt(int index) {
-                       switch (index) {
-                       case 0:
-                               return def ? trueDesc : falseDesc;
-
-                       case 1:
-                               return def ? falseDesc: trueDesc;
-                               
-                       default:
-                               throw new IndexOutOfBoundsException("Boolean asked for index="+index);
-                       }
-               }
-               @Override
-               public int getSize() {
-                       return 2;
-               }
-       }
-       
-       
-       
-       ////////  Singleton implementation  ////////
-       
-       private static PreferencesDialog dialog = null;
-       
-       public static void showPreferences() {
-               if (dialog != null) {
-                       dialog.dispose();
-               }
-               dialog = new PreferencesDialog();
-               dialog.setVisible(true);
-       }
-       
-       
-}
index b24d0c94168e698612ee4c1e1ee048813ca8a36d..92e8473948e0f5aa6214d40f928e359078b2d615 100644 (file)
@@ -1,44 +1,22 @@
 package net.sf.openrocket.gui.adaptors;
 
 
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.Map;
 
 import javax.swing.ComboBoxModel;
-import javax.swing.JButton;
-import javax.swing.JDialog;
-import javax.swing.JFrame;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.JTable;
-import javax.swing.JTextField;
-import javax.swing.ListSelectionModel;
 import javax.swing.SwingUtilities;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
 import javax.swing.event.EventListenerList;
 import javax.swing.event.ListDataEvent;
 import javax.swing.event.ListDataListener;
-import javax.swing.event.ListSelectionEvent;
-import javax.swing.event.ListSelectionListener;
 
-import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.gui.TextFieldListener;
-import net.sf.openrocket.gui.components.ResizeLabel;
+import net.sf.openrocket.gui.dialogs.EditMotorConfigurationDialog;
+import net.sf.openrocket.gui.main.BasicFrame;
 import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
 import net.sf.openrocket.rocketcomponent.Configuration;
-import net.sf.openrocket.rocketcomponent.Motor;
-import net.sf.openrocket.rocketcomponent.MotorMount;
 import net.sf.openrocket.rocketcomponent.Rocket;
-import net.sf.openrocket.rocketcomponent.RocketComponent;
-import net.sf.openrocket.util.GUIUtil;
 
 public class MotorConfigurationModel implements ComboBoxModel, ChangeListener {
 
@@ -91,13 +69,8 @@ public class MotorConfigurationModel implements ComboBoxModel, ChangeListener {
                        SwingUtilities.invokeLater(new Runnable() {
                                @Override
                                public void run() {
-                                       EditConfigurationDialog dialog = new EditConfigurationDialog();
-                                       dialog.setVisible(true);
-                                       
-                                       if (dialog.isRowSelected()) {
-                                               rocket.getDefaultConfiguration().setMotorConfigurationID(
-                                                               dialog.getSelectedID());
-                                       }
+                                       new EditMotorConfigurationDialog(rocket, BasicFrame.findFrame(rocket))
+                                               .setVisible(true);
                                }
                        });
 
@@ -185,198 +158,5 @@ public class MotorConfigurationModel implements ComboBoxModel, ChangeListener {
                }
        }
        
-       
-       private class EditConfigurationDialog extends JDialog {
-               private final ColumnTableModel tableModel;
-               private String[] ids;
-               int selection = -1;
-               
-               private final JButton addButton;
-               private final JButton removeButton;
-               private final JTextField nameField;
-               
-               private final JTable table;
-               
-               
-               public boolean isRowSelected() {
-                       return selection >= 0;
-               }
-               
-               public String getSelectedID() {
-                       if (selection >= 0)
-                               return ids[selection];
-                       return null;
-               }
-               
-               
-               public EditConfigurationDialog() {
-                       super((JFrame)null, "Edit configurations", true);
-
-                       ids = rocket.getMotorConfigurationIDs();
-                       
-                       // Create columns
-                       ArrayList<Column> columnList = new ArrayList<Column>();
-                       columnList.add(new Column("Name") {
-                               @Override
-                               public Object getValueAt(int row) {
-                                       return rocket.getMotorConfigurationNameOrDescription(ids[row]);
-                               }
-                       });
-                       
-                       // Create columns from the motor mounts
-                       Iterator<RocketComponent> iterator = rocket.deepIterator();
-                       while (iterator.hasNext()) {
-                               RocketComponent c = iterator.next();
-                               if (!(c instanceof MotorMount))
-                                       continue;
-                               
-                               final MotorMount mount = (MotorMount)c;
-                               if (!mount.isMotorMount())
-                                       continue;
-                               
-                               Column col = new Column(c.getName()) {
-                                       @Override
-                                       public Object getValueAt(int row) {
-                                               Motor motor = mount.getMotor(ids[row]);
-                                               if (motor == null)
-                                                       return "";
-                                               return motor.getDesignation(mount.getMotorDelay(ids[row]));
-                                       }
-                               };
-                               columnList.add(col);
-                       }
-                       tableModel = new ColumnTableModel(columnList.toArray(new Column[0])) {
-                               @Override
-                               public int getRowCount() {
-                                       return ids.length;
-                               }
-                       };
-
-                       
-                       
-                       // Create the panel
-                       JPanel panel = new JPanel(new MigLayout("fill","[shrink][grow]"));
-                       
-                       
-                       panel.add(new JLabel("Configuration name:"), "gapright para");
-                       nameField = new JTextField();
-                       new TextFieldListener() {
-                               @Override
-                               public void setText(String text) {
-                                       if (selection < 0 || ids[selection] == null)
-                                               return;
-                                       rocket.setMotorConfigurationName(ids[selection], text);
-                                       fireChange();
-                               }
-                       }.listenTo(nameField);
-                       panel.add(nameField, "growx, wrap");
-                       
-                       panel.add(new ResizeLabel("Leave empty for default description", -2), 
-                                       "skip, growx, wrap para");
-                       
-                       
-                       table = new JTable(tableModel);
-                       table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
-                       table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
-                               @Override
-                               public void valueChanged(ListSelectionEvent e) {
-                                       updateSelection();
-                               }
-                       });
-
-                       // Mouse listener to act on double-clicks
-                       table.addMouseListener(new MouseAdapter() {
-                               @Override
-                               public void mouseClicked(MouseEvent e) {
-                                       if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) {
-                                               EditConfigurationDialog.this.dispose();
-                                       }
-                               }
-                       });
-                        
-                       
-                       
-                       JScrollPane scrollpane = new JScrollPane(table);
-                       panel.add(scrollpane, "spanx, height 150lp, width 400lp, grow, wrap");
-                       
-                       
-                       addButton = new JButton("New");
-                       addButton.addActionListener(new ActionListener() {
-                               @Override
-                               public void actionPerformed(ActionEvent e) {
-                                       String id = rocket.newMotorConfigurationID();
-                                       ids = rocket.getMotorConfigurationIDs();
-                                       tableModel.fireTableDataChanged();
-                                       int sel;
-                                       for (sel=0; sel < ids.length; sel++) {
-                                               if (id.equals(ids[sel]))
-                                                       break;
-                                       }
-                                       table.getSelectionModel().addSelectionInterval(sel, sel);
-                               }
-                       });
-                       panel.add(addButton, "growx, spanx, split 2");
-                       
-                       removeButton = new JButton("Remove");
-                       removeButton.addActionListener(new ActionListener() {
-                               @Override
-                               public void actionPerformed(ActionEvent e) {
-                                       int sel = table.getSelectedRow();
-                                       if (sel < 0 || sel >= ids.length || ids[sel] == null)
-                                               return;
-                                       rocket.removeMotorConfigurationID(ids[sel]);
-                                       ids = rocket.getMotorConfigurationIDs();
-                                       tableModel.fireTableDataChanged();
-                                       if (sel >= ids.length)
-                                               sel--;
-                                       table.getSelectionModel().addSelectionInterval(sel, sel);
-                               }
-                       });
-                       panel.add(removeButton, "growx, wrap para");
-                       
-                       
-                       JButton close = new JButton("Close");
-                       close.addActionListener(new ActionListener() {
-                               @Override
-                               public void actionPerformed(ActionEvent e) {
-                                       EditConfigurationDialog.this.dispose();
-                               }
-                       });
-                       panel.add(close, "spanx, alignx 100%");
-                       
-                       this.getRootPane().setDefaultButton(close);
-                       
-                       
-                       this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
-                       GUIUtil.installEscapeCloseOperation(this);
-                       this.setLocationByPlatform(true);
-                       
-                       updateSelection();
-                       
-                       this.add(panel);
-                       this.validate();
-                       this.pack();
-               }
-               
-               private void fireChange() {
-                       int sel = table.getSelectedRow();
-                       tableModel.fireTableDataChanged();
-                       table.getSelectionModel().addSelectionInterval(sel, sel);
-               }
-               
-               private void updateSelection() {
-                       selection = table.getSelectedRow();
-                       if (selection < 0  ||  ids[selection] == null) {
-                               removeButton.setEnabled(false);
-                               nameField.setEnabled(false);
-                               nameField.setText("");
-                       } else {
-                               removeButton.setEnabled(true);
-                               nameField.setEnabled(true);
-                               nameField.setText(rocket.getMotorConfigurationName(ids[selection]));
-                       }
-               }
-       }
-       
 }
 
index 889d4adf61786de2cbcd3a931e5c92c55d0bb957..edda7dc4dd5db5f82e44665b3ebfd10e37ebdb03 100644 (file)
@@ -30,6 +30,7 @@ import net.sf.openrocket.gui.scalefigure.ScaleSelector;
 import net.sf.openrocket.material.Material;
 import net.sf.openrocket.rocketcomponent.FinSet;
 import net.sf.openrocket.rocketcomponent.FreeformFinSet;
+import net.sf.openrocket.rocketcomponent.IllegalFinPointException;
 import net.sf.openrocket.rocketcomponent.RocketComponent;
 import net.sf.openrocket.unit.UnitGroup;
 import net.sf.openrocket.util.Coordinate;
@@ -275,7 +276,7 @@ public class FreeformFinSetConfig extends FinSetConfig {
                                finset.addPoint(index);
                                try {
                                        finset.setPoint(index, point.x, point.y);
-                               } catch (IllegalArgumentException ignore) { }
+                               } catch (IllegalFinPointException ignore) { }
                                dragIndex = index;
                                
                                return;
@@ -299,7 +300,7 @@ public class FreeformFinSetConfig extends FinSetConfig {
                        
                        try {
                                finset.setPoint(dragIndex, point.x, point.y);
-                       } catch (IllegalArgumentException ignore) {
+                       } catch (IllegalFinPointException ignore) {
                                System.out.println("IAE:"+ignore);
                        }
                }
@@ -328,7 +329,7 @@ public class FreeformFinSetConfig extends FinSetConfig {
 
                        try {
                                finset.removePoint(index);
-                       } catch (IllegalArgumentException ignore) {
+                       } catch (IllegalFinPointException ignore) {
                        }
                }
                
@@ -462,6 +463,7 @@ public class FreeformFinSetConfig extends FinSetConfig {
                                finset.setPoint(rowIndex, c.x, c.y);
                        
                        } catch (NumberFormatException ignore) {
+                       } catch (IllegalFinPointException ignore) {
                        }
                }
                
index b1319b7b5fc90aad37b705b08cfe41e08eb7635a..6f568194268ba1653ea1f223d939bb8f5b94b322 100644 (file)
@@ -50,12 +50,12 @@ public class InnerTubeConfig extends ThicknessRingComponentConfig {
                tab = positionTab();
                tabbedPane.insertTab("Radial position", null, tab, "Radial position", 1);
                
-               tab = clusterTab();
-               tabbedPane.insertTab("Cluster", null, tab, "Cluster configuration", 2);
-               
                tab = new MotorConfig((MotorMount)c);
-               tabbedPane.insertTab("Motor", null, tab, "Motor mount configuration", 3);
+               tabbedPane.insertTab("Motor", null, tab, "Motor mount configuration", 2);
 
+               tab = clusterTab();
+               tabbedPane.insertTab("Cluster", null, tab, "Cluster configuration", 3);
+               
                tabbedPane.setSelectedIndex(0);
        }
        
diff --git a/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java b/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java
new file mode 100644 (file)
index 0000000..3c900f2
--- /dev/null
@@ -0,0 +1,587 @@
+package net.sf.openrocket.gui.dialogs;
+
+import static net.sf.openrocket.unit.Unit.NOUNIT2;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTable;
+import javax.swing.JToggleButton;
+import javax.swing.ListSelectionModel;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.table.TableCellRenderer;
+
+import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.aerodynamics.AerodynamicCalculator;
+import net.sf.openrocket.aerodynamics.AerodynamicForces;
+import net.sf.openrocket.aerodynamics.FlightConditions;
+import net.sf.openrocket.aerodynamics.Warning;
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.gui.adaptors.Column;
+import net.sf.openrocket.gui.adaptors.ColumnTableModel;
+import net.sf.openrocket.gui.adaptors.DoubleModel;
+import net.sf.openrocket.gui.adaptors.MotorConfigurationModel;
+import net.sf.openrocket.gui.components.BasicSlider;
+import net.sf.openrocket.gui.components.ResizeLabel;
+import net.sf.openrocket.gui.components.StageSelector;
+import net.sf.openrocket.gui.components.UnitSelector;
+import net.sf.openrocket.gui.scalefigure.RocketPanel;
+import net.sf.openrocket.rocketcomponent.Configuration;
+import net.sf.openrocket.rocketcomponent.FinSet;
+import net.sf.openrocket.rocketcomponent.Rocket;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.unit.Unit;
+import net.sf.openrocket.unit.UnitGroup;
+import net.sf.openrocket.util.GUIUtil;
+import net.sf.openrocket.util.MathUtil;
+import net.sf.openrocket.util.Prefs;
+
+public class ComponentAnalysisDialog extends JDialog implements ChangeListener {
+
+       private static ComponentAnalysisDialog singletonDialog = null;
+
+       
+       private final FlightConditions conditions;
+       private final Configuration configuration;
+       private final DoubleModel theta, aoa, mach, roll;
+       private final JToggleButton worstToggle;
+       private boolean fakeChange = false;
+       private AerodynamicCalculator calculator;
+       
+       private final ColumnTableModel cpTableModel;
+       private final ColumnTableModel dragTableModel;
+       private final ColumnTableModel rollTableModel;
+       
+       private final JList warningList;
+       
+       
+       private final List<AerodynamicForces> cpData = new ArrayList<AerodynamicForces>();
+       private final List<AerodynamicForces> dragData = new ArrayList<AerodynamicForces>();
+       private double totalCD = 0;
+       private final List<AerodynamicForces> rollData = new ArrayList<AerodynamicForces>();
+       
+       
+       public ComponentAnalysisDialog(final RocketPanel rocketPanel) {
+               super(SwingUtilities.getWindowAncestor(rocketPanel), "Component analysis");
+
+               JTable table;
+
+               JPanel panel = new JPanel(new MigLayout("fill","[][35lp::][fill][fill]"));
+               add(panel);
+               
+               this.configuration = rocketPanel.getConfiguration();
+               this.calculator = rocketPanel.getCalculator().newInstance();
+               this.calculator.setConfiguration(configuration);
+
+               
+               conditions = new FlightConditions(configuration);
+               
+               rocketPanel.setCPAOA(0);
+               aoa = new DoubleModel(rocketPanel, "CPAOA", UnitGroup.UNITS_ANGLE, 0, Math.PI);
+               rocketPanel.setCPMach(Prefs.getDefaultMach());
+               mach = new DoubleModel(rocketPanel, "CPMach", UnitGroup.UNITS_COEFFICIENT, 0);
+               rocketPanel.setCPTheta(rocketPanel.getFigure().getRotation());
+               theta = new DoubleModel(rocketPanel, "CPTheta", UnitGroup.UNITS_ANGLE, 0, 2*Math.PI);
+               rocketPanel.setCPRoll(0);
+               roll = new DoubleModel(rocketPanel, "CPRoll", UnitGroup.UNITS_ROLL);
+               
+               
+               panel.add(new JLabel("Wind direction:"),"width 100lp!");
+               panel.add(new UnitSelector(theta,true),"width 50lp!");
+               BasicSlider slider = new BasicSlider(theta.getSliderModel(0, 2*Math.PI));
+               panel.add(slider,"growx, split 2");
+               worstToggle = new JToggleButton("Worst");
+               worstToggle.setSelected(true);
+               worstToggle.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               stateChanged(null);
+                       }
+               });
+               slider.addChangeListener(new ChangeListener() {
+                       @Override
+                       public void stateChanged(ChangeEvent e) {
+                               if (!fakeChange)
+                                       worstToggle.setSelected(false);
+                       }
+               });
+               panel.add(worstToggle,"");
+               
+               
+               warningList = new JList();
+               JScrollPane scrollPane = new JScrollPane(warningList);
+               scrollPane.setBorder(BorderFactory.createTitledBorder("Warnings:"));
+               panel.add(scrollPane,"gap paragraph, spany 4, width 300lp!, growy 1, height :100lp:, wrap");
+               
+               
+               panel.add(new JLabel("Angle of attack:"),"width 100lp!");
+               panel.add(new UnitSelector(aoa,true),"width 50lp!");
+               panel.add(new BasicSlider(aoa.getSliderModel(0, Math.PI)),"growx, wrap");
+               
+               panel.add(new JLabel("Mach number:"),"width 100lp!");
+               panel.add(new UnitSelector(mach,true),"width 50lp!");
+               panel.add(new BasicSlider(mach.getSliderModel(0, 3)),"growx, wrap");
+               
+               panel.add(new JLabel("Roll rate:"), "width 100lp!");
+               panel.add(new UnitSelector(roll,true),"width 50lp!");
+               panel.add(new BasicSlider(roll.getSliderModel(-20*2*Math.PI, 20*2*Math.PI)),
+                               "growx, wrap paragraph");
+               
+               
+               // Stage and motor selection:
+               
+               panel.add(new JLabel("Active stages:"),"spanx, split, gapafter rel");
+               panel.add(new StageSelector(configuration),"gapafter paragraph");
+                               
+               JLabel label = new JLabel("Motor configuration:");
+               label.setHorizontalAlignment(JLabel.RIGHT);
+               panel.add(label,"growx, right");
+               panel.add(new JComboBox(new MotorConfigurationModel(configuration)),"wrap");
+
+               
+               
+               // Tabbed pane
+               
+               JTabbedPane tabbedPane = new JTabbedPane();
+               panel.add(tabbedPane, "spanx, growx, growy");
+               
+               
+               // Create the CP data table
+               cpTableModel = new ColumnTableModel(
+                               
+                               new Column("Component") {
+                                       @Override public Object getValueAt(int row) {
+                                               RocketComponent c = cpData.get(row).component;
+                                               if (c instanceof Rocket) {
+                                                       return "Total";
+                                               }
+                                               return c.toString();
+                                       }
+                                       @Override public int getDefaultWidth() {
+                                               return 200;
+                                       }
+                               },
+                               new Column("CG / " + UnitGroup.UNITS_LENGTH.getDefaultUnit().getUnit()) {
+                                       private Unit unit = UnitGroup.UNITS_LENGTH.getDefaultUnit();
+                                       @Override public Object getValueAt(int row) {
+                                               return unit.toString(cpData.get(row).cg.x);
+                                       }
+                               },
+                               new Column("Mass / " + UnitGroup.UNITS_MASS.getDefaultUnit().getUnit()) {
+                                       private Unit unit = UnitGroup.UNITS_MASS.getDefaultUnit();
+                                       @Override
+                                       public Object getValueAt(int row) {
+                                               return unit.toString(cpData.get(row).cg.weight);
+                                       }
+                               },
+                               new Column("CP / " + UnitGroup.UNITS_LENGTH.getDefaultUnit().getUnit()) {
+                                       private Unit unit = UnitGroup.UNITS_LENGTH.getDefaultUnit();
+                                       @Override public Object getValueAt(int row) {
+                                               return unit.toString(cpData.get(row).cp.x);
+                                       }
+                               },
+                               new Column("<html>C<sub>N<sub>\u03b1</sub></sub>") {
+                                       @Override public Object getValueAt(int row) {
+                                               return NOUNIT2.toString(cpData.get(row).cp.weight);
+                                       }
+                               }
+                               
+               ) {
+                       @Override public int getRowCount() {
+                               return cpData.size();
+                       }
+               };
+               
+               table = new JTable(cpTableModel);
+               table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+               table.setSelectionBackground(Color.LIGHT_GRAY);
+               table.setSelectionForeground(Color.BLACK);
+               cpTableModel.setColumnWidths(table.getColumnModel());
+               
+               table.setDefaultRenderer(Object.class, new CustomCellRenderer());
+//             table.setShowHorizontalLines(false);
+//             table.setShowVerticalLines(true);
+               
+               JScrollPane scrollpane = new JScrollPane(table);
+               scrollpane.setPreferredSize(new Dimension(600,200));
+               
+               tabbedPane.addTab("Stability", null, scrollpane, "Stability information");
+               
+               
+               
+               // Create the drag data table
+               dragTableModel = new ColumnTableModel(
+                               new Column("Component") {
+                                       @Override public Object getValueAt(int row) {
+                                               RocketComponent c = dragData.get(row).component;
+                                               if (c instanceof Rocket) {
+                                                       return "Total";
+                                               }
+                                               return c.toString();
+                                       }
+                                       @Override public int getDefaultWidth() {
+                                               return 200;
+                                       }
+                               },
+                               new Column("<html>Pressure C<sub>D</sub>") {
+                                       @Override public Object getValueAt(int row) {
+                                               return dragData.get(row).pressureCD;
+                                       }
+                               },
+                               new Column("<html>Base C<sub>D</sub>") {
+                                       @Override public Object getValueAt(int row) {
+                                               return dragData.get(row).baseCD;
+                                       }
+                               },
+                               new Column("<html>Friction C<sub>D</sub>") {
+                                       @Override public Object getValueAt(int row) {
+                                               return dragData.get(row).frictionCD;
+                                       }
+                               },
+                               new Column("<html>Total C<sub>D</sub>") {
+                                       @Override public Object getValueAt(int row) {
+                                               return dragData.get(row).CD;
+                                       }
+                               }
+               ) {
+                       @Override public int getRowCount() {
+                               return dragData.size();
+                       }                       
+               };
+               
+
+               table = new JTable(dragTableModel);
+               table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+               table.setSelectionBackground(Color.LIGHT_GRAY);
+               table.setSelectionForeground(Color.BLACK);
+               dragTableModel.setColumnWidths(table.getColumnModel());
+               
+               table.setDefaultRenderer(Object.class, new DragCellRenderer(new Color(0.5f,1.0f,0.5f)));
+//             table.setShowHorizontalLines(false);
+//             table.setShowVerticalLines(true);
+               
+               scrollpane = new JScrollPane(table);
+               scrollpane.setPreferredSize(new Dimension(600,200));
+               
+               tabbedPane.addTab("Drag characteristics", null, scrollpane, "Drag characteristics");
+               
+               
+               
+               
+               // Create the roll data table
+               rollTableModel = new ColumnTableModel(
+                               new Column("Component") {
+                                       @Override public Object getValueAt(int row) {
+                                               RocketComponent c = rollData.get(row).component;
+                                               if (c instanceof Rocket) {
+                                                       return "Total";
+                                               }
+                                               return c.toString();
+                                       }
+                               },
+                               new Column("Roll forcing coefficient") {
+                                       @Override public Object getValueAt(int row) {
+                                               return rollData.get(row).CrollForce;
+                                       }
+                               },
+                               new Column("Roll damping coefficient") {
+                                       @Override public Object getValueAt(int row) {
+                                               return rollData.get(row).CrollDamp;
+                                       }
+                               },
+                               new Column("<html>Total C<sub>l</sub>") {
+                                       @Override public Object getValueAt(int row) {
+                                               return rollData.get(row).Croll;
+                                       }
+                               }
+               ) {
+                       @Override public int getRowCount() {
+                               return rollData.size();
+                       }                       
+               };
+               
+
+               table = new JTable(rollTableModel);
+               table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+               table.setSelectionBackground(Color.LIGHT_GRAY);
+               table.setSelectionForeground(Color.BLACK);
+               rollTableModel.setColumnWidths(table.getColumnModel());
+               
+               scrollpane = new JScrollPane(table);
+               scrollpane.setPreferredSize(new Dimension(600,200));
+               
+               tabbedPane.addTab("Roll dynamics", null, scrollpane, "Roll dynamics");
+               
+               
+               
+               
+               
+               
+               // Add the data updater to listen to changes in aoa and theta
+               mach.addChangeListener(this);
+               theta.addChangeListener(this);
+               aoa.addChangeListener(this);
+               roll.addChangeListener(this);
+               configuration.addChangeListener(this);
+               this.stateChanged(null);
+               
+               
+               
+               // Remove listeners when closing window
+               this.addWindowListener(new WindowAdapter() {
+                       @Override
+                       public void windowClosed(WindowEvent e) {
+                               System.out.println("Closing method called: "+this);
+                               theta.removeChangeListener(ComponentAnalysisDialog.this);
+                               aoa.removeChangeListener(ComponentAnalysisDialog.this);
+                               mach.removeChangeListener(ComponentAnalysisDialog.this);
+                               roll.removeChangeListener(ComponentAnalysisDialog.this);
+                               configuration.removeChangeListener(ComponentAnalysisDialog.this);
+                               System.out.println("SETTING NAN VALUES");
+                               rocketPanel.setCPAOA(Double.NaN);
+                               rocketPanel.setCPTheta(Double.NaN);
+                               rocketPanel.setCPMach(Double.NaN);
+                               rocketPanel.setCPRoll(Double.NaN);
+                               singletonDialog = null;
+                       }
+               });
+               
+
+               panel.add(new ResizeLabel("Reference length: ", -1), 
+                               "span, split, gapleft para, gapright rel");
+               DoubleModel dm = new DoubleModel(conditions, "RefLength", UnitGroup.UNITS_LENGTH);
+               UnitSelector sel = new UnitSelector(dm, true);
+               sel.resizeFont(-1);
+               panel.add(sel, "gapright para");
+               
+               panel.add(new ResizeLabel("Reference area: ", -1), "gapright rel");
+               dm = new DoubleModel(conditions, "RefArea", UnitGroup.UNITS_AREA);
+               sel = new UnitSelector(dm, true);
+               sel.resizeFont(-1);
+               panel.add(sel, "wrap");
+               
+               
+
+               // Buttons
+               JButton button;
+               
+               // TODO: LOW: printing
+//             button = new JButton("Print");
+//             button.addActionListener(new ActionListener() {
+//                     public void actionPerformed(ActionEvent e) {
+//                             try {
+//                                     table.print();
+//                             } catch (PrinterException e1) {
+//                                     JOptionPane.showMessageDialog(ComponentAnalysisDialog.this, 
+//                                                     "An error occurred while printing.", "Print error",
+//                                                     JOptionPane.ERROR_MESSAGE);
+//                             }
+//                     }
+//             });
+//             panel.add(button,"tag ok");
+               
+               button = new JButton("Close");
+               button.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               ComponentAnalysisDialog.this.dispose();
+                       }
+               });
+               panel.add(button,"span, split, tag cancel");
+               
+
+               setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+               GUIUtil.installEscapeCloseOperation(this);
+               pack();
+       }
+       
+       
+       
+       /**
+        * Updates the data in the table and fires a table data change event.
+        */
+       @Override
+       public void stateChanged(ChangeEvent e) {
+               AerodynamicForces forces;
+               WarningSet set = new WarningSet();
+               conditions.setAOA(aoa.getValue());
+               conditions.setTheta(theta.getValue());
+               conditions.setMach(mach.getValue());
+               conditions.setRollRate(roll.getValue());
+               conditions.setReference(configuration);
+               
+               if (worstToggle.isSelected()) {
+                       calculator.getWorstCP(conditions, null);
+                       if (!MathUtil.equals(conditions.getTheta(), theta.getValue())) {
+                               fakeChange = true;
+                               theta.setValue(conditions.getTheta());  // Fires a stateChanged event
+                               fakeChange = false;
+                               return;
+                       }
+               }
+               
+               Map<RocketComponent, AerodynamicForces> data = calculator.getForceAnalysis(conditions, set);
+               
+               cpData.clear();
+               dragData.clear();
+               rollData.clear();
+               for (RocketComponent c: configuration) {
+                       forces = data.get(c);
+                       if (forces == null)
+                               continue;
+                       if (forces.cp != null) {
+                               cpData.add(forces);
+                       }
+                       if (!Double.isNaN(forces.CD)) {
+                               dragData.add(forces);
+                       }
+                       if (c instanceof FinSet) {
+                               rollData.add(forces);
+                       }
+               }
+               forces = data.get(configuration.getRocket());
+               if (forces != null) {
+                       cpData.add(forces);
+                       dragData.add(forces);
+                       rollData.add(forces);
+                       totalCD = forces.CD;
+               } else {
+                       totalCD = 0;
+               }
+               
+               // Set warnings
+               if (set.isEmpty()) {
+                       warningList.setListData(new String[] {
+                                       "<html><i><font color=\"gray\">No warnings.</font></i>"
+                       });
+               } else {
+                       warningList.setListData(new Vector<Warning>(set));
+               }
+               
+               cpTableModel.fireTableDataChanged();
+               dragTableModel.fireTableDataChanged();
+               rollTableModel.fireTableDataChanged();
+       }
+       
+       
+       private class CustomCellRenderer extends JLabel implements TableCellRenderer {
+               private final Font normalFont;
+               private final Font boldFont;
+               
+               public CustomCellRenderer() {
+                       super();
+                       normalFont = getFont();
+                       boldFont = normalFont.deriveFont(Font.BOLD);
+               }
+               @Override
+               public Component getTableCellRendererComponent(JTable table, Object value, 
+                               boolean isSelected, boolean hasFocus, int row, int column) {
+                       
+                       this.setText(value.toString());
+                       
+                       if ((row < 0) || (row >= cpData.size()))
+                                       return this;
+                       
+                       if (cpData.get(row).component instanceof Rocket) {
+                               this.setFont(boldFont);
+                       } else {
+                               this.setFont(normalFont);
+                       }
+                       return this;
+               }
+       }
+       
+
+       
+       private class DragCellRenderer extends JLabel implements TableCellRenderer {
+               private final Font normalFont;
+               private final Font boldFont;
+               
+               private final float[] start = { 0.3333f, 0.2f, 1.0f };
+               private final float[] end = { 0.0f, 0.8f, 1.0f };
+               
+               
+               public DragCellRenderer(Color baseColor) {
+                       super();
+                       normalFont = getFont();
+                       boldFont = normalFont.deriveFont(Font.BOLD);
+               }
+               @Override
+               public Component getTableCellRendererComponent(JTable table, Object value, 
+                               boolean isSelected, boolean hasFocus, int row, int column) {
+                       
+                       if (value instanceof Double) {
+                               
+                               // A drag coefficient
+                               double cd = (Double)value;
+                               this.setText(String.format("%.2f (%.0f%%)", cd, 100*cd/totalCD));
+
+                               float r = (float)(cd/1.5);
+                               
+                               float hue = MathUtil.clamp(0.3333f * (1-2.0f*r), 0, 0.3333f);
+                               float sat = MathUtil.clamp(0.8f*r + 0.1f*(1-r), 0, 1);
+                               float val = 1.0f;
+                               
+                               this.setBackground(Color.getHSBColor(hue, sat, val));
+                               this.setOpaque(true);
+                               this.setHorizontalAlignment(SwingConstants.CENTER);
+                               
+                       } else {
+                               
+                               // Other
+                               this.setText(value.toString());
+                               this.setOpaque(false);
+                               this.setHorizontalAlignment(SwingConstants.LEFT);
+                               
+                       }
+                       
+                       if ((row < 0) || (row >= dragData.size()))
+                                       return this;
+                       
+                       if ((dragData.get(row).component instanceof Rocket) || (column == 4)){
+                               this.setFont(boldFont);
+                       } else {
+                               this.setFont(normalFont);
+                       }
+                       return this;
+               }
+       }
+       
+       
+       /////////  Singleton implementation
+       
+       public static void showDialog(RocketPanel rocketpanel) {
+               if (singletonDialog != null)
+                       singletonDialog.dispose();
+               singletonDialog = new ComponentAnalysisDialog(rocketpanel);
+               singletonDialog.setVisible(true);
+       }
+       
+       public static void hideDialog() {
+               if (singletonDialog != null)
+                       singletonDialog.dispose();
+       }
+       
+}
diff --git a/src/net/sf/openrocket/gui/dialogs/DetailDialog.java b/src/net/sf/openrocket/gui/dialogs/DetailDialog.java
new file mode 100644 (file)
index 0000000..05a1995
--- /dev/null
@@ -0,0 +1,18 @@
+package net.sf.openrocket.gui.dialogs;
+
+import java.awt.Component;
+
+import javax.swing.JOptionPane;
+
+public class DetailDialog {
+
+       public static void showDetailedMessageDialog(Component parentComponent, Object message, 
+                       String details, String title, int messageType)  {
+               
+               // TODO: HIGH: Detailed dialog
+               JOptionPane.showMessageDialog(parentComponent, message, title, messageType, null);
+               
+       }
+       
+       
+}
diff --git a/src/net/sf/openrocket/gui/dialogs/EditMotorConfigurationDialog.java b/src/net/sf/openrocket/gui/dialogs/EditMotorConfigurationDialog.java
new file mode 100644 (file)
index 0000000..ffa3a09
--- /dev/null
@@ -0,0 +1,479 @@
+package net.sf.openrocket.gui.dialogs;
+
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.TableColumn;
+import javax.swing.table.TableColumnModel;
+
+import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.document.OpenRocketDocument;
+import net.sf.openrocket.gui.main.BasicFrame;
+import net.sf.openrocket.gui.main.MotorChooserDialog;
+import net.sf.openrocket.rocketcomponent.Motor;
+import net.sf.openrocket.rocketcomponent.MotorMount;
+import net.sf.openrocket.rocketcomponent.Rocket;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.util.GUIUtil;
+
+public class EditMotorConfigurationDialog extends JDialog {
+       
+       private final Rocket rocket;
+       
+       private final MotorMount[] mounts;
+
+       private final JTable configurationTable;
+       private final MotorConfigurationTableModel configurationTableModel;
+
+       
+       private final JButton newConfButton, removeConfButton;
+       private final JButton selectMotorButton, removeMotorButton;
+       private final JTextField configurationNameField;
+       
+       
+       private String currentID = null;
+       private MotorMount currentMount = null;
+       
+       public EditMotorConfigurationDialog(final Rocket rocket, Window parent) {
+               super(parent, "Edit motor configurations");
+               
+               if (parent != null)
+                       this.setModalityType(ModalityType.DOCUMENT_MODAL);
+               else
+                       this.setModalityType(ModalityType.APPLICATION_MODAL);
+               
+               this.rocket = rocket;
+               
+               ArrayList<MotorMount> mountList = new ArrayList<MotorMount>();
+               Iterator<RocketComponent> iterator = rocket.deepIterator();
+               while (iterator.hasNext()) {
+                       RocketComponent c = iterator.next();
+                       if (c instanceof MotorMount) {
+                               mountList.add((MotorMount)c);
+                       }
+               }
+               mounts = mountList.toArray(new MotorMount[0]);
+               
+               
+               
+               JPanel panel = new JPanel(new MigLayout("fill, flowy"));
+               
+               
+               ////  Motor mount selection
+               
+               JLabel label = new JLabel("<html><b>Motor mounts:</b>");
+               panel.add(label, "gapbottom para");
+               
+               label = new JLabel("<html>Select which components function as motor mounts:");
+               panel.add(label,"ay 100%, w 1px, growx");
+               
+               
+               JTable table = new JTable(new MotorMountTableModel());
+               table.setTableHeader(null);
+               table.setShowVerticalLines(false);
+               table.setRowSelectionAllowed(false);
+               table.setColumnSelectionAllowed(false);
+               
+               TableColumnModel columnModel = table.getColumnModel();
+               TableColumn col0 = columnModel.getColumn(0);
+               int w = table.getRowHeight() + 2;
+               col0.setMinWidth(w);
+               col0.setPreferredWidth(w);
+               col0.setMaxWidth(w);
+               
+               JScrollPane scroll = new JScrollPane(table);
+               panel.add(scroll, "w 200lp, h 150lp, grow, wrap 20lp");
+               
+
+               
+               
+               
+               //// Motor selection
+               
+               label = new JLabel("<html><b>Motor configurations:</b>");
+               panel.add(label, "spanx, gapbottom para");
+               
+               
+               label = new JLabel("Configuration name:");
+               String tip = "Leave name empty for default.";
+               label.setToolTipText(tip);
+               panel.add(label, "");
+               
+               configurationNameField = new JTextField(10);
+               configurationNameField.setToolTipText(tip);
+               configurationNameField.getDocument().addDocumentListener(new DocumentListener() {
+                       @Override
+                       public void changedUpdate(DocumentEvent e) {
+                               update();
+                       }
+                       @Override
+                       public void insertUpdate(DocumentEvent e) {
+                               update();
+                       }
+                       @Override
+                       public void removeUpdate(DocumentEvent e) {
+                               update();
+                       }
+                       private void update() {
+                               String text = configurationNameField.getText();
+                               if (currentID != null) {
+                                       rocket.setMotorConfigurationName(currentID, text);
+                                       int row = configurationTable.getSelectedRow();
+                                       configurationTableModel.fireTableCellUpdated(row, 0);
+                                       updateEnabled();
+                               }
+                       }
+               });
+               panel.add(configurationNameField, "cell 2 1, gapright para");
+               
+               newConfButton = new JButton("New configuration");
+               newConfButton.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               String id = rocket.newMotorConfigurationID();
+                               rocket.getDefaultConfiguration().setMotorConfigurationID(id);
+                               configurationTableModel.fireTableDataChanged();
+                               updateEnabled();
+                       }
+               });
+               panel.add(newConfButton, "cell 3 1");
+               
+               removeConfButton = new JButton("Remove configuration");
+               removeConfButton.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               if (currentID == null)
+                                       return;
+                               rocket.removeMotorConfigurationID(currentID);
+                               rocket.getDefaultConfiguration().setMotorConfigurationID(null);
+                               configurationTableModel.fireTableDataChanged();
+                               updateEnabled();
+                       }
+               });
+               panel.add(removeConfButton, "cell 4 1");
+               
+               
+
+               
+               configurationTableModel = new MotorConfigurationTableModel();
+               configurationTable = new JTable(configurationTableModel);
+               configurationTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+               configurationTable.setCellSelectionEnabled(true);
+               
+               configurationTable.addMouseListener(new MouseAdapter() {
+                       @Override
+                       public void mouseClicked(MouseEvent e) {
+                               
+                               if (e.getClickCount() == 1) {
+                                       
+                                       // Single click updates selection
+                                       updateEnabled();
+                                       
+                               } else if (e.getClickCount() == 2) {
+                                       
+                                       // Double-click edits motor
+                                       selectMotor();
+                                       
+                               }
+                               
+                       }
+               });
+               
+
+               scroll = new JScrollPane(configurationTable);
+               panel.add(scroll, "cell 1 2, spanx, w 500lp, h 150lp, grow");
+               
+               
+               selectMotorButton = new JButton("Select motor");
+               selectMotorButton.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               selectMotor();
+                       }
+               });
+               panel.add(selectMotorButton, "spanx, flowx, split 2, ax 50%");
+               
+               
+               removeMotorButton = new JButton("Remove motor");
+               removeMotorButton.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               removeMotor();
+                       }
+               });
+               panel.add(removeMotorButton, "ax 50%");
+               
+               
+               
+               //// Close button
+               
+               JButton close = new JButton("Close");
+               close.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               EditMotorConfigurationDialog.this.dispose();
+                       }
+               });
+               panel.add(close, "spanx, right");
+               
+               this.add(panel);
+               this.validate();
+               this.pack();
+               
+               updateEnabled();
+               
+               GUIUtil.installEscapeCloseOperation(this);
+               GUIUtil.setDefaultButton(close);
+               
+               // Undo description
+               final OpenRocketDocument document = BasicFrame.findDocument(rocket);
+               if (document != null) {
+                       document.startUndo("Edit motor configurations");
+                       this.addWindowListener(new WindowAdapter() {
+                               @Override
+                               public void windowClosed(WindowEvent e) {
+                                       document.stopUndo();
+                               }
+                       });
+               }
+       }
+
+       
+       
+       
+       private void updateEnabled() {
+               int column = configurationTable.getSelectedColumn();
+               int row = configurationTable.getSelectedRow();
+               
+               if (column < 0 || row < 0) {
+                       currentID = null;
+                       currentMount = null;
+               } else {
+                       
+                       currentID = findID(row);
+                       if (column == 0) {
+                               currentMount = null;
+                       } else {
+                               currentMount = findMount(column);
+                       }
+                       rocket.getDefaultConfiguration().setMotorConfigurationID(currentID);
+                       
+               }
+
+               configurationNameField.setEnabled(currentID != null);
+               if (currentID == null) {
+                       configurationNameField.setText("");
+               } else {
+                       configurationNameField.setText(rocket.getMotorConfigurationName(currentID));
+               }
+               removeConfButton.setEnabled(currentID != null);
+               selectMotorButton.setEnabled(currentMount != null && currentID != null);
+               removeMotorButton.setEnabled(currentMount != null && currentID != null);
+       }
+       
+
+       
+       
+       private void selectMotor() {
+               if (currentID == null || currentMount == null)
+                       return;
+               
+               MotorChooserDialog dialog = new MotorChooserDialog(currentMount.getMotor(currentID),
+                               currentMount.getMotorDelay(currentID), currentMount.getMotorMountDiameter());
+               dialog.setVisible(true);
+               Motor m = dialog.getSelectedMotor();
+               double d = dialog.getSelectedDelay();
+               
+               if (m != null) {
+                       currentMount.setMotor(currentID, m);
+                       currentMount.setMotorDelay(currentID, d);
+               }
+
+               int row = configurationTable.getSelectedRow();
+               configurationTableModel.fireTableRowsUpdated(row, row);
+               updateEnabled();
+       }
+       
+       
+       private void removeMotor() {
+               if (currentID == null || currentMount == null)
+                       return;
+               
+               currentMount.setMotor(currentID, null);
+               
+               int row = configurationTable.getSelectedRow();
+               configurationTableModel.fireTableRowsUpdated(row, row);
+               updateEnabled();
+       }
+       
+       
+       private String findID(int row) {
+               return rocket.getMotorConfigurationIDs()[row+1];
+       }
+
+       
+       private MotorMount findMount(int column) {
+               MotorMount mount = null;
+
+               int count = column;
+               for (MotorMount m: mounts) {
+                       if (m.isMotorMount())
+                               count--;
+                       if (count <= 0) {
+                               mount = m;
+                               break;
+                       }
+               }
+               
+               if (mount == null) {
+                       throw new IndexOutOfBoundsException("motor mount not found, column="+column);
+               }
+               return mount;
+       }
+
+       
+       /**
+        * The table model for selecting whether components are motor mounts or not.
+        */
+       private class MotorMountTableModel extends AbstractTableModel {
+
+               @Override
+               public int getColumnCount() {
+                       return 2;
+               }
+
+               @Override
+               public int getRowCount() {
+                       return mounts.length;
+               }
+               
+               @Override
+               public Class<?> getColumnClass(int column) {
+                       switch (column) {
+                       case 0:
+                               return Boolean.class;
+                               
+                       case 1:
+                               return String.class;
+                               
+                       default:
+                               throw new IndexOutOfBoundsException("column="+column);
+                       }
+               }
+
+               @Override
+               public Object getValueAt(int row, int column) {
+                       switch (column) {
+                       case 0:
+                               return new Boolean(mounts[row].isMotorMount());
+                               
+                       case 1:
+                               return mounts[row].toString();
+                               
+                       default:
+                               throw new IndexOutOfBoundsException("column="+column);
+                       }
+               }
+               
+               @Override
+               public boolean isCellEditable(int row, int column) {
+                       return column == 0;
+               }
+               
+               @Override
+               public void setValueAt(Object value, int row, int column) {
+                       if (column != 0 || !(value instanceof Boolean)) {
+                               throw new IllegalArgumentException("column="+column+", value="+value);
+                       }
+                       
+                       mounts[row].setMotorMount((Boolean)value);
+                       configurationTableModel.fireTableStructureChanged();
+                       updateEnabled();
+               }
+       }
+       
+       
+       
+       /**
+        * The table model for selecting and editing the motor configurations.
+        */
+       private class MotorConfigurationTableModel extends AbstractTableModel {
+               
+               @Override
+               public int getColumnCount() {
+                       int count = 1;
+                       for (MotorMount m: mounts) {
+                               if (m.isMotorMount())
+                                       count++;
+                       }
+                       return count;
+               }
+
+               @Override
+               public int getRowCount() {
+                       return rocket.getMotorConfigurationIDs().length-1;
+               }
+
+               @Override
+               public Object getValueAt(int row, int column) {
+                       
+                       String id = findID(row);
+
+                       if (column == 0) {
+                               return rocket.getMotorConfigurationNameOrDescription(id);
+                       }
+                       
+                       MotorMount mount = findMount(column);
+                       Motor motor = mount.getMotor(id);
+                       if (motor == null)
+                               return "None";
+                       
+                       String str = motor.getDesignation(mount.getMotorDelay(id)); 
+                       int count = mount.getMotorCount();
+                       if (count > 1) {
+                               str = "" + count + "\u00d7 " + str;
+                       }
+                       return str;
+               }
+
+
+               @Override
+               public String getColumnName(int column) {
+                       if (column == 0) {
+                               return "Configuration name";
+                       }
+                       
+                       MotorMount mount = findMount(column);
+                       String name = mount.toString();
+                       int count = mount.getMotorCount();
+                       if (count > 1) {
+                               name = name + " (\u00d7" + count + ")";
+                       }
+                       return name;
+               }
+               
+               
+       }
+
+       
+       
+       
+}
diff --git a/src/net/sf/openrocket/gui/dialogs/LicenseDialog.java b/src/net/sf/openrocket/gui/dialogs/LicenseDialog.java
new file mode 100644 (file)
index 0000000..bc718d6
--- /dev/null
@@ -0,0 +1,79 @@
+package net.sf.openrocket.gui.dialogs;
+
+import java.awt.Font;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+
+import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.gui.components.ResizeLabel;
+import net.sf.openrocket.util.GUIUtil;
+
+public class LicenseDialog extends JDialog {
+       private static final String LICENSE_FILENAME = "LICENSE.TXT";
+       
+       private static final String DEFAULT_LICENSE_TEXT =
+               "\n" +
+               "Error:  Unable to load " + LICENSE_FILENAME + "!\n" +
+               "\n" +
+               "OpenRocket is licensed under the GNU GPL version 3, with additional permissions.\n" +
+               "See http://openrocket.sourceforge.net/ for details.";
+
+       public LicenseDialog(JFrame parent) {
+               super(parent, true);
+               
+               JPanel panel = new JPanel(new MigLayout("fill"));
+               
+               panel.add(new ResizeLabel("OpenRocket license", 10), "ax 50%, wrap para");
+
+               String licenseText;
+               try {
+                       
+                       BufferedReader reader = new BufferedReader(
+                                       new InputStreamReader(ClassLoader.getSystemResourceAsStream(LICENSE_FILENAME)));
+                       StringBuffer sb = new StringBuffer();
+                       for (String s = reader.readLine(); s != null; s = reader.readLine()) {
+                               sb.append(s);
+                               sb.append('\n');
+                       }
+                       licenseText = sb.toString();
+                       
+               } catch (Exception e) {
+
+                       licenseText = DEFAULT_LICENSE_TEXT;
+                       
+               }
+               
+               JTextArea text = new JTextArea(licenseText);
+               text.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
+               text.setRows(20);
+               text.setColumns(80);
+               text.setEditable(false);
+               panel.add(new JScrollPane(text),"grow, wrap para");
+               
+               JButton close = new JButton("Close");
+               close.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               LicenseDialog.this.dispose();
+                       }
+               });
+               panel.add(close, "right");
+               
+               this.add(panel);
+               this.setTitle("OpenRocket license");
+               this.pack();
+               this.setLocationByPlatform(true);
+               GUIUtil.setDefaultButton(close);
+               GUIUtil.installEscapeCloseOperation(this);
+       }
+       
+}
diff --git a/src/net/sf/openrocket/gui/dialogs/PreferencesDialog.java b/src/net/sf/openrocket/gui/dialogs/PreferencesDialog.java
new file mode 100644 (file)
index 0000000..cfb6f88
--- /dev/null
@@ -0,0 +1,390 @@
+package net.sf.openrocket.gui.dialogs;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.AbstractListModel;
+import javax.swing.ComboBoxModel;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTabbedPane;
+
+import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.gui.components.ResizeLabel;
+import net.sf.openrocket.unit.Unit;
+import net.sf.openrocket.unit.UnitGroup;
+import net.sf.openrocket.util.GUIUtil;
+import net.sf.openrocket.util.Prefs;
+
+public class PreferencesDialog extends JDialog {
+       
+       private final List<DefaultUnitSelector> unitSelectors = new ArrayList<DefaultUnitSelector>();
+
+       private PreferencesDialog() {
+               super((JFrame)null, "Preferences", true);
+               
+               JPanel panel = new JPanel(new MigLayout("fill, gap unrel","[grow]","[grow][]"));
+                               
+               JTabbedPane tabbedPane = new JTabbedPane();
+               panel.add(tabbedPane,"grow, wrap");
+               
+
+               tabbedPane.addTab("Units", null, unitsPane(), "Default units");
+               tabbedPane.addTab("Confirmation", null, confirmationPane(), "Confirmation dialog settings");
+               
+               
+               
+               JButton close = new JButton("Close");
+               close.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent arg0) {
+                               PreferencesDialog.this.setVisible(false);
+                               PreferencesDialog.this.dispose();
+                       }
+               });
+               panel.add(close,"span, right, tag close");
+               
+               this.setContentPane(panel);
+               pack();
+               setAlwaysOnTop(true);
+               this.setLocationRelativeTo(null);
+               
+               this.addWindowListener(new WindowAdapter() {
+                       @Override
+                       public void windowClosed(WindowEvent e) {
+                               Prefs.storeDefaultUnits();
+                       }
+               });
+
+               GUIUtil.setDefaultButton(close);
+               GUIUtil.installEscapeCloseOperation(this);
+       }
+       
+       
+       private JPanel confirmationPane() {
+               JPanel panel = new JPanel(new MigLayout("fill"));
+               
+               panel.add(new JLabel("Position to insert new body components:"));
+               panel.add(new JComboBox(new PrefChoiseSelector(Prefs.BODY_COMPONENT_INSERT_POSITION_KEY,
+                               "Always ask", "Insert in middle", "Add to end")), "wrap para, sg combos");
+               
+               panel.add(new JLabel("Confirm deletion of simulations:"));
+               panel.add(new JComboBox(new PrefBooleanSelector(Prefs.CONFIRM_DELETE_SIMULATION,
+                               "Delete", "Confirm", true)), "wrap para, sg combos");
+               
+               return panel;
+       }
+       
+       private JPanel unitsPane() {
+               JPanel panel = new JPanel(new MigLayout("", "[][]40lp[][]"));
+               JComboBox combo;
+               
+               panel.add(new JLabel("Select your preferred units:"), "span, wrap paragraph");
+               
+/*
+               public static final UnitGroup UNITS_LENGTH;
+               public static final UnitGroup UNITS_MOTOR_DIMENSIONS;
+               public static final UnitGroup UNITS_DISTANCE;
+               
+               public static final UnitGroup UNITS_VELOCITY;
+               public static final UnitGroup UNITS_ACCELERATION;
+               public static final UnitGroup UNITS_MASS;
+               public static final UnitGroup UNITS_FORCE;
+               public static final UnitGroup UNITS_IMPULSE;
+
+               public static final UnitGroup UNITS_STABILITY;
+               public static final UnitGroup UNITS_FLIGHT_TIME;
+               public static final UnitGroup UNITS_ROLL;
+               
+               public static final UnitGroup UNITS_AREA;
+               public static final UnitGroup UNITS_DENSITY_LINE;
+               public static final UnitGroup UNITS_DENSITY_SURFACE;
+               public static final UnitGroup UNITS_DENSITY_BULK;
+               public static final UnitGroup UNITS_ROUGHNESS;
+               
+               public static final UnitGroup UNITS_TEMPERATURE;
+               public static final UnitGroup UNITS_PRESSURE;
+               public static final UnitGroup UNITS_ANGLE;
+*/
+               
+               panel.add(new JLabel("Rocket dimensions:"));
+               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_LENGTH));
+               panel.add(combo, "sizegroup boxes");
+               
+               panel.add(new JLabel("Line density:"));
+               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_LINE));
+               panel.add(combo, "sizegroup boxes, wrap");
+               
+               
+               
+               panel.add(new JLabel("Motor dimensions:"));
+               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_MOTOR_DIMENSIONS));
+               panel.add(combo, "sizegroup boxes");
+               
+               panel.add(new JLabel("Surface density:"));
+               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_SURFACE));
+               panel.add(combo, "sizegroup boxes, wrap");
+               
+
+               
+               panel.add(new JLabel("Distance:"));
+               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DISTANCE));
+               panel.add(combo, "sizegroup boxes");
+               
+               panel.add(new JLabel("Bulk density::"));
+               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_BULK));
+               panel.add(combo, "sizegroup boxes, wrap");
+               
+
+               
+               panel.add(new JLabel("Velocity:"));
+               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_VELOCITY));
+               panel.add(combo, "sizegroup boxes");
+
+               panel.add(new JLabel("Surface roughness:"));
+               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ROUGHNESS));
+               panel.add(combo, "sizegroup boxes, wrap");
+               
+               
+               
+               panel.add(new JLabel("Acceleration:"));
+               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ACCELERATION));
+               panel.add(combo, "sizegroup boxes");
+
+               panel.add(new JLabel("Area:"));
+               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_AREA));
+               panel.add(combo, "sizegroup boxes, wrap");
+               
+               
+
+               panel.add(new JLabel("Mass:"));
+               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_MASS));
+               panel.add(combo, "sizegroup boxes");
+               
+               panel.add(new JLabel("Angle:"));
+               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ANGLE));
+               panel.add(combo, "sizegroup boxes, wrap");
+               
+
+               
+               panel.add(new JLabel("Force:"));
+               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_FORCE));
+               panel.add(combo, "sizegroup boxes");
+               
+               panel.add(new JLabel("Roll rate:"));
+               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ROLL));
+               panel.add(combo, "sizegroup boxes, wrap");
+               
+
+               
+               panel.add(new JLabel("Total impulse:"));
+               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_IMPULSE));
+               panel.add(combo, "sizegroup boxes");
+               
+               panel.add(new JLabel("Temperature:"));
+               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_TEMPERATURE));
+               panel.add(combo, "sizegroup boxes, wrap");
+               
+
+               
+               panel.add(new JLabel("Stability:"));
+               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_STABILITY));
+               panel.add(combo, "sizegroup boxes");
+
+               panel.add(new JLabel("Pressure:"));
+               combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_PRESSURE));
+               panel.add(combo, "sizegroup boxes, wrap para");
+               
+               
+               
+               JButton button = new JButton("Default metric");
+               button.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               UnitGroup.setDefaultMetricUnits();
+                               for (DefaultUnitSelector s: unitSelectors)
+                                       s.fireChange();
+                       }
+               });
+               panel.add(button, "spanx, split 2, grow");
+               
+               button = new JButton("Default imperial");
+               button.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               UnitGroup.setDefaultImperialUnits();
+                               for (DefaultUnitSelector s: unitSelectors)
+                                       s.fireChange();
+                       }
+               });
+               panel.add(button, "grow, wrap para");
+               
+               
+               panel.add(new ResizeLabel("The effects will take place the next time you open a window.",-2),
+                               "spanx, wrap");
+               
+
+               return panel;
+       }
+       
+       
+       
+       
+       private class DefaultUnitSelector extends AbstractListModel implements ComboBoxModel {
+               
+               private final UnitGroup group;
+               public DefaultUnitSelector(UnitGroup group) {
+                       this.group = group;
+                       unitSelectors.add(this);
+               }
+               
+               @Override
+               public Object getSelectedItem() {
+                       return group.getDefaultUnit();
+               }
+               @Override
+               public void setSelectedItem(Object item) {
+                       if (!(item instanceof Unit)) {
+                               throw new IllegalArgumentException("Illegal argument "+item);
+                       }
+                       group.setDefaultUnit(group.getUnitIndex((Unit)item));
+               }
+               @Override
+               public Object getElementAt(int index) {
+                       return group.getUnit(index);
+               }
+               @Override
+               public int getSize() {
+                       return group.getUnitCount();
+               }
+               
+               
+               public void fireChange() {
+                       this.fireContentsChanged(this, 0, this.getSize());
+               }
+       }
+       
+
+       
+       private class PrefChoiseSelector extends AbstractListModel implements ComboBoxModel {
+               private final String preference;
+               private final String[] descriptions;
+               
+               public PrefChoiseSelector(String preference, String ... descriptions) {
+                       this.preference = preference;
+                       this.descriptions = descriptions;
+               }
+               
+               @Override
+               public Object getSelectedItem() {
+                       return descriptions[Prefs.getChoise(preference, descriptions.length, 0)];
+               }
+               
+               @Override
+               public void setSelectedItem(Object item) {
+                       if (!(item instanceof String)) {
+                               throw new IllegalArgumentException("Illegal argument "+item);
+                       }
+                       int index;
+                       for (index = 0; index < descriptions.length; index++) {
+                               if (((String)item).equalsIgnoreCase(descriptions[index]))
+                                       break;
+                       }
+                       if (index >= descriptions.length) {
+                               throw new IllegalArgumentException("Illegal argument "+item);
+                       }
+                       
+                       Prefs.putChoise(preference, index);
+               }
+               
+               @Override
+               public Object getElementAt(int index) {
+                       return descriptions[index];
+               }
+               @Override
+               public int getSize() {
+                       return descriptions.length;
+               }
+       }
+       
+
+       private class PrefBooleanSelector extends AbstractListModel implements ComboBoxModel {
+               private final String preference;
+               private final String trueDesc, falseDesc;
+               private final boolean def;
+               
+               public PrefBooleanSelector(String preference, String falseDescription, 
+                               String trueDescription, boolean defaultState) {
+                       this.preference = preference;
+                       this.trueDesc = trueDescription;
+                       this.falseDesc = falseDescription;
+                       this.def = defaultState;
+               }
+               
+               @Override
+               public Object getSelectedItem() {
+                       if (Prefs.NODE.getBoolean(preference, def)) {
+                               return trueDesc;
+                       } else {
+                               return falseDesc;
+                       }
+               }
+               
+               @Override
+               public void setSelectedItem(Object item) {
+                       if (!(item instanceof String)) {
+                               throw new IllegalArgumentException("Illegal argument "+item);
+                       }
+                       
+                       if (trueDesc.equals(item)) {
+                               Prefs.NODE.putBoolean(preference, true);
+                       } else if (falseDesc.equals(item)) {
+                               Prefs.NODE.putBoolean(preference, false);
+                       } else {
+                               throw new IllegalArgumentException("Illegal argument "+item);
+                       }
+               }
+               
+               @Override
+               public Object getElementAt(int index) {
+                       switch (index) {
+                       case 0:
+                               return def ? trueDesc : falseDesc;
+
+                       case 1:
+                               return def ? falseDesc: trueDesc;
+                               
+                       default:
+                               throw new IndexOutOfBoundsException("Boolean asked for index="+index);
+                       }
+               }
+               @Override
+               public int getSize() {
+                       return 2;
+               }
+       }
+       
+       
+       
+       ////////  Singleton implementation  ////////
+       
+       private static PreferencesDialog dialog = null;
+       
+       public static void showPreferences() {
+               if (dialog != null) {
+                       dialog.dispose();
+               }
+               dialog = new PreferencesDialog();
+               dialog.setVisible(true);
+       }
+       
+       
+}
diff --git a/src/net/sf/openrocket/gui/dialogs/SwingWorkerDialog.java b/src/net/sf/openrocket/gui/dialogs/SwingWorkerDialog.java
new file mode 100644 (file)
index 0000000..6670a93
--- /dev/null
@@ -0,0 +1,118 @@
+package net.sf.openrocket.gui.dialogs;
+
+import java.awt.Window;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JProgressBar;
+import javax.swing.SwingWorker;
+
+import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.util.Pair;
+
+
+/**
+ * A modal dialog that runs specific SwingWorkers and waits until they complete.
+ * A message and progress bar is provided and a cancel button.  If the cancel button
+ * is pressed, the currently running worker is interrupted and the later workers are not
+ * executed.
+ * 
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class SwingWorkerDialog extends JDialog implements PropertyChangeListener {
+
+       private final JLabel label;
+       private final JProgressBar progressBar;
+       
+       private int position;
+       private Pair<String, SwingWorker<?,?>>[] workers;
+       
+       private boolean cancelled = false;
+       
+       public SwingWorkerDialog(Window parent, String title) {
+               super(parent, title, ModalityType.APPLICATION_MODAL);
+
+               JPanel panel = new JPanel(new MigLayout("fill"));
+               
+               label = new JLabel("");
+               panel.add(label, "wrap para");
+               
+               progressBar = new JProgressBar();
+               panel.add(progressBar, "growx, wrap para");
+               
+               JButton cancel = new JButton("Cancel");
+               // TODO: CRITICAL: Implement cancel
+               panel.add(cancel, "right");
+               
+               this.add(panel);
+               this.pack();
+               this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
+       }
+
+       
+       /**
+        * Execute the provided workers one after another.  When this call returns
+        * the workers will all have completed.
+        *   
+        * @param workers       pairs of description texts and workers to run.
+        */
+       public void runWorkers(Pair<String, SwingWorker<?,?>> ... workers) {
+               if (workers.length == 0) {
+                       throw new IllegalArgumentException("No workers provided.");
+               }
+               
+               this.workers = workers;
+               position = -1;
+               
+               for (int i=0; i < workers.length; i++) {
+                       workers[i].getV().addPropertyChangeListener(this);
+               }
+               
+               nextWorker();
+               this.setVisible(true);  // Waits until all have ended
+       }
+
+       
+       
+       /**
+        * Starts the execution of the next worker in the queue.  If the last worker
+        * has completed or the operation has been cancelled, closes the dialog.
+        */
+       private void nextWorker() {
+               if ((position >= workers.length-1) || cancelled) {
+                       close();
+                       return;
+               }
+               
+               position++;
+               
+               label.setText(workers[position].getU());
+               workers[position].getV().execute();
+       }
+       
+       
+
+       @Override
+       public void propertyChange(PropertyChangeEvent evt) {
+               if (workers[position].getV().getState() == SwingWorker.StateValue.DONE) {
+                       nextWorker();
+               }
+               
+               int value = workers[position].getV().getProgress();
+               value = (value + position*100 ) / workers.length;
+               progressBar.setValue(value);
+       }
+       
+       
+       
+       private void close() {
+               for (int i=0; i < workers.length; i++) {
+                       workers[i].getV().removePropertyChangeListener(this);
+               }
+               this.setVisible(false);
+       }
+}
index 3f12d259fc916fdd7047bd662a52acfce6a54a9c..e7cb2ca5c4c451dd28f2343d0c61e4e5454ba305 100644 (file)
@@ -1,5 +1,6 @@
 package net.sf.openrocket.gui.main;
 
+import java.awt.Component;
 import java.awt.Dimension;
 import java.awt.Font;
 import java.awt.Toolkit;
@@ -14,6 +15,7 @@ import java.awt.event.MouseListener;
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -38,6 +40,7 @@ import javax.swing.ListSelectionModel;
 import javax.swing.LookAndFeel;
 import javax.swing.ScrollPaneConstants;
 import javax.swing.SwingUtilities;
+import javax.swing.SwingWorker;
 import javax.swing.ToolTipManager;
 import javax.swing.UIManager;
 import javax.swing.border.TitledBorder;
@@ -56,17 +59,20 @@ import net.sf.openrocket.file.OpenRocketSaver;
 import net.sf.openrocket.file.RocketLoadException;
 import net.sf.openrocket.file.RocketLoader;
 import net.sf.openrocket.file.RocketSaver;
-import net.sf.openrocket.gui.ComponentAnalysisDialog;
-import net.sf.openrocket.gui.PreferencesDialog;
 import net.sf.openrocket.gui.StorageOptionChooser;
 import net.sf.openrocket.gui.configdialog.ComponentConfigDialog;
 import net.sf.openrocket.gui.dialogs.BugDialog;
+import net.sf.openrocket.gui.dialogs.ComponentAnalysisDialog;
+import net.sf.openrocket.gui.dialogs.LicenseDialog;
+import net.sf.openrocket.gui.dialogs.PreferencesDialog;
 import net.sf.openrocket.gui.scalefigure.RocketPanel;
 import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
 import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
 import net.sf.openrocket.rocketcomponent.Rocket;
 import net.sf.openrocket.rocketcomponent.RocketComponent;
 import net.sf.openrocket.rocketcomponent.Stage;
+import net.sf.openrocket.util.ConcurrentProgressMonitor;
+import net.sf.openrocket.util.ConcurrentProgressMonitorInputStream;
 import net.sf.openrocket.util.Icons;
 import net.sf.openrocket.util.Prefs;
 
@@ -588,7 +594,7 @@ public class BasicFrame extends JFrame {
            
            for (File file: files) {
                System.out.println("Opening file: " + file);
-               if (open(file)) {
+               if (open(file, this)) {
                        opened = true;
                }
            }
@@ -604,13 +610,16 @@ public class BasicFrame extends JFrame {
         * Open the specified file in a new design frame.  If an error occurs, an error dialog
         * is shown and <code>false</code> is returned.
         * 
-        * @param file  the file to open.
-        * @return              whether the file was successfully loaded and opened.
+        * @param file          the file to open.
+        * @param parent        the parent component for which a progress dialog is opened.
+        * @return                      whether the file was successfully loaded and opened.
         */
-       private static boolean open(File file) {
+       private static boolean open(File file, Component parent) {
            OpenRocketDocument doc = null;
+           
+           
                try {
-                       doc = ROCKET_LOADER.load(file);
+                       doc = ROCKET_LOADER.load(file, parent);
                } catch (RocketLoadException e) {
                        JOptionPane.showMessageDialog(null, "Unable to open file '" + file.getName() 
                                        +"': " + e.getMessage(), "Error opening file", JOptionPane.ERROR_MESSAGE);
@@ -643,6 +652,33 @@ public class BasicFrame extends JFrame {
        
        
        
+       private static class OpenWorker extends SwingWorker<OpenRocketDocument, Void> {
+               private final File file;
+               private final Component parent;
+               private ConcurrentProgressMonitor monitor = null;
+               
+               public OpenWorker(File file, Component parent) {
+                       this.file = file;
+                       this.parent = parent;
+               }
+               
+               @Override
+               protected OpenRocketDocument doInBackground() throws Exception {
+                       ConcurrentProgressMonitorInputStream is = 
+                               new ConcurrentProgressMonitorInputStream(parent, 
+                                               "Loading " + file.getName(), new FileInputStream(file));
+                       monitor = is.getProgressMonitor();
+                       return ROCKET_LOADER.load(is);
+               }
+               
+               public ConcurrentProgressMonitor getMonitor() {
+                       return monitor;
+               }
+       }
+       
+       
+       
+       
        private boolean saveAction() {
                File file = document.getFile();
                if (file==null) {
@@ -804,6 +840,35 @@ 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.
+        * 
+        * @param rocket the Rocket.
+        * @return               the corresponding BasicFrame, or <code>null</code> if none found.
+        */
+       public static BasicFrame findFrame(Rocket rocket) {
+               for (BasicFrame f: frames) {
+                       if (f.rocket == rocket)
+                               return f;
+               }
+               return null;
+       }
+       
+       /**
+        * Find a currently open document by the rocket object.  This method can be used
+        * to map a Rocket to OpenRocketDocument from GUI methods.
+        * 
+        * @param rocket the Rocket.
+        * @return               the corresponding OpenRocketDocument, or <code>null</code> if not found.
+        */
+       public static OpenRocketDocument findDocument(Rocket rocket) {
+               for (BasicFrame f: frames) {
+                       if (f.rocket == rocket)
+                               return f.document;
+               }
+               return null;
+       }
        
        
        
@@ -861,7 +926,7 @@ public class BasicFrame extends JFrame {
                // Check command-line for files
                boolean opened = false;
                for (String file: args) {
-                       if (open(new File(file))) {
+                       if (open(new File(file), null)) {
                                opened = true;
                        }
                }
diff --git a/src/net/sf/openrocket/gui/main/LicenseDialog.java b/src/net/sf/openrocket/gui/main/LicenseDialog.java
deleted file mode 100644 (file)
index 6df2569..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-package net.sf.openrocket.gui.main;
-
-import java.awt.Font;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
-
-import javax.swing.JButton;
-import javax.swing.JDialog;
-import javax.swing.JFrame;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.JTextArea;
-
-import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.gui.components.ResizeLabel;
-import net.sf.openrocket.util.GUIUtil;
-
-public class LicenseDialog extends JDialog {
-       private static final String LICENSE_FILENAME = "LICENSE.TXT";
-       
-       private static final String DEFAULT_LICENSE_TEXT =
-               "\n" +
-               "Error:  Unable to load " + LICENSE_FILENAME + "!\n" +
-               "\n" +
-               "OpenRocket is licensed under the GNU GPL version 3, with additional permissions.\n" +
-               "See http://openrocket.sourceforge.net/ for details.";
-
-       public LicenseDialog(JFrame parent) {
-               super(parent, true);
-               
-               JPanel panel = new JPanel(new MigLayout("fill"));
-               
-               panel.add(new ResizeLabel("OpenRocket license", 10), "ax 50%, wrap para");
-
-               String licenseText;
-               try {
-                       
-                       BufferedReader reader = new BufferedReader(
-                                       new InputStreamReader(ClassLoader.getSystemResourceAsStream(LICENSE_FILENAME)));
-                       StringBuffer sb = new StringBuffer();
-                       for (String s = reader.readLine(); s != null; s = reader.readLine()) {
-                               sb.append(s);
-                               sb.append('\n');
-                       }
-                       licenseText = sb.toString();
-                       
-               } catch (Exception e) {
-
-                       licenseText = DEFAULT_LICENSE_TEXT;
-                       
-               }
-               
-               JTextArea text = new JTextArea(licenseText);
-               text.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
-               text.setRows(20);
-               text.setColumns(80);
-               text.setEditable(false);
-               panel.add(new JScrollPane(text),"grow, wrap para");
-               
-               JButton close = new JButton("Close");
-               close.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               LicenseDialog.this.dispose();
-                       }
-               });
-               panel.add(close, "right");
-               
-               this.add(panel);
-               this.setTitle("OpenRocket license");
-               this.pack();
-               this.setLocationByPlatform(true);
-               GUIUtil.setDefaultButton(close);
-               GUIUtil.installEscapeCloseOperation(this);
-       }
-       
-}
index 04a78979aa6575593143edaade26decf14308fd6..63c8535e4a28cb13a0db624b17fa561ea0031af5 100644 (file)
@@ -9,6 +9,7 @@ import java.awt.event.ActionListener;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 import java.text.Collator;
+import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.Locale;
 import java.util.regex.Matcher;
@@ -22,8 +23,11 @@ import javax.swing.JLabel;
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
 import javax.swing.JTable;
+import javax.swing.JTextField;
 import javax.swing.ListSelectionModel;
 import javax.swing.RowFilter;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
 import javax.swing.event.ListSelectionEvent;
 import javax.swing.event.ListSelectionListener;
 import javax.swing.table.AbstractTableModel;
@@ -49,7 +53,9 @@ public class MotorChooserDialog extends JDialog {
                "Show motors with diameter equal to that of the motor mount"
        };
        private static final int SHOW_MAX = 2;
-       
+
+       private final JTextField searchField; 
+       private String[] searchTerms = new String[0];
 
        private final double diameter;
 
@@ -81,16 +87,16 @@ public class MotorChooserDialog extends JDialog {
                this.selectedDelay = delay;
                this.diameter = diameter;
                
-               JPanel panel = new JPanel(new MigLayout("fill"));
+               JPanel panel = new JPanel(new MigLayout("fill", "[grow][]"));
 
                // Label
                JLabel label = new JLabel("Select a rocket motor:");
                label.setFont(label.getFont().deriveFont(Font.BOLD));
-               panel.add(label,"split 2, growx");
+               panel.add(label,"growx");
                
                label = new JLabel("Motor mount diameter: " +
                                UnitGroup.UNITS_MOTOR_DIMENSIONS.getDefaultUnit().toStringUnit(diameter));
-               panel.add(label,"alignx 100%, wrap paragraph");
+               panel.add(label,"gapleft para, wrap paragraph");
                
                
                // Diameter selection
@@ -104,17 +110,14 @@ public class MotorChooserDialog extends JDialog {
                                        sel = SHOW_ALL;
                                switch (sel) {
                                case SHOW_ALL:
-                                       System.out.println("Setting filter: all");
                                        sorter.setRowFilter(new MotorRowFilterAll());
                                        break;
                                        
                                case SHOW_SMALLER:
-                                       System.out.println("Setting filter: smaller");
                                        sorter.setRowFilter(new MotorRowFilterSmaller());
                                        break;
                                        
                                case SHOW_EXACT:
-                                       System.out.println("Setting filter: exact");
                                        sorter.setRowFilter(new MotorRowFilterExact());
                                        break;
                                        
@@ -125,9 +128,46 @@ public class MotorChooserDialog extends JDialog {
                                setSelectionVisible();
                        }
                });
-               panel.add(combo,"growx, wrap");
+               panel.add(combo,"growx 1000");
 
                
+               
+               label = new JLabel("Search:");
+               panel.add(label, "gapleft para, split 2");
+               
+               searchField = new JTextField();
+               searchField.getDocument().addDocumentListener(new DocumentListener() {
+                       @Override
+                       public void changedUpdate(DocumentEvent e) {
+                               update();
+                       }
+                       @Override
+                       public void insertUpdate(DocumentEvent e) {
+                               update();
+                       }
+                       @Override
+                       public void removeUpdate(DocumentEvent e) {
+                               update();
+                       }
+                       
+                       private void update() {
+                               String text = searchField.getText().trim();
+                               String[] split = text.split("\\s+");
+                               ArrayList<String> list = new ArrayList<String>();
+                               for (String s: split) {
+                                       s = s.trim().toLowerCase();
+                                       if (s.length() > 0) {
+                                               list.add(s);
+                                       }
+                               }
+                               searchTerms = list.toArray(new String[0]);
+                               sorter.sort();
+                       }
+               });
+               panel.add(searchField, "growx 1, wrap");
+               
+               
+               
                // Table, overridden to show meaningful tooltip texts
                model = new MotorDatabaseModel(current);
                table = new JTable(model) {
@@ -186,11 +226,11 @@ public class MotorChooserDialog extends JDialog {
                
                JScrollPane scrollpane = new JScrollPane();
                scrollpane.setViewportView(table);
-               panel.add(scrollpane,"grow, width :700:, height :300:, wrap paragraph");
+               panel.add(scrollpane,"spanx, grow, width :700:, height :300:, wrap paragraph");
                
                
                // Ejection delay
-               panel.add(new JLabel("Select ejection charge delay:"), "split 3, gap rel");
+               panel.add(new JLabel("Select ejection charge delay:"), "spanx, split 3, gap rel");
                
                delayBox = new JComboBox();
                delayBox.setEditable(true);
@@ -222,7 +262,7 @@ public class MotorChooserDialog extends JDialog {
                                MotorChooserDialog.this.setVisible(false);
                        }
                });
-               panel.add(okButton,"split, tag ok");
+               panel.add(okButton,"spanx, split, tag ok");
 
                button = new JButton("Cancel");
                button.addActionListener(new ActionListener() {
@@ -248,6 +288,9 @@ public class MotorChooserDialog extends JDialog {
                
                // Table can be scrolled only after pack() has been called
                setSelectionVisible();
+               
+               // Focus the search field
+               searchField.grabFocus();
        }
        
        private void setSelectionVisible() {
@@ -567,14 +610,26 @@ public class MotorChooserDialog extends JDialog {
         */
        private abstract class MotorRowFilter extends RowFilter<TableModel,Integer> {
                @Override
-               public boolean include(
-                               RowFilter.Entry<? extends TableModel, ? extends Integer> entry) {
+               public boolean include(RowFilter.Entry<? extends TableModel, ? extends Integer> entry) {
                        int index = entry.getIdentifier();
                        Motor m = model.getMotor(index);
-                       return include(m);
+                       return filterByDiameter(m) && filterByString(m);
                }
                
-               public abstract boolean include(Motor m);
+               public abstract boolean filterByDiameter(Motor m);
+               
+               
+               public boolean filterByString(Motor m) {
+                       main: for (String s : searchTerms) {
+                               for (MotorColumns col : MotorColumns.values()) {
+                                       String str = col.getValue(m).toLowerCase();
+                                       if (str.indexOf(s) >= 0)
+                                               continue main;
+                               }
+                               return false;
+                       }
+                       return true;
+               }
        }
        
        /**
@@ -582,7 +637,7 @@ public class MotorChooserDialog extends JDialog {
         */
        private class MotorRowFilterAll extends MotorRowFilter {
                @Override
-               public boolean include(Motor m) {
+               public boolean filterByDiameter(Motor m) {
                        return true;
                }
        }
@@ -592,7 +647,7 @@ public class MotorChooserDialog extends JDialog {
         */
        private class MotorRowFilterSmaller extends MotorRowFilter {
                @Override
-               public boolean include(Motor m) {
+               public boolean filterByDiameter(Motor m) {
                        return (m.getDiameter() <= diameter + 0.0004);
                }
        }
@@ -602,7 +657,7 @@ public class MotorChooserDialog extends JDialog {
         */
        private class MotorRowFilterExact extends MotorRowFilter {
                @Override
-               public boolean include(Motor m) {
+               public boolean filterByDiameter(Motor m) {
                        return ((m.getDiameter() <= diameter + 0.0004) &&
                                        (m.getDiameter() >= diameter - 0.0015));
                }
index fa98140a6b2ba1ee5b746c2a4e668e3e89fe17b9..e16486b7a8fe7d13cb6aff38dc791063be94b2cd 100644 (file)
@@ -25,7 +25,7 @@ import javax.swing.JProgressBar;
 
 import net.miginfocom.swing.MigLayout;
 import net.sf.openrocket.document.Simulation;
-import net.sf.openrocket.gui.DetailDialog;
+import net.sf.openrocket.gui.dialogs.DetailDialog;
 import net.sf.openrocket.rocketcomponent.Configuration;
 import net.sf.openrocket.rocketcomponent.MotorMount;
 import net.sf.openrocket.rocketcomponent.MotorMount.IgnitionEvent;
index 5019fccc9e2eaeb2c676536c4c419146d2d850b9..d80d369562075862a562e93c2bb0b340e72ff8df 100644 (file)
@@ -56,9 +56,11 @@ public class ClusterConfiguration {
        };
        
        
+       
        private final List<Double> points;
        private final String xmlName;
        
+       
        private ClusterConfiguration(String xmlName, double... points) {
                this.xmlName = xmlName;
                if (points.length == 0 || points.length%2 == 1) {
@@ -100,4 +102,10 @@ public class ClusterConfiguration {
                }
                return ret;
        }
+       
+       
+       @Override
+       public String toString() {
+               return xmlName;
+       }
 }
index 1ea92caee2f879f7f3ea908885bebfd5018b8137..1312799fd36c1a8ebf51a0aa2498f3e0908aec86 100644 (file)
@@ -59,10 +59,11 @@ public class FreeformFinSet extends FinSet {
         * if attempted.
         * 
         * @param index   the fin point index to remove
+        * @throws IllegalFinPointException if removing the first or last fin point was attempted.
         */
-       public void removePoint(int index) {
+       public void removePoint(int index) throws IllegalFinPointException {
                if (index == 0  ||  index == points.size()-1) {
-                       throw new IllegalArgumentException("cannot remove first or last point");
+                       throw new IllegalFinPointException("cannot remove first or last point");
                }
                points.remove(index);
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
@@ -73,19 +74,19 @@ public class FreeformFinSet extends FinSet {
                return points.size();
        }
        
-       public void setPoints(Coordinate[] p) {
+       public void setPoints(Coordinate[] p) throws IllegalFinPointException {
                if (p[0].x != 0 || p[0].y != 0 || p[p.length-1].y != 0) {
-                       throw new IllegalArgumentException("Start or end point illegal.");
+                       throw new IllegalFinPointException("Start or end point illegal.");
                }
                for (int i=0; i < p.length-1; i++) {
                        for (int j=i+2; j < p.length-1; j++) {
                                if (intersects(p[i].x, p[i].y, p[i+1].x, p[i+1].y,
                                                       p[j].x, p[j].y, p[j+1].x, p[j+1].y)) {
-                                       throw new IllegalArgumentException("segments intersect");
+                                       throw new IllegalFinPointException("segments intersect");
                                }
                        }
                        if (p[i].z != 0) {
-                               throw new IllegalArgumentException("z-coordinate not zero");
+                               throw new IllegalFinPointException("z-coordinate not zero");
                        }
                }
                
@@ -112,8 +113,10 @@ public class FreeformFinSet extends FinSet {
         * @param index the point index to modify.
         * @param x             the x-coordinate.
         * @param y             the y-coordinate.
+        * @throws IllegalFinPointException     if the specified fin point would cause intersecting
+        *                                                                      segments
         */
-       public void setPoint(int index, double x, double y) {
+       public void setPoint(int index, double x, double y) throws IllegalFinPointException {
                if (y < 0)
                        y = 0;
                
@@ -160,12 +163,12 @@ public class FreeformFinSet extends FinSet {
                        
                        if (i != index-1 && i != index && i != index+1) {
                                if (intersects(x0,y0,x,y,px0,py0,px1,py1)) {
-                                       throw new IllegalArgumentException("segments intersect");
+                                       throw new IllegalFinPointException("segments intersect");
                                }
                        }
                        if (i != index && i != index+1 && i != index+2) {
                                if (intersects(x,y,x1,y1,px0,py0,px1,py1)) {
-                                       throw new IllegalArgumentException("segments intersect");
+                                       throw new IllegalFinPointException("segments intersect");
                                }
                        }
                        
diff --git a/src/net/sf/openrocket/rocketcomponent/IllegalFinPointException.java b/src/net/sf/openrocket/rocketcomponent/IllegalFinPointException.java
new file mode 100644 (file)
index 0000000..8f9a88a
--- /dev/null
@@ -0,0 +1,27 @@
+package net.sf.openrocket.rocketcomponent;
+
+/**
+ * An exception signifying that an operation on the freeform fin set points was
+ * illegal (segments intersect, removing first or last point, etc).
+ * 
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class IllegalFinPointException extends Exception {
+
+       public IllegalFinPointException() {
+
+       }
+
+       public IllegalFinPointException(String message) {
+               super(message);
+       }
+
+       public IllegalFinPointException(Throwable cause) {
+               super(cause);
+       }
+
+       public IllegalFinPointException(String message, Throwable cause) {
+               super(message, cause);
+       }
+
+}
index f696a69da0cc7b8241051a3b78825a29214b75f0..48904f892992a3c2e21586bfdb8079e445ca919d 100644 (file)
@@ -7,7 +7,6 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
 import javax.swing.event.ChangeListener;
@@ -72,8 +71,8 @@ public class Rocket extends RocketComponent {
        
        
        // Motor configuration list
-       private List<String> motorConfigurationIDs = new ArrayList<String>();
-       private Map<String, String> motorConfigurationNames = new HashMap<String, String>();
+       private ArrayList<String> motorConfigurationIDs = new ArrayList<String>();
+       private HashMap<String, String> motorConfigurationNames = new HashMap<String, String>();
        {
                motorConfigurationIDs.add(null);
        }
@@ -267,22 +266,25 @@ public class Rocket extends RocketComponent {
        }
        
 
+       
+       
+       
        /**
         * Make a deep copy of the Rocket structure.  This is a helper method which simply 
         * casts the result of the superclass method to a Rocket.
         */
+       @SuppressWarnings("unchecked")
        @Override
        public Rocket copy() {
                Rocket copy = (Rocket)super.copy();
+               copy.motorConfigurationIDs = (ArrayList<String>) this.motorConfigurationIDs.clone();
+               copy.motorConfigurationNames = 
+                       (HashMap<String, String>) this.motorConfigurationNames.clone();
                copy.resetListeners();
+               
                return copy;
        }
        
-       
-       
-
-       
-       
        /**
         * Load the rocket structure from the source.  The method loads the fields of this
         * Rocket object and copies the references to siblings from the <code>source</code>.
@@ -293,6 +295,7 @@ public class Rocket extends RocketComponent {
         * and therefore fires an UNDO_EVENT, masked with all applicable mass/aerodynamic/tree
         * changes.
         */
+       @SuppressWarnings("unchecked")
        public void loadFrom(Rocket r) {
                super.copyFrom(r);
                
@@ -312,10 +315,15 @@ public class Rocket extends RocketComponent {
                this.refType = r.refType;
                this.customReferenceLength = r.customReferenceLength;
                
-               this.motorConfigurationIDs = r.motorConfigurationIDs;
-               this.motorConfigurationNames = r.motorConfigurationNames;
+               this.motorConfigurationIDs = (ArrayList<String>) r.motorConfigurationIDs.clone();
+               this.motorConfigurationNames = 
+                       (HashMap<String, String>) r.motorConfigurationNames.clone();
                this.perfectFinish = r.perfectFinish;
                
+               String id = defaultConfiguration.getMotorConfigurationID();
+               if (!this.motorConfigurationIDs.contains(id))
+                       defaultConfiguration.setMotorConfigurationID(null);
+               
                fireComponentChangeEvent(type);
        }
 
@@ -594,7 +602,7 @@ public class Rocket extends RocketComponent {
         */
        public void setMotorConfigurationName(String id, String name) {
                motorConfigurationNames.put(id,name);
-               fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
+               fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
        }
        
                
diff --git a/src/net/sf/openrocket/util/ConcurrentProgressMonitor.java b/src/net/sf/openrocket/util/ConcurrentProgressMonitor.java
new file mode 100644 (file)
index 0000000..6d98a5f
--- /dev/null
@@ -0,0 +1,45 @@
+package net.sf.openrocket.util;
+
+import java.awt.Component;
+
+import javax.swing.ProgressMonitor;
+import javax.swing.SwingUtilities;
+
+
+/**
+ * A thread-safe <code>ProgressMonitor</code>.  This class may be instantiated
+ * and the method {@link #setProgress(int)} called safely from any thread.
+ * <p>
+ * Why the FSCK&!¤#&%¤ isn't the default API version thread-safe?!?!
+ * 
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class ConcurrentProgressMonitor extends ProgressMonitor {
+
+       public ConcurrentProgressMonitor(Component parentComponent, Object message,
+                       String note, int min, int max) {
+               super(parentComponent, message, note, min, max);
+       }
+
+       @Override
+       public void setProgress(final int nv) {
+               
+               if (SwingUtilities.isEventDispatchThread()) {
+                       super.setProgress(nv);
+               } else {
+                       
+                       SwingUtilities.invokeLater(new Runnable() {
+
+                               @Override
+                               public void run() {
+                                       ConcurrentProgressMonitor.super.setProgress(nv);
+                               }
+                               
+                       });
+                       
+               }
+       }
+       
+       
+
+}
diff --git a/src/net/sf/openrocket/util/ConcurrentProgressMonitorInputStream.java b/src/net/sf/openrocket/util/ConcurrentProgressMonitorInputStream.java
new file mode 100644 (file)
index 0000000..7fd5a8a
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * TODO: CRITICAL: Licensing
+ */
+
+package net.sf.openrocket.util;
+
+import java.awt.Component;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+
+
+/**
+ * A functional equivalent of <code>ProgressMonitorInputStream</code> which 
+ * uses {@link ConcurrentProgressMonitor} and leaves the progress dialog open
+ * to be manually closed later on.
+ */
+
+public class ConcurrentProgressMonitorInputStream extends FilterInputStream {
+       private ConcurrentProgressMonitor monitor;
+       private int nread = 0;
+       private int size = 0;
+
+
+       /**
+        * Constructs an object to monitor the progress of an input stream.
+        *
+        * @param message Descriptive text to be placed in the dialog box
+        *                if one is popped up.
+        * @param parentComponent The component triggering the operation
+        *                        being monitored.
+        * @param in The input stream to be monitored.
+        */
+       public ConcurrentProgressMonitorInputStream(Component parentComponent,
+                       Object message, InputStream in) {
+               super(in);
+               try {
+                       size = in.available();
+               } catch (IOException ioe) {
+                       size = 0;
+               }
+               monitor = new ConcurrentProgressMonitor(parentComponent, message, null, 0,
+                               size + 1);
+       }
+
+
+       /**
+        * Get the ProgressMonitor object being used by this stream. Normally
+        * this isn't needed unless you want to do something like change the
+        * descriptive text partway through reading the file.
+        * @return the ProgressMonitor object used by this object 
+        */
+       public ConcurrentProgressMonitor getProgressMonitor() {
+               return monitor;
+       }
+
+
+       /**
+        * Overrides <code>FilterInputStream.read</code> 
+        * to update the progress monitor after the read.
+        */
+       @Override
+       public int read() throws IOException {
+               int c = in.read();
+               if (c >= 0)
+                       monitor.setProgress(++nread);
+               if (monitor.isCanceled()) {
+                       InterruptedIOException exc = new InterruptedIOException("progress");
+                       exc.bytesTransferred = nread;
+                       throw exc;
+               }
+               return c;
+       }
+
+
+       /**
+        * Overrides <code>FilterInputStream.read</code> 
+        * to update the progress monitor after the read.
+        */
+       @Override
+       public int read(byte b[]) throws IOException {
+               int nr = in.read(b);
+               if (nr > 0)
+                       monitor.setProgress(nread += nr);
+               if (monitor.isCanceled()) {
+                       InterruptedIOException exc = new InterruptedIOException("progress");
+                       exc.bytesTransferred = nread;
+                       throw exc;
+               }
+               return nr;
+       }
+
+
+       /**
+        * Overrides <code>FilterInputStream.read</code> 
+        * to update the progress monitor after the read.
+        */
+       @Override
+       public int read(byte b[], int off, int len) throws IOException {
+               int nr = in.read(b, off, len);
+               if (nr > 0)
+                       monitor.setProgress(nread += nr);
+               if (monitor.isCanceled()) {
+                       InterruptedIOException exc = new InterruptedIOException("progress");
+                       exc.bytesTransferred = nread;
+                       throw exc;
+               }
+               return nr;
+       }
+
+
+       /**
+        * Overrides <code>FilterInputStream.skip</code> 
+        * to update the progress monitor after the skip.
+        */
+       @Override
+       public long skip(long n) throws IOException {
+               long nr = in.skip(n);
+               if (nr > 0)
+                       monitor.setProgress(nread += nr);
+               return nr;
+       }
+
+
+       /**
+        * Overrides <code>FilterInputStream.close</code> 
+        * to close the progress monitor as well as the stream.
+        */
+       @Override
+       public void close() throws IOException {
+               in.close();
+       }
+
+
+       /**
+        * Overrides <code>FilterInputStream.reset</code> 
+        * to reset the progress monitor as well as the stream.
+        */
+       @Override
+       public synchronized void reset() throws IOException {
+               in.reset();
+               nread = size - in.available();
+               monitor.setProgress(nread);
+       }
+}
index 8f039555cc4b174e36f6c43e80b267fd5d2a0d21..4d150d42debc1ea6ad1d901e08d4bb2057f89b3f 100644 (file)
@@ -6,6 +6,7 @@ import net.sf.openrocket.rocketcomponent.BodyTube;
 import net.sf.openrocket.rocketcomponent.Bulkhead;
 import net.sf.openrocket.rocketcomponent.CenteringRing;
 import net.sf.openrocket.rocketcomponent.FreeformFinSet;
+import net.sf.openrocket.rocketcomponent.IllegalFinPointException;
 import net.sf.openrocket.rocketcomponent.InnerTube;
 import net.sf.openrocket.rocketcomponent.LaunchLug;
 import net.sf.openrocket.rocketcomponent.MassComponent;
@@ -144,13 +145,17 @@ public class Test {
                bodytube = new BodyTube(0.69,0.033,0.001);
 
                finset = new FreeformFinSet();
-               finset.setPoints(new Coordinate[] {
-                               new Coordinate(0, 0),
-                               new Coordinate(0.115, 0.072),
-                               new Coordinate(0.255, 0.072),
-                               new Coordinate(0.255, 0.037),
-                               new Coordinate(0.150, 0)
-               });
+               try {
+                       finset.setPoints(new Coordinate[] {
+                                       new Coordinate(0, 0),
+                                       new Coordinate(0.115, 0.072),
+                                       new Coordinate(0.255, 0.072),
+                                       new Coordinate(0.255, 0.037),
+                                       new Coordinate(0.150, 0)
+                       });
+               } catch (IllegalFinPointException e) {
+                       e.printStackTrace();
+               }
                finset.setThickness(0.003);
                finset.setFinCount(4);