diff --git a/core/src/net/sf/openrocket/formatting/MotorDescriptionSubstitutor.java b/core/src/net/sf/openrocket/formatting/MotorDescriptionSubstitutor.java new file mode 100644 index 000000000..7b4f69924 --- /dev/null +++ b/core/src/net/sf/openrocket/formatting/MotorDescriptionSubstitutor.java @@ -0,0 +1,157 @@ +package net.sf.openrocket.formatting; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.plugin.Plugin; +import net.sf.openrocket.rocketcomponent.MotorMount; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.Stage; +import net.sf.openrocket.util.ArrayList; +import net.sf.openrocket.util.Chars; + +import com.google.inject.Inject; + +@Plugin +public class MotorDescriptionSubstitutor implements RocketSubstitutor { + public static final String SUBSTITUTION = "{motors}"; + + @Inject + private Translator trans; + + @Override + public boolean containsSubstitution(String str) { + return str.contains(SUBSTITUTION); + } + + @Override + public String substitute(String str, Rocket rocket, String configId) { + String description = getFlightConfigurationDescription(rocket, configId); + return str.replaceAll(SUBSTITUTION, description); + } + + @Override + public Map getDescriptions() { + Map desc = new HashMap(); + desc.put(SUBSTITUTION, trans.get("description")); + return null; + } + + + + private String getFlightConfigurationDescription(Rocket rocket, String id) { + String name; + int motorCount = 0; + + // Generate the description + + // First iterate over each stage and store the designations of each motor + List> list = new ArrayList>(); + List currentList = Collections.emptyList(); + + Iterator iterator = rocket.iterator(); + while (iterator.hasNext()) { + RocketComponent c = iterator.next(); + + if (c instanceof Stage) { + + currentList = new ArrayList(); + list.add(currentList); + + } else if (c instanceof MotorMount) { + + MotorMount mount = (MotorMount) c; + Motor motor = mount.getMotor(id); + + if (mount.isMotorMount() && motor != null) { + String designation = motor.getDesignation(mount.getMotorDelay(id)); + + for (int i = 0; i < mount.getMotorCount(); i++) { + currentList.add(designation); + motorCount++; + } + } + + } + } + + if (motorCount == 0) { + //// [No motors] + return trans.get("Rocket.motorCount.Nomotor"); + } + + // Change multiple occurrences of a motor to n x motor + List stages = new ArrayList(); + + for (List stage : list) { + String stageName = ""; + String previous = null; + int count = 0; + + Collections.sort(stage); + for (String current : stage) { + if (current.equals(previous)) { + + count++; + + } else { + + if (previous != null) { + String s = ""; + if (count > 1) { + s = "" + count + Chars.TIMES + previous; + } else { + s = previous; + } + + if (stageName.equals("")) + stageName = s; + else + stageName = stageName + "," + s; + } + + previous = current; + count = 1; + + } + } + if (previous != null) { + String s = ""; + if (count > 1) { + s = "" + count + Chars.TIMES + previous; + } else { + s = previous; + } + + if (stageName.equals("")) + stageName = s; + else + stageName = stageName + "," + s; + } + + stages.add(stageName); + } + + name = "["; + for (int i = 0; i < stages.size(); i++) { + String s = stages.get(i); + if (s.equals("")) + s = "None"; + if (i == 0) + name = name + s; + else + name = name + "; " + s; + } + name += "]"; + return name; + } + + + +} diff --git a/core/src/net/sf/openrocket/formatting/RocketFormatter.java b/core/src/net/sf/openrocket/formatting/RocketFormatter.java new file mode 100644 index 000000000..b2b43a04d --- /dev/null +++ b/core/src/net/sf/openrocket/formatting/RocketFormatter.java @@ -0,0 +1,17 @@ +package net.sf.openrocket.formatting; + +import net.sf.openrocket.rocketcomponent.Rocket; + +/** + * Interface for formatting a flight configuration into a + * textual string. + */ +public interface RocketFormatter { + + /** + * Return a string describing a particular flight configuration + * of the rocket. + */ + public String format(Rocket rocket, String configId); + +} diff --git a/core/src/net/sf/openrocket/formatting/RocketFormatterImpl.java b/core/src/net/sf/openrocket/formatting/RocketFormatterImpl.java new file mode 100644 index 000000000..f813b5ea3 --- /dev/null +++ b/core/src/net/sf/openrocket/formatting/RocketFormatterImpl.java @@ -0,0 +1,27 @@ +package net.sf.openrocket.formatting; + +import java.util.Set; + +import net.sf.openrocket.rocketcomponent.Rocket; + +import com.google.inject.Inject; + +public class RocketFormatterImpl implements RocketFormatter { + + @Inject + private Set substitutors; + + @Override + public String format(Rocket rocket, String configId) { + String name = rocket.getFlightConfigurationName(configId); + + for (RocketSubstitutor s : substitutors) { + while (s.containsSubstitution(name)) { + name = s.substitute(name, rocket, configId); + } + } + + return name; + } + +} diff --git a/core/src/net/sf/openrocket/formatting/RocketSubstitutor.java b/core/src/net/sf/openrocket/formatting/RocketSubstitutor.java new file mode 100644 index 000000000..006c33b34 --- /dev/null +++ b/core/src/net/sf/openrocket/formatting/RocketSubstitutor.java @@ -0,0 +1,20 @@ +package net.sf.openrocket.formatting; + +import java.util.Map; + +import net.sf.openrocket.plugin.Plugin; +import net.sf.openrocket.rocketcomponent.Rocket; + +/** + * A class that allows substitution to occur in a text string. + */ +@Plugin +public interface RocketSubstitutor { + + public boolean containsSubstitution(String str); + + public String substitute(String str, Rocket rocket, String configId); + + public Map getDescriptions(); + +} diff --git a/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/FlightConfigurationDialog.java b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/FlightConfigurationDialog.java index 5e9817e97..0b906162e 100644 --- a/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/FlightConfigurationDialog.java +++ b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/FlightConfigurationDialog.java @@ -19,8 +19,9 @@ import net.sf.openrocket.gui.adaptors.FlightConfigurationModel; import net.sf.openrocket.gui.main.BasicFrame; import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.FlightConfigurableComponent; import net.sf.openrocket.rocketcomponent.Rocket; -import net.sf.openrocket.rocketvisitors.CopyFlightConfigurationVisitor; +import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.startup.Application; /** @@ -64,7 +65,7 @@ public class FlightConfigurationDialog extends JDialog { configSelector.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - + configurationChanged(); } }); @@ -85,7 +86,7 @@ public class FlightConfigurationDialog extends JDialog { renameConfButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - new RenameConfigDialog(FlightConfigurationDialog.this, rocket).setVisible(true); + renameConfiguration(); } }); panel.add(renameConfButton); @@ -167,37 +168,32 @@ public class FlightConfigurationDialog extends JDialog { updateButtonState(); } - public void addConfiguration() { - String id = rocket.newFlightConfigurationID(); - rocket.getDefaultConfiguration().setFlightConfigurationID(id); - motorConfigurationPanel.fireTableDataChanged(); - recoveryConfigurationPanel.fireTableDataChanged(); - separationConfigurationPanel.fireTableDataChanged(); - updateButtonState(); + private void addConfiguration() { + String newId = rocket.newFlightConfigurationID(); + rocket.getDefaultConfiguration().setFlightConfigurationID(newId); + configurationChanged(); } - public void copyConfiguration() { + private void copyConfiguration() { String currentId = rocket.getDefaultConfiguration().getFlightConfigurationID(); // currentID is the currently selected configuration. String newConfigId = rocket.newFlightConfigurationID(); String oldName = rocket.getFlightConfigurationName(currentId); - CopyFlightConfigurationVisitor v = new CopyFlightConfigurationVisitor(currentId, newConfigId); - v.visit(rocket); - // Select the new configuration + + for (RocketComponent c : rocket) { + if (c instanceof FlightConfigurableComponent) { + ((FlightConfigurableComponent) c).cloneFlightConfiguration(currentId, newConfigId); + } + } + rocket.setFlightConfigurationName(currentId, oldName); rocket.getDefaultConfiguration().setFlightConfigurationID(newConfigId); - // Copy the name. - this.changeConfigurationName(oldName); - motorConfigurationPanel.fireTableDataChanged(); - recoveryConfigurationPanel.fireTableDataChanged(); - separationConfigurationPanel.fireTableDataChanged(); - updateButtonState(); + configurationChanged(); } - public void changeConfigurationName(String newName) { - String currentId = rocket.getDefaultConfiguration().getFlightConfigurationID(); - rocket.setFlightConfigurationName(currentId, newName); + private void renameConfiguration() { + new RenameConfigDialog(this, rocket).setVisible(true); } private void removeConfiguration() { @@ -206,26 +202,13 @@ public class FlightConfigurationDialog extends JDialog { return; rocket.removeFlightConfigurationID(currentId); rocket.getDefaultConfiguration().setFlightConfigurationID(null); - motorConfigurationPanel.fireTableDataChanged(); - recoveryConfigurationPanel.fireTableDataChanged(); - separationConfigurationPanel.fireTableDataChanged(); - updateButtonState(); - } - - /** - * Call this from other panels when a change might cause the names of the configurations to change. - */ - public void fireContentsUpdated() { + configurationChanged(); } private void updateButtonState() { String currentId = rocket.getDefaultConfiguration().getFlightConfigurationID(); removeConfButton.setEnabled(currentId != null); renameConfButton.setEnabled(currentId != null); - motorConfigurationPanel.updateButtonState(); - recoveryConfigurationPanel.updateButtonState(); - separationConfigurationPanel.updateButtonState(); } - } diff --git a/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/FlightConfigurationModelRemoveMe.java b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/FlightConfigurationModelRemoveMe.java new file mode 100644 index 000000000..9552751fb --- /dev/null +++ b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/FlightConfigurationModelRemoveMe.java @@ -0,0 +1,97 @@ +package net.sf.openrocket.gui.dialogs.flightconfiguration; + +import java.util.HashMap; +import java.util.Map; + +import javax.swing.DefaultComboBoxModel; + +import net.sf.openrocket.rocketcomponent.Configuration; +import net.sf.openrocket.rocketcomponent.Rocket; + +public class FlightConfigurationModelRemoveMe extends DefaultComboBoxModel { + + private final Configuration config; + private final Rocket rocket; + + private Map map = new HashMap(); + + private final FlightConfigurationDialog flightConfigurationDialog; + + public FlightConfigurationModelRemoveMe(FlightConfigurationDialog flightConfigurationDialog, Configuration config) { + this.flightConfigurationDialog = flightConfigurationDialog; + this.config = config; + this.rocket = config.getRocket(); + } + + void fireContentsUpdated() { + fireContentsChanged(this, 0, rocket.getFlightConfigurationIDs().length); + } + + @Override + public Object getElementAt(int index) { + String[] ids = rocket.getFlightConfigurationIDs(); + if (index < 0 || index >= ids.length) + return null; + + return get(ids[index]); + } + + @Override + public int getSize() { + return rocket.getFlightConfigurationIDs().length; + } + + @Override + public Object getSelectedItem() { + return get(config.getFlightConfigurationID()); + } + + @Override + public void setSelectedItem(Object item) { + if (item == null) { + // Clear selection - huh? + return; + } + if (!(item instanceof ID)) { + throw new IllegalArgumentException("MotorConfigurationModel item=" + item); + } + + ID idObject = (ID) item; + // flightConfigurationDialog.selectConfiguration(idObject.getID()); + } + + /* + * The ID class is an adapter, that contains the actual configuration ID, + * but gives the configuration description as its String representation. + * The get(id) method retrieves ID objects and caches them for reuse. + */ + + private ID get(String id) { + ID idObject = map.get(id); + if (idObject != null) + return idObject; + + idObject = new ID(id); + map.put(id, idObject); + return idObject; + } + + + private class ID { + private final String id; + + public ID(String id) { + this.id = id; + } + + public String getID() { + return id; + } + + @Override + public String toString() { + return rocket.getFlightConfigurationNameOrDescription(id); + } + } + +} diff --git a/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/MotorConfigurationPanel.java b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/MotorConfigurationPanel.java index 4f9368d51..cd97ef167 100644 --- a/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/MotorConfigurationPanel.java +++ b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/MotorConfigurationPanel.java @@ -140,7 +140,7 @@ public class MotorConfigurationPanel extends JPanel { @Override public void actionPerformed(ActionEvent e) { // FIXME - selectIgnition(); + resetIgnition(); } }); this.add(resetIgnitionButton, "sizegroup button, wrap"); @@ -157,7 +157,7 @@ public class MotorConfigurationPanel extends JPanel { updateButtonState(); } - public void updateButtonState() { + private void updateButtonState() { String currentID = rocket.getDefaultConfiguration().getFlightConfigurationID(); MotorMount currentMount = getCurrentMount(); selectMotorButton.setEnabled(currentMount != null && currentID != null); @@ -220,7 +220,6 @@ public class MotorConfigurationPanel extends JPanel { mount.getMotorConfiguration().set(id, config); } - flightConfigurationDialog.fireContentsUpdated(); fireTableDataChanged(); } @@ -232,7 +231,6 @@ public class MotorConfigurationPanel extends JPanel { mount.getMotorConfiguration().resetDefault(id); - flightConfigurationDialog.fireContentsUpdated(); fireTableDataChanged(); } @@ -248,7 +246,18 @@ public class MotorConfigurationPanel extends JPanel { currentMount); dialog.setVisible(true); - flightConfigurationDialog.fireContentsUpdated(); + fireTableDataChanged(); + } + + + private void resetIgnition() { + String currentID = rocket.getDefaultConfiguration().getFlightConfigurationID(); + MotorMount currentMount = getCurrentMount(); + if (currentID == null || currentMount == null) + return; + + currentMount.getIgnitionConfiguration().resetDefault(currentID); + fireTableDataChanged(); } diff --git a/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RecoveryConfigurationPanel.java b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RecoveryConfigurationPanel.java index 939ca1029..343c55415 100644 --- a/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RecoveryConfigurationPanel.java +++ b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RecoveryConfigurationPanel.java @@ -26,6 +26,8 @@ import net.sf.openrocket.unit.UnitGroup; public class RecoveryConfigurationPanel extends JPanel { + // FIXME: Gray italics for default selection + private Translator trans = Application.getTranslator(); diff --git a/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RenameConfigDialog.java b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RenameConfigDialog.java index df2845118..8200c2b93 100644 --- a/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RenameConfigDialog.java +++ b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RenameConfigDialog.java @@ -6,12 +6,13 @@ import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JDialog; +import javax.swing.JLabel; import javax.swing.JPanel; -import javax.swing.JTextArea; +import javax.swing.JTextField; import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.l10n.Translator; -import net.sf.openrocket.rocketcomponent.Configuration; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.startup.Application; @@ -21,56 +22,51 @@ public class RenameConfigDialog extends JDialog { RenameConfigDialog(final FlightConfigurationDialog parent, final Rocket rocket) { super(parent, trans.get("edtmotorconfdlg.title.Renameconf"), Dialog.ModalityType.APPLICATION_MODAL); - final Configuration config = rocket.getDefaultConfiguration(); + final String configId = rocket.getDefaultConfiguration().getFlightConfigurationID(); JPanel panel = new JPanel(new MigLayout("fill")); - final JTextArea textbox = new JTextArea(config.getFlightConfigurationDescription()); - panel.add(textbox, "span, w 200lp, wrap"); + // FIXME: Localize + panel.add(new JLabel("Name for flight configuration:"), "span, wrap rel"); + + final JTextField textbox = new JTextField(rocket.getFlightConfigurationName(configId)); + panel.add(textbox, "span, w 200lp, growx, wrap para"); + + panel.add(new JPanel(), "growx"); JButton okButton = new JButton("Ok"); okButton.addActionListener(new ActionListener() { - @Override public void actionPerformed(ActionEvent e) { - parent.changeConfigurationName(textbox.getText()); + String newName = textbox.getText(); + rocket.setFlightConfigurationName(configId, newName); RenameConfigDialog.this.setVisible(false); } - }); - panel.add(okButton); JButton defaultButton = new JButton("Reset to default"); defaultButton.addActionListener(new ActionListener() { - @Override public void actionPerformed(ActionEvent e) { - parent.changeConfigurationName(null); + rocket.setFlightConfigurationName(configId, null); RenameConfigDialog.this.setVisible(false); } - }); - panel.add(defaultButton); JButton cancel = new JButton("Cancel"); cancel.addActionListener(new ActionListener() { - @Override public void actionPerformed(ActionEvent e) { RenameConfigDialog.this.setVisible(false); } - }); - panel.add(cancel); - this.setContentPane(panel); - this.validate(); - this.pack(); - this.setLocationByPlatform(true); + this.add(panel); + GUIUtil.setDisposableDialogOptions(this, okButton); } } diff --git a/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/SeparationConfigurationPanel.java b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/SeparationConfigurationPanel.java index 05be8b880..aff0b9108 100644 --- a/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/SeparationConfigurationPanel.java +++ b/core/src/net/sf/openrocket/gui/dialogs/flightconfiguration/SeparationConfigurationPanel.java @@ -20,11 +20,14 @@ import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.Stage; import net.sf.openrocket.rocketcomponent.StageSeparationConfiguration; -import net.sf.openrocket.rocketcomponent.StageSeparationConfiguration.SeparationEvent; import net.sf.openrocket.startup.Application; +import net.sf.openrocket.unit.UnitGroup; public class SeparationConfigurationPanel extends JPanel { + // FIXME: Gray italics for default selection + + private static final Translator trans = Application.getTranslator(); private final FlightConfigurationDialog flightConfigurationDialog; @@ -164,17 +167,20 @@ public class SeparationConfigurationPanel extends JPanel { return d.getName(); case 1: String id = rocket.getDefaultConfiguration().getFlightConfigurationID(); - StageSeparationConfiguration separationConfig = d.getStageSeparationConfiguration().get(id); + StageSeparationConfiguration config = d.getStageSeparationConfiguration().get(id); - SeparationEvent event = separationConfig.getSeparationEvent(); - String str = event.toString(); + String str; + + str = config.getSeparationEvent().toString(); + if (config.getSeparationDelay() > 0.001) { + str += " + " + UnitGroup.UNITS_SHORT_TIME.toStringUnit(config.getSeparationDelay()); + } if (d.getStageSeparationConfiguration().isDefault(id)) { - str = trans.get("SeparationConfigurationPanel.table.separation.default"); - str = str.replace("{0}", event.toString()); - } else { - str = event.toString(); + String def = trans.get("SeparationConfigurationPanel.table.separation.default"); + str = def.replace("{0}", str); } + return str; default: diff --git a/core/src/net/sf/openrocket/rocketcomponent/Rocket.java b/core/src/net/sf/openrocket/rocketcomponent/Rocket.java index ad20ade6a..814f17696 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/Rocket.java +++ b/core/src/net/sf/openrocket/rocketcomponent/Rocket.java @@ -651,7 +651,7 @@ public class Rocket extends RocketComponent { * @return a textual representation of the configuration */ @SuppressWarnings("null") - public String getFlightConfigurationDescription(String id) { + private String getFlightConfigurationDescription(String id) { checkState(); String name; int motorCount = 0; diff --git a/core/src/net/sf/openrocket/simulation/SimulationOptions.java b/core/src/net/sf/openrocket/simulation/SimulationOptions.java index 773d77f1c..0271af62e 100644 --- a/core/src/net/sf/openrocket/simulation/SimulationOptions.java +++ b/core/src/net/sf/openrocket/simulation/SimulationOptions.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.Random; import net.sf.openrocket.aerodynamics.BarrowmanCalculator; +import net.sf.openrocket.formatting.MotorDescriptionSubstitutor; import net.sf.openrocket.masscalc.BasicMassCalculator; import net.sf.openrocket.models.atmosphere.AtmosphericModel; import net.sf.openrocket.models.atmosphere.ExtendedISAModel; @@ -402,12 +403,15 @@ public class SimulationOptions implements ChangeSource, Cloneable { } else { if (src.rocket.hasMotors(src.motorID)) { - // Try to find a matching motor ID - String motorDesc = src.rocket.getFlightConfigurationDescription(src.motorID); + // Try to find a closely matching motor ID + MotorDescriptionSubstitutor formatter = new MotorDescriptionSubstitutor(); + + String motorDesc = formatter.substitute(MotorDescriptionSubstitutor.SUBSTITUTION, src.rocket, src.motorID); String matchID = null; for (String id : this.rocket.getFlightConfigurationIDs()) { - if (motorDesc.equals(this.rocket.getFlightConfigurationDescription(id))) { + String motorDesc2 = formatter.substitute(MotorDescriptionSubstitutor.SUBSTITUTION, this.rocket, id); + if (motorDesc.equals(motorDesc2)) { matchID = id; break; } diff --git a/core/src/net/sf/openrocket/startup/ApplicationModule.java b/core/src/net/sf/openrocket/startup/ApplicationModule.java index 16932e968..158899f47 100644 --- a/core/src/net/sf/openrocket/startup/ApplicationModule.java +++ b/core/src/net/sf/openrocket/startup/ApplicationModule.java @@ -1,5 +1,7 @@ package net.sf.openrocket.startup; +import net.sf.openrocket.formatting.RocketFormatter; +import net.sf.openrocket.formatting.RocketFormatterImpl; import net.sf.openrocket.gui.watcher.WatchService; import net.sf.openrocket.gui.watcher.WatchServiceImpl; import net.sf.openrocket.l10n.Translator; @@ -15,6 +17,7 @@ public class ApplicationModule extends AbstractModule { bind(Preferences.class).toInstance(Application.getPreferences()); bind(Translator.class).toInstance(Application.getTranslator()); bind(WatchService.class).to(WatchServiceImpl.class); + bind(RocketFormatter.class).to(RocketFormatterImpl.class); } }