From: plaa Date: Sun, 21 Jun 2009 18:24:33 +0000 (+0000) Subject: Motor chooser search field and new edit motor config dialog X-Git-Tag: upstream/1.0.0~29 X-Git-Url: https://git.gag.com/?a=commitdiff_plain;h=4b214bb1bdbcbe40d47c16c380715ed0b5095d64;p=debian%2Fopenrocket Motor chooser search field and new edit motor config dialog git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@10 180e2498-e6e9-4542-8430-84ac67f01cd8 --- diff --git a/ChangeLog b/ChangeLog index 9808b616..f54156a7 100644 --- 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 diff --git a/ReleaseNotes b/ReleaseNotes index c802e1ab..2703d971 100644 --- a/ReleaseNotes +++ b/ReleaseNotes @@ -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 a39c6647..e05d6f84 100644 --- a/TODO +++ b/TODO @@ -1,126 +1,35 @@ -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 diff --git a/build.properties b/build.properties index 3a32f338..9fd21c5a 100644 --- a/build.properties +++ b/build.properties @@ -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! diff --git a/src/net/sf/openrocket/document/OpenRocketDocument.java b/src/net/sf/openrocket/document/OpenRocketDocument.java index 24c144cd..164f76da 100644 --- a/src/net/sf/openrocket/document/OpenRocketDocument.java +++ b/src/net/sf/openrocket/document/OpenRocketDocument.java @@ -45,6 +45,7 @@ public class OpenRocketDocument implements ComponentChangeListener { private LinkedList undoDescription = new LinkedList(); 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) { diff --git a/src/net/sf/openrocket/file/OpenRocketLoader.java b/src/net/sf/openrocket/file/OpenRocketLoader.java index e1978a7f..81edde84 100644 --- a/src/net/sf/openrocket/file/OpenRocketLoader.java +++ b/src/net/sf/openrocket/file/OpenRocketLoader.java @@ -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.")); } } diff --git a/src/net/sf/openrocket/file/RocketLoader.java b/src/net/sf/openrocket/file/RocketLoader.java index 13f888ca..0e8e051e 100644 --- a/src/net/sf/openrocket/file/RocketLoader.java +++ b/src/net/sf/openrocket/file/RocketLoader.java @@ -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 index 7fa39a7a..00000000 --- a/src/net/sf/openrocket/gui/ComponentAnalysisDialog.java +++ /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 cpData = new ArrayList(); - private final List dragData = new ArrayList(); - private double totalCD = 0; - private final List rollData = new ArrayList(); - - - 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("CN\u03b1") { - @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("Pressure CD") { - @Override public Object getValueAt(int row) { - return dragData.get(row).pressureCD; - } - }, - new Column("Base CD") { - @Override public Object getValueAt(int row) { - return dragData.get(row).baseCD; - } - }, - new Column("Friction CD") { - @Override public Object getValueAt(int row) { - return dragData.get(row).frictionCD; - } - }, - new Column("Total CD") { - @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("Total Cl") { - @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 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[] { - "No warnings." - }); - } else { - warningList.setListData(new Vector(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 index f0c08308..00000000 --- a/src/net/sf/openrocket/gui/DetailDialog.java +++ /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 index 2cdb4c45..00000000 --- a/src/net/sf/openrocket/gui/PreferencesDialog.java +++ /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 unitSelectors = new ArrayList(); - - 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/adaptors/MotorConfigurationModel.java b/src/net/sf/openrocket/gui/adaptors/MotorConfigurationModel.java index b24d0c94..92e84739 100644 --- a/src/net/sf/openrocket/gui/adaptors/MotorConfigurationModel.java +++ b/src/net/sf/openrocket/gui/adaptors/MotorConfigurationModel.java @@ -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 columnList = new ArrayList(); - columnList.add(new Column("Name") { - @Override - public Object getValueAt(int row) { - return rocket.getMotorConfigurationNameOrDescription(ids[row]); - } - }); - - // Create columns from the motor mounts - Iterator 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])); - } - } - } - } diff --git a/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java b/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java index 889d4adf..edda7dc4 100644 --- a/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java +++ b/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java @@ -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) { } } diff --git a/src/net/sf/openrocket/gui/configdialog/InnerTubeConfig.java b/src/net/sf/openrocket/gui/configdialog/InnerTubeConfig.java index b1319b7b..6f568194 100644 --- a/src/net/sf/openrocket/gui/configdialog/InnerTubeConfig.java +++ b/src/net/sf/openrocket/gui/configdialog/InnerTubeConfig.java @@ -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 index 00000000..3c900f25 --- /dev/null +++ b/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java @@ -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 cpData = new ArrayList(); + private final List dragData = new ArrayList(); + private double totalCD = 0; + private final List rollData = new ArrayList(); + + + 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("CN\u03b1") { + @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("Pressure CD") { + @Override public Object getValueAt(int row) { + return dragData.get(row).pressureCD; + } + }, + new Column("Base CD") { + @Override public Object getValueAt(int row) { + return dragData.get(row).baseCD; + } + }, + new Column("Friction CD") { + @Override public Object getValueAt(int row) { + return dragData.get(row).frictionCD; + } + }, + new Column("Total CD") { + @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("Total Cl") { + @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 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[] { + "No warnings." + }); + } else { + warningList.setListData(new Vector(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 index 00000000..05a19950 --- /dev/null +++ b/src/net/sf/openrocket/gui/dialogs/DetailDialog.java @@ -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 index 00000000..ffa3a096 --- /dev/null +++ b/src/net/sf/openrocket/gui/dialogs/EditMotorConfigurationDialog.java @@ -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 mountList = new ArrayList(); + Iterator 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("Motor mounts:"); + panel.add(label, "gapbottom para"); + + label = new JLabel("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("Motor configurations:"); + 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 index 00000000..bc718d6d --- /dev/null +++ b/src/net/sf/openrocket/gui/dialogs/LicenseDialog.java @@ -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 index 00000000..cfb6f882 --- /dev/null +++ b/src/net/sf/openrocket/gui/dialogs/PreferencesDialog.java @@ -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 unitSelectors = new ArrayList(); + + 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 index 00000000..6670a931 --- /dev/null +++ b/src/net/sf/openrocket/gui/dialogs/SwingWorkerDialog.java @@ -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 + */ +public class SwingWorkerDialog extends JDialog implements PropertyChangeListener { + + private final JLabel label; + private final JProgressBar progressBar; + + private int position; + private Pair>[] 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> ... 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); + } +} diff --git a/src/net/sf/openrocket/gui/main/BasicFrame.java b/src/net/sf/openrocket/gui/main/BasicFrame.java index 3f12d259..e7cb2ca5 100644 --- a/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -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 false 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 { + 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 null 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 null 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 index 6df25692..00000000 --- a/src/net/sf/openrocket/gui/main/LicenseDialog.java +++ /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); - } - -} diff --git a/src/net/sf/openrocket/gui/main/MotorChooserDialog.java b/src/net/sf/openrocket/gui/main/MotorChooserDialog.java index 04a78979..63c8535e 100644 --- a/src/net/sf/openrocket/gui/main/MotorChooserDialog.java +++ b/src/net/sf/openrocket/gui/main/MotorChooserDialog.java @@ -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 list = new ArrayList(); + 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 { @Override - public boolean include( - RowFilter.Entry entry) { + public boolean include(RowFilter.Entry 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)); } diff --git a/src/net/sf/openrocket/gui/main/SimulationRunDialog.java b/src/net/sf/openrocket/gui/main/SimulationRunDialog.java index fa98140a..e16486b7 100644 --- a/src/net/sf/openrocket/gui/main/SimulationRunDialog.java +++ b/src/net/sf/openrocket/gui/main/SimulationRunDialog.java @@ -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; diff --git a/src/net/sf/openrocket/rocketcomponent/ClusterConfiguration.java b/src/net/sf/openrocket/rocketcomponent/ClusterConfiguration.java index 5019fccc..d80d3695 100644 --- a/src/net/sf/openrocket/rocketcomponent/ClusterConfiguration.java +++ b/src/net/sf/openrocket/rocketcomponent/ClusterConfiguration.java @@ -56,9 +56,11 @@ public class ClusterConfiguration { }; + private final List 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; + } } diff --git a/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java b/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java index 1ea92cae..1312799f 100644 --- a/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java +++ b/src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java @@ -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 index 00000000..8f9a88ac --- /dev/null +++ b/src/net/sf/openrocket/rocketcomponent/IllegalFinPointException.java @@ -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 + */ +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); + } + +} diff --git a/src/net/sf/openrocket/rocketcomponent/Rocket.java b/src/net/sf/openrocket/rocketcomponent/Rocket.java index f696a69d..48904f89 100644 --- a/src/net/sf/openrocket/rocketcomponent/Rocket.java +++ b/src/net/sf/openrocket/rocketcomponent/Rocket.java @@ -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 motorConfigurationIDs = new ArrayList(); - private Map motorConfigurationNames = new HashMap(); + private ArrayList motorConfigurationIDs = new ArrayList(); + private HashMap motorConfigurationNames = new HashMap(); { 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) this.motorConfigurationIDs.clone(); + copy.motorConfigurationNames = + (HashMap) 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 source. @@ -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) r.motorConfigurationIDs.clone(); + this.motorConfigurationNames = + (HashMap) 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 index 00000000..6d98a5fc --- /dev/null +++ b/src/net/sf/openrocket/util/ConcurrentProgressMonitor.java @@ -0,0 +1,45 @@ +package net.sf.openrocket.util; + +import java.awt.Component; + +import javax.swing.ProgressMonitor; +import javax.swing.SwingUtilities; + + +/** + * A thread-safe ProgressMonitor. This class may be instantiated + * and the method {@link #setProgress(int)} called safely from any thread. + *

+ * Why the FSCK&!¤#&%¤ isn't the default API version thread-safe?!?! + * + * @author Sampo Niskanen + */ +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 index 00000000..7fd5a8a9 --- /dev/null +++ b/src/net/sf/openrocket/util/ConcurrentProgressMonitorInputStream.java @@ -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 ProgressMonitorInputStream 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 FilterInputStream.read + * 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 FilterInputStream.read + * 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 FilterInputStream.read + * 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 FilterInputStream.skip + * 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 FilterInputStream.close + * to close the progress monitor as well as the stream. + */ + @Override + public void close() throws IOException { + in.close(); + } + + + /** + * Overrides FilterInputStream.reset + * 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); + } +} diff --git a/src/net/sf/openrocket/util/Test.java b/src/net/sf/openrocket/util/Test.java index 8f039555..4d150d42 100644 --- a/src/net/sf/openrocket/util/Test.java +++ b/src/net/sf/openrocket/util/Test.java @@ -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);