Motor chooser search field and new edit motor config dialog

This commit is contained in:
Sampo Niskanen 2009-06-21 18:24:33 +00:00
parent a65e061445
commit 6e14883374
26 changed files with 1136 additions and 405 deletions

View File

@ -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

View File

@ -1,4 +1,11 @@
OpenRocket 0.9.2 (future):
---------------------------
- a new and enhanced "Edit motor configurations" dialog
- a search field in the motor selection dialog
OpenRocket 0.9.1 (2009-06-09):
-------------------------------

131
TODO
View File

@ -1,126 +1,35 @@
GUI:
- Preferences dialog
Feature roadmap for OpenRocket 1.0
BUGS:
Must-have:
- 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:
Done:
- Automatic diameters of body components
- Copy/paste
- Search field in motor selection dialog
- Motor selection/editing from Edit configurations dialog
- Change FreeformFinSet to throw checked exceptions
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
18.5.:
- About dialog + version number

View File

@ -1,6 +1,6 @@
# The OpenRocket build version
build.version=0.9.1
build.version=0.9.2pre
# The source of the package. When building a package for a specific
# distribution (Debian, Fedora etc.), this should be changed appropriately!

View File

@ -45,6 +45,7 @@ public class OpenRocketDocument implements ComponentChangeListener {
private LinkedList<String> undoDescription = new LinkedList<String>();
private String nextDescription = null;
private String storedDescription = null;
private File file = null;
@ -70,9 +71,7 @@ public class OpenRocketDocument implements ComponentChangeListener {
this.configuration = configuration;
this.rocket = configuration.getRocket();
undoHistory.add(rocket.copy());
undoDescription.add(null);
undoPosition = 0;
clearUndo();
undoAction = new UndoRedoAction(UndoRedoAction.UNDO);
redoAction = new UndoRedoAction(UndoRedoAction.REDO);
@ -234,6 +233,31 @@ public class OpenRocketDocument implements ComponentChangeListener {
}
/**
* Start a time-limited undoable operation. After the operation {@link #stopUndo()}
* must be called, which will restore the previous undo description into effect.
* Only one level of start-stop undo descriptions is supported, i.e. start-stop
* undo cannot be nested, and no other undo operations may be called between
* the start and stop calls.
*
* @param description Description of the following undoable operations.
*/
public void startUndo(String description) {
storedDescription = nextDescription;
addUndoPosition(description);
}
/**
* End the previous time-limited undoable operation. This must be called after
* {@link #startUndo(String)} has been called before any other undo operations are
* performed.
*/
public void stopUndo() {
addUndoPosition(storedDescription);
storedDescription = null;
}
public Action getUndoAction() {
return undoAction;
}
@ -244,6 +268,24 @@ public class OpenRocketDocument implements ComponentChangeListener {
}
/**
* Clear the undo history.
*/
public void clearUndo() {
undoHistory.clear();
undoDescription.clear();
undoHistory.add(rocket.copy());
undoDescription.add(null);
undoPosition = 0;
if (undoAction != null)
undoAction.setAllValues();
if (redoAction != null)
redoAction.setAllValues();
}
@Override
public void componentChanged(ComponentChangeEvent e) {

View File

@ -29,6 +29,7 @@ import net.sf.openrocket.rocketcomponent.EngineBlock;
import net.sf.openrocket.rocketcomponent.ExternalComponent;
import net.sf.openrocket.rocketcomponent.FinSet;
import net.sf.openrocket.rocketcomponent.FreeformFinSet;
import net.sf.openrocket.rocketcomponent.IllegalFinPointException;
import net.sf.openrocket.rocketcomponent.InnerTube;
import net.sf.openrocket.rocketcomponent.InternalComponent;
import net.sf.openrocket.rocketcomponent.LaunchLug;
@ -137,6 +138,8 @@ public class OpenRocketLoader extends RocketLoader {
doc.getDefaultStorageOptions().setSimulationTimeSkip(timeSkip);
doc.getDefaultStorageOptions().setCompressionEnabled(false); // Set by caller if compressed
doc.getDefaultStorageOptions().setExplicitlySet(false);
doc.clearUndo();
return doc;
}
@ -977,7 +980,7 @@ class FinSetPointHandler extends ElementHandler {
String content, WarningSet warnings) {
try {
finset.setPoints(coordinates.toArray(new Coordinate[0]));
} catch (IllegalArgumentException e) {
} catch (IllegalFinPointException e) {
warnings.add(Warning.fromString("Freeform fin set point definitions illegal, ignoring."));
}
}

View File

@ -1,5 +1,6 @@
package net.sf.openrocket.file;
import java.awt.Component;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
@ -7,6 +8,8 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import javax.swing.ProgressMonitorInputStream;
import net.sf.openrocket.aerodynamics.WarningSet;
import net.sf.openrocket.document.OpenRocketDocument;
@ -14,6 +17,19 @@ import net.sf.openrocket.document.OpenRocketDocument;
public abstract class RocketLoader {
protected final WarningSet warnings = new WarningSet();
public final OpenRocketDocument load(File source, Component parent)
throws RocketLoadException {
warnings.clear();
try {
return load(new BufferedInputStream(new ProgressMonitorInputStream(
parent, "Loading " + source.getName(),
new FileInputStream(source))));
} catch (FileNotFoundException e) {
throw new RocketLoadException("File not found: " + source);
}
}
/**
* Loads a rocket from the specified File object.

View File

@ -1,44 +1,22 @@
package net.sf.openrocket.gui.adaptors;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.swing.ComboBoxModel;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.EventListenerList;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.gui.TextFieldListener;
import net.sf.openrocket.gui.components.ResizeLabel;
import net.sf.openrocket.gui.dialogs.EditMotorConfigurationDialog;
import net.sf.openrocket.gui.main.BasicFrame;
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.Motor;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.util.GUIUtil;
public class MotorConfigurationModel implements ComboBoxModel, ChangeListener {
@ -91,13 +69,8 @@ public class MotorConfigurationModel implements ComboBoxModel, ChangeListener {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
EditConfigurationDialog dialog = new EditConfigurationDialog();
dialog.setVisible(true);
if (dialog.isRowSelected()) {
rocket.getDefaultConfiguration().setMotorConfigurationID(
dialog.getSelectedID());
}
new EditMotorConfigurationDialog(rocket, BasicFrame.findFrame(rocket))
.setVisible(true);
}
});
@ -185,198 +158,5 @@ public class MotorConfigurationModel implements ComboBoxModel, ChangeListener {
}
}
private class EditConfigurationDialog extends JDialog {
private final ColumnTableModel tableModel;
private String[] ids;
int selection = -1;
private final JButton addButton;
private final JButton removeButton;
private final JTextField nameField;
private final JTable table;
public boolean isRowSelected() {
return selection >= 0;
}
public String getSelectedID() {
if (selection >= 0)
return ids[selection];
return null;
}
public EditConfigurationDialog() {
super((JFrame)null, "Edit configurations", true);
ids = rocket.getMotorConfigurationIDs();
// Create columns
ArrayList<Column> columnList = new ArrayList<Column>();
columnList.add(new Column("Name") {
@Override
public Object getValueAt(int row) {
return rocket.getMotorConfigurationNameOrDescription(ids[row]);
}
});
// Create columns from the motor mounts
Iterator<RocketComponent> iterator = rocket.deepIterator();
while (iterator.hasNext()) {
RocketComponent c = iterator.next();
if (!(c instanceof MotorMount))
continue;
final MotorMount mount = (MotorMount)c;
if (!mount.isMotorMount())
continue;
Column col = new Column(c.getName()) {
@Override
public Object getValueAt(int row) {
Motor motor = mount.getMotor(ids[row]);
if (motor == null)
return "";
return motor.getDesignation(mount.getMotorDelay(ids[row]));
}
};
columnList.add(col);
}
tableModel = new ColumnTableModel(columnList.toArray(new Column[0])) {
@Override
public int getRowCount() {
return ids.length;
}
};
// Create the panel
JPanel panel = new JPanel(new MigLayout("fill","[shrink][grow]"));
panel.add(new JLabel("Configuration name:"), "gapright para");
nameField = new JTextField();
new TextFieldListener() {
@Override
public void setText(String text) {
if (selection < 0 || ids[selection] == null)
return;
rocket.setMotorConfigurationName(ids[selection], text);
fireChange();
}
}.listenTo(nameField);
panel.add(nameField, "growx, wrap");
panel.add(new ResizeLabel("Leave empty for default description", -2),
"skip, growx, wrap para");
table = new JTable(tableModel);
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
updateSelection();
}
});
// Mouse listener to act on double-clicks
table.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) {
EditConfigurationDialog.this.dispose();
}
}
});
JScrollPane scrollpane = new JScrollPane(table);
panel.add(scrollpane, "spanx, height 150lp, width 400lp, grow, wrap");
addButton = new JButton("New");
addButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String id = rocket.newMotorConfigurationID();
ids = rocket.getMotorConfigurationIDs();
tableModel.fireTableDataChanged();
int sel;
for (sel=0; sel < ids.length; sel++) {
if (id.equals(ids[sel]))
break;
}
table.getSelectionModel().addSelectionInterval(sel, sel);
}
});
panel.add(addButton, "growx, spanx, split 2");
removeButton = new JButton("Remove");
removeButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int sel = table.getSelectedRow();
if (sel < 0 || sel >= ids.length || ids[sel] == null)
return;
rocket.removeMotorConfigurationID(ids[sel]);
ids = rocket.getMotorConfigurationIDs();
tableModel.fireTableDataChanged();
if (sel >= ids.length)
sel--;
table.getSelectionModel().addSelectionInterval(sel, sel);
}
});
panel.add(removeButton, "growx, wrap para");
JButton close = new JButton("Close");
close.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
EditConfigurationDialog.this.dispose();
}
});
panel.add(close, "spanx, alignx 100%");
this.getRootPane().setDefaultButton(close);
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
GUIUtil.installEscapeCloseOperation(this);
this.setLocationByPlatform(true);
updateSelection();
this.add(panel);
this.validate();
this.pack();
}
private void fireChange() {
int sel = table.getSelectedRow();
tableModel.fireTableDataChanged();
table.getSelectionModel().addSelectionInterval(sel, sel);
}
private void updateSelection() {
selection = table.getSelectedRow();
if (selection < 0 || ids[selection] == null) {
removeButton.setEnabled(false);
nameField.setEnabled(false);
nameField.setText("");
} else {
removeButton.setEnabled(true);
nameField.setEnabled(true);
nameField.setText(rocket.getMotorConfigurationName(ids[selection]));
}
}
}
}

View File

@ -30,6 +30,7 @@ import net.sf.openrocket.gui.scalefigure.ScaleSelector;
import net.sf.openrocket.material.Material;
import net.sf.openrocket.rocketcomponent.FinSet;
import net.sf.openrocket.rocketcomponent.FreeformFinSet;
import net.sf.openrocket.rocketcomponent.IllegalFinPointException;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.unit.UnitGroup;
import net.sf.openrocket.util.Coordinate;
@ -275,7 +276,7 @@ public class FreeformFinSetConfig extends FinSetConfig {
finset.addPoint(index);
try {
finset.setPoint(index, point.x, point.y);
} catch (IllegalArgumentException ignore) { }
} catch (IllegalFinPointException ignore) { }
dragIndex = index;
return;
@ -299,7 +300,7 @@ public class FreeformFinSetConfig extends FinSetConfig {
try {
finset.setPoint(dragIndex, point.x, point.y);
} catch (IllegalArgumentException ignore) {
} catch (IllegalFinPointException ignore) {
System.out.println("IAE:"+ignore);
}
}
@ -328,7 +329,7 @@ public class FreeformFinSetConfig extends FinSetConfig {
try {
finset.removePoint(index);
} catch (IllegalArgumentException ignore) {
} catch (IllegalFinPointException ignore) {
}
}
@ -462,6 +463,7 @@ public class FreeformFinSetConfig extends FinSetConfig {
finset.setPoint(rowIndex, c.x, c.y);
} catch (NumberFormatException ignore) {
} catch (IllegalFinPointException ignore) {
}
}

View File

@ -50,12 +50,12 @@ public class InnerTubeConfig extends ThicknessRingComponentConfig {
tab = positionTab();
tabbedPane.insertTab("Radial position", null, tab, "Radial position", 1);
tab = clusterTab();
tabbedPane.insertTab("Cluster", null, tab, "Cluster configuration", 2);
tab = new MotorConfig((MotorMount)c);
tabbedPane.insertTab("Motor", null, tab, "Motor mount configuration", 3);
tabbedPane.insertTab("Motor", null, tab, "Motor mount configuration", 2);
tab = clusterTab();
tabbedPane.insertTab("Cluster", null, tab, "Cluster configuration", 3);
tabbedPane.setSelectedIndex(0);
}

View File

@ -1,4 +1,4 @@
package net.sf.openrocket.gui;
package net.sf.openrocket.gui.dialogs;
import static net.sf.openrocket.unit.Unit.NOUNIT2;

View File

@ -1,4 +1,4 @@
package net.sf.openrocket.gui;
package net.sf.openrocket.gui.dialogs;
import java.awt.Component;

View File

@ -0,0 +1,479 @@
package net.sf.openrocket.gui.dialogs;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Iterator;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.gui.main.BasicFrame;
import net.sf.openrocket.gui.main.MotorChooserDialog;
import net.sf.openrocket.rocketcomponent.Motor;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.util.GUIUtil;
public class EditMotorConfigurationDialog extends JDialog {
private final Rocket rocket;
private final MotorMount[] mounts;
private final JTable configurationTable;
private final MotorConfigurationTableModel configurationTableModel;
private final JButton newConfButton, removeConfButton;
private final JButton selectMotorButton, removeMotorButton;
private final JTextField configurationNameField;
private String currentID = null;
private MotorMount currentMount = null;
public EditMotorConfigurationDialog(final Rocket rocket, Window parent) {
super(parent, "Edit motor configurations");
if (parent != null)
this.setModalityType(ModalityType.DOCUMENT_MODAL);
else
this.setModalityType(ModalityType.APPLICATION_MODAL);
this.rocket = rocket;
ArrayList<MotorMount> mountList = new ArrayList<MotorMount>();
Iterator<RocketComponent> iterator = rocket.deepIterator();
while (iterator.hasNext()) {
RocketComponent c = iterator.next();
if (c instanceof MotorMount) {
mountList.add((MotorMount)c);
}
}
mounts = mountList.toArray(new MotorMount[0]);
JPanel panel = new JPanel(new MigLayout("fill, flowy"));
//// Motor mount selection
JLabel label = new JLabel("<html><b>Motor mounts:</b>");
panel.add(label, "gapbottom para");
label = new JLabel("<html>Select which components function as motor mounts:");
panel.add(label,"ay 100%, w 1px, growx");
JTable table = new JTable(new MotorMountTableModel());
table.setTableHeader(null);
table.setShowVerticalLines(false);
table.setRowSelectionAllowed(false);
table.setColumnSelectionAllowed(false);
TableColumnModel columnModel = table.getColumnModel();
TableColumn col0 = columnModel.getColumn(0);
int w = table.getRowHeight() + 2;
col0.setMinWidth(w);
col0.setPreferredWidth(w);
col0.setMaxWidth(w);
JScrollPane scroll = new JScrollPane(table);
panel.add(scroll, "w 200lp, h 150lp, grow, wrap 20lp");
//// Motor selection
label = new JLabel("<html><b>Motor configurations:</b>");
panel.add(label, "spanx, gapbottom para");
label = new JLabel("Configuration name:");
String tip = "Leave name empty for default.";
label.setToolTipText(tip);
panel.add(label, "");
configurationNameField = new JTextField(10);
configurationNameField.setToolTipText(tip);
configurationNameField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void changedUpdate(DocumentEvent e) {
update();
}
@Override
public void insertUpdate(DocumentEvent e) {
update();
}
@Override
public void removeUpdate(DocumentEvent e) {
update();
}
private void update() {
String text = configurationNameField.getText();
if (currentID != null) {
rocket.setMotorConfigurationName(currentID, text);
int row = configurationTable.getSelectedRow();
configurationTableModel.fireTableCellUpdated(row, 0);
updateEnabled();
}
}
});
panel.add(configurationNameField, "cell 2 1, gapright para");
newConfButton = new JButton("New configuration");
newConfButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String id = rocket.newMotorConfigurationID();
rocket.getDefaultConfiguration().setMotorConfigurationID(id);
configurationTableModel.fireTableDataChanged();
updateEnabled();
}
});
panel.add(newConfButton, "cell 3 1");
removeConfButton = new JButton("Remove configuration");
removeConfButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (currentID == null)
return;
rocket.removeMotorConfigurationID(currentID);
rocket.getDefaultConfiguration().setMotorConfigurationID(null);
configurationTableModel.fireTableDataChanged();
updateEnabled();
}
});
panel.add(removeConfButton, "cell 4 1");
configurationTableModel = new MotorConfigurationTableModel();
configurationTable = new JTable(configurationTableModel);
configurationTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
configurationTable.setCellSelectionEnabled(true);
configurationTable.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 1) {
// Single click updates selection
updateEnabled();
} else if (e.getClickCount() == 2) {
// Double-click edits motor
selectMotor();
}
}
});
scroll = new JScrollPane(configurationTable);
panel.add(scroll, "cell 1 2, spanx, w 500lp, h 150lp, grow");
selectMotorButton = new JButton("Select motor");
selectMotorButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
selectMotor();
}
});
panel.add(selectMotorButton, "spanx, flowx, split 2, ax 50%");
removeMotorButton = new JButton("Remove motor");
removeMotorButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
removeMotor();
}
});
panel.add(removeMotorButton, "ax 50%");
//// Close button
JButton close = new JButton("Close");
close.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
EditMotorConfigurationDialog.this.dispose();
}
});
panel.add(close, "spanx, right");
this.add(panel);
this.validate();
this.pack();
updateEnabled();
GUIUtil.installEscapeCloseOperation(this);
GUIUtil.setDefaultButton(close);
// Undo description
final OpenRocketDocument document = BasicFrame.findDocument(rocket);
if (document != null) {
document.startUndo("Edit motor configurations");
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
document.stopUndo();
}
});
}
}
private void updateEnabled() {
int column = configurationTable.getSelectedColumn();
int row = configurationTable.getSelectedRow();
if (column < 0 || row < 0) {
currentID = null;
currentMount = null;
} else {
currentID = findID(row);
if (column == 0) {
currentMount = null;
} else {
currentMount = findMount(column);
}
rocket.getDefaultConfiguration().setMotorConfigurationID(currentID);
}
configurationNameField.setEnabled(currentID != null);
if (currentID == null) {
configurationNameField.setText("");
} else {
configurationNameField.setText(rocket.getMotorConfigurationName(currentID));
}
removeConfButton.setEnabled(currentID != null);
selectMotorButton.setEnabled(currentMount != null && currentID != null);
removeMotorButton.setEnabled(currentMount != null && currentID != null);
}
private void selectMotor() {
if (currentID == null || currentMount == null)
return;
MotorChooserDialog dialog = new MotorChooserDialog(currentMount.getMotor(currentID),
currentMount.getMotorDelay(currentID), currentMount.getMotorMountDiameter());
dialog.setVisible(true);
Motor m = dialog.getSelectedMotor();
double d = dialog.getSelectedDelay();
if (m != null) {
currentMount.setMotor(currentID, m);
currentMount.setMotorDelay(currentID, d);
}
int row = configurationTable.getSelectedRow();
configurationTableModel.fireTableRowsUpdated(row, row);
updateEnabled();
}
private void removeMotor() {
if (currentID == null || currentMount == null)
return;
currentMount.setMotor(currentID, null);
int row = configurationTable.getSelectedRow();
configurationTableModel.fireTableRowsUpdated(row, row);
updateEnabled();
}
private String findID(int row) {
return rocket.getMotorConfigurationIDs()[row+1];
}
private MotorMount findMount(int column) {
MotorMount mount = null;
int count = column;
for (MotorMount m: mounts) {
if (m.isMotorMount())
count--;
if (count <= 0) {
mount = m;
break;
}
}
if (mount == null) {
throw new IndexOutOfBoundsException("motor mount not found, column="+column);
}
return mount;
}
/**
* The table model for selecting whether components are motor mounts or not.
*/
private class MotorMountTableModel extends AbstractTableModel {
@Override
public int getColumnCount() {
return 2;
}
@Override
public int getRowCount() {
return mounts.length;
}
@Override
public Class<?> getColumnClass(int column) {
switch (column) {
case 0:
return Boolean.class;
case 1:
return String.class;
default:
throw new IndexOutOfBoundsException("column="+column);
}
}
@Override
public Object getValueAt(int row, int column) {
switch (column) {
case 0:
return new Boolean(mounts[row].isMotorMount());
case 1:
return mounts[row].toString();
default:
throw new IndexOutOfBoundsException("column="+column);
}
}
@Override
public boolean isCellEditable(int row, int column) {
return column == 0;
}
@Override
public void setValueAt(Object value, int row, int column) {
if (column != 0 || !(value instanceof Boolean)) {
throw new IllegalArgumentException("column="+column+", value="+value);
}
mounts[row].setMotorMount((Boolean)value);
configurationTableModel.fireTableStructureChanged();
updateEnabled();
}
}
/**
* The table model for selecting and editing the motor configurations.
*/
private class MotorConfigurationTableModel extends AbstractTableModel {
@Override
public int getColumnCount() {
int count = 1;
for (MotorMount m: mounts) {
if (m.isMotorMount())
count++;
}
return count;
}
@Override
public int getRowCount() {
return rocket.getMotorConfigurationIDs().length-1;
}
@Override
public Object getValueAt(int row, int column) {
String id = findID(row);
if (column == 0) {
return rocket.getMotorConfigurationNameOrDescription(id);
}
MotorMount mount = findMount(column);
Motor motor = mount.getMotor(id);
if (motor == null)
return "None";
String str = motor.getDesignation(mount.getMotorDelay(id));
int count = mount.getMotorCount();
if (count > 1) {
str = "" + count + "\u00d7 " + str;
}
return str;
}
@Override
public String getColumnName(int column) {
if (column == 0) {
return "Configuration name";
}
MotorMount mount = findMount(column);
String name = mount.toString();
int count = mount.getMotorCount();
if (count > 1) {
name = name + " (\u00d7" + count + ")";
}
return name;
}
}
}

View File

@ -1,4 +1,4 @@
package net.sf.openrocket.gui.main;
package net.sf.openrocket.gui.dialogs;
import java.awt.Font;
import java.awt.event.ActionEvent;

View File

@ -1,4 +1,4 @@
package net.sf.openrocket.gui;
package net.sf.openrocket.gui.dialogs;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

View File

@ -0,0 +1,118 @@
package net.sf.openrocket.gui.dialogs;
import java.awt.Window;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.util.Pair;
/**
* A modal dialog that runs specific SwingWorkers and waits until they complete.
* A message and progress bar is provided and a cancel button. If the cancel button
* is pressed, the currently running worker is interrupted and the later workers are not
* executed.
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class SwingWorkerDialog extends JDialog implements PropertyChangeListener {
private final JLabel label;
private final JProgressBar progressBar;
private int position;
private Pair<String, SwingWorker<?,?>>[] workers;
private boolean cancelled = false;
public SwingWorkerDialog(Window parent, String title) {
super(parent, title, ModalityType.APPLICATION_MODAL);
JPanel panel = new JPanel(new MigLayout("fill"));
label = new JLabel("");
panel.add(label, "wrap para");
progressBar = new JProgressBar();
panel.add(progressBar, "growx, wrap para");
JButton cancel = new JButton("Cancel");
// TODO: CRITICAL: Implement cancel
panel.add(cancel, "right");
this.add(panel);
this.pack();
this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
}
/**
* Execute the provided workers one after another. When this call returns
* the workers will all have completed.
*
* @param workers pairs of description texts and workers to run.
*/
public void runWorkers(Pair<String, SwingWorker<?,?>> ... workers) {
if (workers.length == 0) {
throw new IllegalArgumentException("No workers provided.");
}
this.workers = workers;
position = -1;
for (int i=0; i < workers.length; i++) {
workers[i].getV().addPropertyChangeListener(this);
}
nextWorker();
this.setVisible(true); // Waits until all have ended
}
/**
* Starts the execution of the next worker in the queue. If the last worker
* has completed or the operation has been cancelled, closes the dialog.
*/
private void nextWorker() {
if ((position >= workers.length-1) || cancelled) {
close();
return;
}
position++;
label.setText(workers[position].getU());
workers[position].getV().execute();
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (workers[position].getV().getState() == SwingWorker.StateValue.DONE) {
nextWorker();
}
int value = workers[position].getV().getProgress();
value = (value + position*100 ) / workers.length;
progressBar.setValue(value);
}
private void close() {
for (int i=0; i < workers.length; i++) {
workers[i].getV().removePropertyChangeListener(this);
}
this.setVisible(false);
}
}

View File

@ -1,5 +1,6 @@
package net.sf.openrocket.gui.main;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Toolkit;
@ -14,6 +15,7 @@ import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
@ -38,6 +40,7 @@ import javax.swing.ListSelectionModel;
import javax.swing.LookAndFeel;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.ToolTipManager;
import javax.swing.UIManager;
import javax.swing.border.TitledBorder;
@ -56,17 +59,20 @@ import net.sf.openrocket.file.OpenRocketSaver;
import net.sf.openrocket.file.RocketLoadException;
import net.sf.openrocket.file.RocketLoader;
import net.sf.openrocket.file.RocketSaver;
import net.sf.openrocket.gui.ComponentAnalysisDialog;
import net.sf.openrocket.gui.PreferencesDialog;
import net.sf.openrocket.gui.StorageOptionChooser;
import net.sf.openrocket.gui.configdialog.ComponentConfigDialog;
import net.sf.openrocket.gui.dialogs.BugDialog;
import net.sf.openrocket.gui.dialogs.ComponentAnalysisDialog;
import net.sf.openrocket.gui.dialogs.LicenseDialog;
import net.sf.openrocket.gui.dialogs.PreferencesDialog;
import net.sf.openrocket.gui.scalefigure.RocketPanel;
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.Stage;
import net.sf.openrocket.util.ConcurrentProgressMonitor;
import net.sf.openrocket.util.ConcurrentProgressMonitorInputStream;
import net.sf.openrocket.util.Icons;
import net.sf.openrocket.util.Prefs;
@ -588,7 +594,7 @@ public class BasicFrame extends JFrame {
for (File file: files) {
System.out.println("Opening file: " + file);
if (open(file)) {
if (open(file, this)) {
opened = true;
}
}
@ -604,13 +610,16 @@ public class BasicFrame extends JFrame {
* Open the specified file in a new design frame. If an error occurs, an error dialog
* is shown and <code>false</code> is returned.
*
* @param file the file to open.
* @return whether the file was successfully loaded and opened.
* @param file the file to open.
* @param parent the parent component for which a progress dialog is opened.
* @return whether the file was successfully loaded and opened.
*/
private static boolean open(File file) {
private static boolean open(File file, Component parent) {
OpenRocketDocument doc = null;
try {
doc = ROCKET_LOADER.load(file);
doc = ROCKET_LOADER.load(file, parent);
} catch (RocketLoadException e) {
JOptionPane.showMessageDialog(null, "Unable to open file '" + file.getName()
+"': " + e.getMessage(), "Error opening file", JOptionPane.ERROR_MESSAGE);
@ -643,6 +652,33 @@ public class BasicFrame extends JFrame {
private static class OpenWorker extends SwingWorker<OpenRocketDocument, Void> {
private final File file;
private final Component parent;
private ConcurrentProgressMonitor monitor = null;
public OpenWorker(File file, Component parent) {
this.file = file;
this.parent = parent;
}
@Override
protected OpenRocketDocument doInBackground() throws Exception {
ConcurrentProgressMonitorInputStream is =
new ConcurrentProgressMonitorInputStream(parent,
"Loading " + file.getName(), new FileInputStream(file));
monitor = is.getProgressMonitor();
return ROCKET_LOADER.load(is);
}
public ConcurrentProgressMonitor getMonitor() {
return monitor;
}
}
private boolean saveAction() {
File file = document.getFile();
if (file==null) {
@ -804,6 +840,35 @@ public class BasicFrame extends JFrame {
/**
* Find a currently open BasicFrame containing the specified rocket. This method
* can be used to map a Rocket to a BasicFrame from GUI methods.
*
* @param rocket the Rocket.
* @return the corresponding BasicFrame, or <code>null</code> if none found.
*/
public static BasicFrame findFrame(Rocket rocket) {
for (BasicFrame f: frames) {
if (f.rocket == rocket)
return f;
}
return null;
}
/**
* Find a currently open document by the rocket object. This method can be used
* to map a Rocket to OpenRocketDocument from GUI methods.
*
* @param rocket the Rocket.
* @return the corresponding OpenRocketDocument, or <code>null</code> if not found.
*/
public static OpenRocketDocument findDocument(Rocket rocket) {
for (BasicFrame f: frames) {
if (f.rocket == rocket)
return f.document;
}
return null;
}
@ -861,7 +926,7 @@ public class BasicFrame extends JFrame {
// Check command-line for files
boolean opened = false;
for (String file: args) {
if (open(new File(file))) {
if (open(new File(file), null)) {
opened = true;
}
}

View File

@ -9,6 +9,7 @@ import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Locale;
import java.util.regex.Matcher;
@ -22,8 +23,11 @@ import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.RowFilter;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
@ -49,7 +53,9 @@ public class MotorChooserDialog extends JDialog {
"Show motors with diameter equal to that of the motor mount"
};
private static final int SHOW_MAX = 2;
private final JTextField searchField;
private String[] searchTerms = new String[0];
private final double diameter;
@ -81,16 +87,16 @@ public class MotorChooserDialog extends JDialog {
this.selectedDelay = delay;
this.diameter = diameter;
JPanel panel = new JPanel(new MigLayout("fill"));
JPanel panel = new JPanel(new MigLayout("fill", "[grow][]"));
// Label
JLabel label = new JLabel("Select a rocket motor:");
label.setFont(label.getFont().deriveFont(Font.BOLD));
panel.add(label,"split 2, growx");
panel.add(label,"growx");
label = new JLabel("Motor mount diameter: " +
UnitGroup.UNITS_MOTOR_DIMENSIONS.getDefaultUnit().toStringUnit(diameter));
panel.add(label,"alignx 100%, wrap paragraph");
panel.add(label,"gapleft para, wrap paragraph");
// Diameter selection
@ -104,17 +110,14 @@ public class MotorChooserDialog extends JDialog {
sel = SHOW_ALL;
switch (sel) {
case SHOW_ALL:
System.out.println("Setting filter: all");
sorter.setRowFilter(new MotorRowFilterAll());
break;
case SHOW_SMALLER:
System.out.println("Setting filter: smaller");
sorter.setRowFilter(new MotorRowFilterSmaller());
break;
case SHOW_EXACT:
System.out.println("Setting filter: exact");
sorter.setRowFilter(new MotorRowFilterExact());
break;
@ -125,9 +128,46 @@ public class MotorChooserDialog extends JDialog {
setSelectionVisible();
}
});
panel.add(combo,"growx, wrap");
panel.add(combo,"growx 1000");
label = new JLabel("Search:");
panel.add(label, "gapleft para, split 2");
searchField = new JTextField();
searchField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void changedUpdate(DocumentEvent e) {
update();
}
@Override
public void insertUpdate(DocumentEvent e) {
update();
}
@Override
public void removeUpdate(DocumentEvent e) {
update();
}
private void update() {
String text = searchField.getText().trim();
String[] split = text.split("\\s+");
ArrayList<String> list = new ArrayList<String>();
for (String s: split) {
s = s.trim().toLowerCase();
if (s.length() > 0) {
list.add(s);
}
}
searchTerms = list.toArray(new String[0]);
sorter.sort();
}
});
panel.add(searchField, "growx 1, wrap");
// Table, overridden to show meaningful tooltip texts
model = new MotorDatabaseModel(current);
table = new JTable(model) {
@ -186,11 +226,11 @@ public class MotorChooserDialog extends JDialog {
JScrollPane scrollpane = new JScrollPane();
scrollpane.setViewportView(table);
panel.add(scrollpane,"grow, width :700:, height :300:, wrap paragraph");
panel.add(scrollpane,"spanx, grow, width :700:, height :300:, wrap paragraph");
// Ejection delay
panel.add(new JLabel("Select ejection charge delay:"), "split 3, gap rel");
panel.add(new JLabel("Select ejection charge delay:"), "spanx, split 3, gap rel");
delayBox = new JComboBox();
delayBox.setEditable(true);
@ -222,7 +262,7 @@ public class MotorChooserDialog extends JDialog {
MotorChooserDialog.this.setVisible(false);
}
});
panel.add(okButton,"split, tag ok");
panel.add(okButton,"spanx, split, tag ok");
button = new JButton("Cancel");
button.addActionListener(new ActionListener() {
@ -248,6 +288,9 @@ public class MotorChooserDialog extends JDialog {
// Table can be scrolled only after pack() has been called
setSelectionVisible();
// Focus the search field
searchField.grabFocus();
}
private void setSelectionVisible() {
@ -567,14 +610,26 @@ public class MotorChooserDialog extends JDialog {
*/
private abstract class MotorRowFilter extends RowFilter<TableModel,Integer> {
@Override
public boolean include(
RowFilter.Entry<? extends TableModel, ? extends Integer> entry) {
public boolean include(RowFilter.Entry<? extends TableModel, ? extends Integer> entry) {
int index = entry.getIdentifier();
Motor m = model.getMotor(index);
return include(m);
return filterByDiameter(m) && filterByString(m);
}
public abstract boolean include(Motor m);
public abstract boolean filterByDiameter(Motor m);
public boolean filterByString(Motor m) {
main: for (String s : searchTerms) {
for (MotorColumns col : MotorColumns.values()) {
String str = col.getValue(m).toLowerCase();
if (str.indexOf(s) >= 0)
continue main;
}
return false;
}
return true;
}
}
/**
@ -582,7 +637,7 @@ public class MotorChooserDialog extends JDialog {
*/
private class MotorRowFilterAll extends MotorRowFilter {
@Override
public boolean include(Motor m) {
public boolean filterByDiameter(Motor m) {
return true;
}
}
@ -592,7 +647,7 @@ public class MotorChooserDialog extends JDialog {
*/
private class MotorRowFilterSmaller extends MotorRowFilter {
@Override
public boolean include(Motor m) {
public boolean filterByDiameter(Motor m) {
return (m.getDiameter() <= diameter + 0.0004);
}
}
@ -602,7 +657,7 @@ public class MotorChooserDialog extends JDialog {
*/
private class MotorRowFilterExact extends MotorRowFilter {
@Override
public boolean include(Motor m) {
public boolean filterByDiameter(Motor m) {
return ((m.getDiameter() <= diameter + 0.0004) &&
(m.getDiameter() >= diameter - 0.0015));
}

View File

@ -25,7 +25,7 @@ import javax.swing.JProgressBar;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.gui.DetailDialog;
import net.sf.openrocket.gui.dialogs.DetailDialog;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.MotorMount.IgnitionEvent;

View File

@ -56,9 +56,11 @@ public class ClusterConfiguration {
};
private final List<Double> points;
private final String xmlName;
private ClusterConfiguration(String xmlName, double... points) {
this.xmlName = xmlName;
if (points.length == 0 || points.length%2 == 1) {
@ -100,4 +102,10 @@ public class ClusterConfiguration {
}
return ret;
}
@Override
public String toString() {
return xmlName;
}
}

View File

@ -59,10 +59,11 @@ public class FreeformFinSet extends FinSet {
* if attempted.
*
* @param index the fin point index to remove
* @throws IllegalFinPointException if removing the first or last fin point was attempted.
*/
public void removePoint(int index) {
public void removePoint(int index) throws IllegalFinPointException {
if (index == 0 || index == points.size()-1) {
throw new IllegalArgumentException("cannot remove first or last point");
throw new IllegalFinPointException("cannot remove first or last point");
}
points.remove(index);
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
@ -73,19 +74,19 @@ public class FreeformFinSet extends FinSet {
return points.size();
}
public void setPoints(Coordinate[] p) {
public void setPoints(Coordinate[] p) throws IllegalFinPointException {
if (p[0].x != 0 || p[0].y != 0 || p[p.length-1].y != 0) {
throw new IllegalArgumentException("Start or end point illegal.");
throw new IllegalFinPointException("Start or end point illegal.");
}
for (int i=0; i < p.length-1; i++) {
for (int j=i+2; j < p.length-1; j++) {
if (intersects(p[i].x, p[i].y, p[i+1].x, p[i+1].y,
p[j].x, p[j].y, p[j+1].x, p[j+1].y)) {
throw new IllegalArgumentException("segments intersect");
throw new IllegalFinPointException("segments intersect");
}
}
if (p[i].z != 0) {
throw new IllegalArgumentException("z-coordinate not zero");
throw new IllegalFinPointException("z-coordinate not zero");
}
}
@ -112,8 +113,10 @@ public class FreeformFinSet extends FinSet {
* @param index the point index to modify.
* @param x the x-coordinate.
* @param y the y-coordinate.
* @throws IllegalFinPointException if the specified fin point would cause intersecting
* segments
*/
public void setPoint(int index, double x, double y) {
public void setPoint(int index, double x, double y) throws IllegalFinPointException {
if (y < 0)
y = 0;
@ -160,12 +163,12 @@ public class FreeformFinSet extends FinSet {
if (i != index-1 && i != index && i != index+1) {
if (intersects(x0,y0,x,y,px0,py0,px1,py1)) {
throw new IllegalArgumentException("segments intersect");
throw new IllegalFinPointException("segments intersect");
}
}
if (i != index && i != index+1 && i != index+2) {
if (intersects(x,y,x1,y1,px0,py0,px1,py1)) {
throw new IllegalArgumentException("segments intersect");
throw new IllegalFinPointException("segments intersect");
}
}

View File

@ -0,0 +1,27 @@
package net.sf.openrocket.rocketcomponent;
/**
* An exception signifying that an operation on the freeform fin set points was
* illegal (segments intersect, removing first or last point, etc).
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class IllegalFinPointException extends Exception {
public IllegalFinPointException() {
}
public IllegalFinPointException(String message) {
super(message);
}
public IllegalFinPointException(Throwable cause) {
super(cause);
}
public IllegalFinPointException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -7,7 +7,6 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.swing.event.ChangeListener;
@ -72,8 +71,8 @@ public class Rocket extends RocketComponent {
// Motor configuration list
private List<String> motorConfigurationIDs = new ArrayList<String>();
private Map<String, String> motorConfigurationNames = new HashMap<String, String>();
private ArrayList<String> motorConfigurationIDs = new ArrayList<String>();
private HashMap<String, String> motorConfigurationNames = new HashMap<String, String>();
{
motorConfigurationIDs.add(null);
}
@ -267,22 +266,25 @@ public class Rocket extends RocketComponent {
}
/**
* Make a deep copy of the Rocket structure. This is a helper method which simply
* casts the result of the superclass method to a Rocket.
*/
@SuppressWarnings("unchecked")
@Override
public Rocket copy() {
Rocket copy = (Rocket)super.copy();
copy.motorConfigurationIDs = (ArrayList<String>) this.motorConfigurationIDs.clone();
copy.motorConfigurationNames =
(HashMap<String, String>) this.motorConfigurationNames.clone();
copy.resetListeners();
return copy;
}
/**
* Load the rocket structure from the source. The method loads the fields of this
* Rocket object and copies the references to siblings from the <code>source</code>.
@ -293,6 +295,7 @@ public class Rocket extends RocketComponent {
* and therefore fires an UNDO_EVENT, masked with all applicable mass/aerodynamic/tree
* changes.
*/
@SuppressWarnings("unchecked")
public void loadFrom(Rocket r) {
super.copyFrom(r);
@ -312,10 +315,15 @@ public class Rocket extends RocketComponent {
this.refType = r.refType;
this.customReferenceLength = r.customReferenceLength;
this.motorConfigurationIDs = r.motorConfigurationIDs;
this.motorConfigurationNames = r.motorConfigurationNames;
this.motorConfigurationIDs = (ArrayList<String>) r.motorConfigurationIDs.clone();
this.motorConfigurationNames =
(HashMap<String, String>) r.motorConfigurationNames.clone();
this.perfectFinish = r.perfectFinish;
String id = defaultConfiguration.getMotorConfigurationID();
if (!this.motorConfigurationIDs.contains(id))
defaultConfiguration.setMotorConfigurationID(null);
fireComponentChangeEvent(type);
}
@ -594,7 +602,7 @@ public class Rocket extends RocketComponent {
*/
public void setMotorConfigurationName(String id, String name) {
motorConfigurationNames.put(id,name);
fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
}

View File

@ -0,0 +1,45 @@
package net.sf.openrocket.util;
import java.awt.Component;
import javax.swing.ProgressMonitor;
import javax.swing.SwingUtilities;
/**
* A thread-safe <code>ProgressMonitor</code>. This class may be instantiated
* and the method {@link #setProgress(int)} called safely from any thread.
* <p>
* Why the FSCK&!¤#&%¤ isn't the default API version thread-safe?!?!
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class ConcurrentProgressMonitor extends ProgressMonitor {
public ConcurrentProgressMonitor(Component parentComponent, Object message,
String note, int min, int max) {
super(parentComponent, message, note, min, max);
}
@Override
public void setProgress(final int nv) {
if (SwingUtilities.isEventDispatchThread()) {
super.setProgress(nv);
} else {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
ConcurrentProgressMonitor.super.setProgress(nv);
}
});
}
}
}

View File

@ -0,0 +1,146 @@
/*
* TODO: CRITICAL: Licensing
*/
package net.sf.openrocket.util;
import java.awt.Component;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
/**
* A functional equivalent of <code>ProgressMonitorInputStream</code> which
* uses {@link ConcurrentProgressMonitor} and leaves the progress dialog open
* to be manually closed later on.
*/
public class ConcurrentProgressMonitorInputStream extends FilterInputStream {
private ConcurrentProgressMonitor monitor;
private int nread = 0;
private int size = 0;
/**
* Constructs an object to monitor the progress of an input stream.
*
* @param message Descriptive text to be placed in the dialog box
* if one is popped up.
* @param parentComponent The component triggering the operation
* being monitored.
* @param in The input stream to be monitored.
*/
public ConcurrentProgressMonitorInputStream(Component parentComponent,
Object message, InputStream in) {
super(in);
try {
size = in.available();
} catch (IOException ioe) {
size = 0;
}
monitor = new ConcurrentProgressMonitor(parentComponent, message, null, 0,
size + 1);
}
/**
* Get the ProgressMonitor object being used by this stream. Normally
* this isn't needed unless you want to do something like change the
* descriptive text partway through reading the file.
* @return the ProgressMonitor object used by this object
*/
public ConcurrentProgressMonitor getProgressMonitor() {
return monitor;
}
/**
* Overrides <code>FilterInputStream.read</code>
* to update the progress monitor after the read.
*/
@Override
public int read() throws IOException {
int c = in.read();
if (c >= 0)
monitor.setProgress(++nread);
if (monitor.isCanceled()) {
InterruptedIOException exc = new InterruptedIOException("progress");
exc.bytesTransferred = nread;
throw exc;
}
return c;
}
/**
* Overrides <code>FilterInputStream.read</code>
* to update the progress monitor after the read.
*/
@Override
public int read(byte b[]) throws IOException {
int nr = in.read(b);
if (nr > 0)
monitor.setProgress(nread += nr);
if (monitor.isCanceled()) {
InterruptedIOException exc = new InterruptedIOException("progress");
exc.bytesTransferred = nread;
throw exc;
}
return nr;
}
/**
* Overrides <code>FilterInputStream.read</code>
* to update the progress monitor after the read.
*/
@Override
public int read(byte b[], int off, int len) throws IOException {
int nr = in.read(b, off, len);
if (nr > 0)
monitor.setProgress(nread += nr);
if (monitor.isCanceled()) {
InterruptedIOException exc = new InterruptedIOException("progress");
exc.bytesTransferred = nread;
throw exc;
}
return nr;
}
/**
* Overrides <code>FilterInputStream.skip</code>
* to update the progress monitor after the skip.
*/
@Override
public long skip(long n) throws IOException {
long nr = in.skip(n);
if (nr > 0)
monitor.setProgress(nread += nr);
return nr;
}
/**
* Overrides <code>FilterInputStream.close</code>
* to close the progress monitor as well as the stream.
*/
@Override
public void close() throws IOException {
in.close();
}
/**
* Overrides <code>FilterInputStream.reset</code>
* to reset the progress monitor as well as the stream.
*/
@Override
public synchronized void reset() throws IOException {
in.reset();
nread = size - in.available();
monitor.setProgress(nread);
}
}

View File

@ -6,6 +6,7 @@ import net.sf.openrocket.rocketcomponent.BodyTube;
import net.sf.openrocket.rocketcomponent.Bulkhead;
import net.sf.openrocket.rocketcomponent.CenteringRing;
import net.sf.openrocket.rocketcomponent.FreeformFinSet;
import net.sf.openrocket.rocketcomponent.IllegalFinPointException;
import net.sf.openrocket.rocketcomponent.InnerTube;
import net.sf.openrocket.rocketcomponent.LaunchLug;
import net.sf.openrocket.rocketcomponent.MassComponent;
@ -144,13 +145,17 @@ public class Test {
bodytube = new BodyTube(0.69,0.033,0.001);
finset = new FreeformFinSet();
finset.setPoints(new Coordinate[] {
new Coordinate(0, 0),
new Coordinate(0.115, 0.072),
new Coordinate(0.255, 0.072),
new Coordinate(0.255, 0.037),
new Coordinate(0.150, 0)
});
try {
finset.setPoints(new Coordinate[] {
new Coordinate(0, 0),
new Coordinate(0.115, 0.072),
new Coordinate(0.255, 0.072),
new Coordinate(0.255, 0.037),
new Coordinate(0.150, 0)
});
} catch (IllegalFinPointException e) {
e.printStackTrace();
}
finset.setThickness(0.003);
finset.setFinCount(4);