diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 7d169196e..d257d8814 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -58,6 +58,7 @@ RocketPanel.lbl.ViewType = View Type: ! BasicFrame BasicFrame.tab.Rocketdesign = Rocket design +BasicFrame.tab.Flightconfig = Configurations BasicFrame.tab.Flightsim = Flight simulations BasicFrame.title.Addnewcomp = Add new component BasicFrame.dlg.lbl1 = Design ' diff --git a/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/DeploymentSelectionDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/DeploymentSelectionDialog.java index 87bfe2a76..6744f07eb 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/DeploymentSelectionDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/DeploymentSelectionDialog.java @@ -1,6 +1,7 @@ package net.sf.openrocket.gui.dialogs.flightconfiguration; import java.awt.Dialog; +import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -43,7 +44,7 @@ public class DeploymentSelectionDialog extends JDialog { private final UnitSelector altUnit; private final JSlider altSlider; - DeploymentSelectionDialog(JDialog parent, final Rocket rocket, final RecoveryDevice component) { + public DeploymentSelectionDialog(Window parent, final Rocket rocket, final RecoveryDevice component) { super(parent, trans.get("edtmotorconfdlg.title.Selectdeploymentconf"), Dialog.ModalityType.APPLICATION_MODAL); final String id = rocket.getDefaultConfiguration().getFlightConfigurationID(); diff --git a/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/IgnitionSelectionDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/IgnitionSelectionDialog.java index f4e8cd4f6..8093deb34 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/IgnitionSelectionDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/IgnitionSelectionDialog.java @@ -1,6 +1,7 @@ package net.sf.openrocket.gui.dialogs.flightconfiguration; import java.awt.Dialog; +import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -36,7 +37,7 @@ public class IgnitionSelectionDialog extends JDialog { private IgnitionConfiguration newConfiguration; - public IgnitionSelectionDialog(JDialog parent, final Rocket rocket, final MotorMount component) { + public IgnitionSelectionDialog(Window parent, final Rocket rocket, final MotorMount component) { super(parent, trans.get("edtmotorconfdlg.title.Selectignitionconf"), Dialog.ModalityType.APPLICATION_MODAL); final String id = rocket.getDefaultConfiguration().getFlightConfigurationID(); diff --git a/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RenameConfigDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RenameConfigDialog.java index bd5d5afeb..1b26bc5d8 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RenameConfigDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RenameConfigDialog.java @@ -1,6 +1,7 @@ package net.sf.openrocket.gui.dialogs.flightconfiguration; import java.awt.Dialog; +import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -20,7 +21,7 @@ public class RenameConfigDialog extends JDialog { private static final Translator trans = Application.getTranslator(); - RenameConfigDialog(final FlightConfigurationDialog parent, final Rocket rocket) { + public RenameConfigDialog(final Window parent, final Rocket rocket) { super(parent, trans.get("RenameConfigDialog.title"), Dialog.ModalityType.APPLICATION_MODAL); final String configId = rocket.getDefaultConfiguration().getFlightConfigurationID(); diff --git a/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/SeparationSelectionDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/SeparationSelectionDialog.java index 58a76f19f..2456d1694 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/SeparationSelectionDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/SeparationSelectionDialog.java @@ -1,6 +1,7 @@ package net.sf.openrocket.gui.dialogs.flightconfiguration; import java.awt.Dialog; +import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -35,7 +36,7 @@ public class SeparationSelectionDialog extends JDialog { private StageSeparationConfiguration newConfiguration; - SeparationSelectionDialog(JDialog parent, final Rocket rocket, final Stage component) { + public SeparationSelectionDialog(Window parent, final Rocket rocket, final Stage component) { super(parent, trans.get("edtmotorconfdlg.title.Selectseparationconf"), Dialog.ModalityType.APPLICATION_MODAL); final String id = rocket.getDefaultConfiguration().getFlightConfigurationID(); diff --git a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java index e44fa0c24..0b754ef7e 100644 --- a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -83,6 +83,7 @@ import net.sf.openrocket.gui.dialogs.optimization.GeneralOptimizationDialog; import net.sf.openrocket.gui.dialogs.preferences.PreferencesDialog; import net.sf.openrocket.gui.help.tours.GuidedTourSelectionDialog; import net.sf.openrocket.gui.main.componenttree.ComponentTree; +import net.sf.openrocket.gui.main.flightconfigpanel.FlightConfigurationPanel; import net.sf.openrocket.gui.scalefigure.RocketPanel; import net.sf.openrocket.gui.util.FileHelper; import net.sf.openrocket.gui.util.GUIUtil; @@ -100,10 +101,10 @@ import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.BugException; import net.sf.openrocket.util.MemoryManagement; import net.sf.openrocket.util.MemoryManagement.MemoryData; -import net.sf.openrocket.utils.ComponentPresetEditor; import net.sf.openrocket.util.Reflection; import net.sf.openrocket.util.StateChangeListener; import net.sf.openrocket.util.TestRockets; +import net.sf.openrocket.utils.ComponentPresetEditor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -195,6 +196,8 @@ public class BasicFrame extends JFrame { tabbedPane = new JTabbedPane(); //// Rocket design tabbedPane.addTab(trans.get("BasicFrame.tab.Rocketdesign"), null, designTab()); + //// Flight configurations + tabbedPane.addTab(trans.get("BasicFrame.tab.Flightconfig"), null, new FlightConfigurationPanel(document)); //// Flight simulations tabbedPane.addTab(trans.get("BasicFrame.tab.Flightsim"), null, simulationPanel); @@ -251,7 +254,6 @@ public class BasicFrame extends JFrame { log.debug("BasicFrame instantiation complete"); } - /** * Construct the "Rocket design" tab. This contains a horizontal split pane * with the left component the design tree and the right component buttons diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java new file mode 100644 index 000000000..54ec0b4a4 --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java @@ -0,0 +1,45 @@ +package net.sf.openrocket.gui.main.flightconfigpanel; + +import javax.swing.JPanel; +import javax.swing.JTable; + +import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.formatting.RocketDescriptor; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.FlightConfigurableComponent; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.util.Pair; + +public abstract class FlightConfigurablePanel extends JPanel { + + protected static final Translator trans = Application.getTranslator(); + protected RocketDescriptor descriptor = Application.getInjector().getInstance(RocketDescriptor.class); + + protected final FlightConfigurationPanel flightConfigurationPanel; + protected final Rocket rocket; + + public FlightConfigurablePanel(final FlightConfigurationPanel flightConfigurationPanel, Rocket rocket) { + super(new MigLayout("fill")); + this.flightConfigurationPanel = flightConfigurationPanel; + this.rocket = rocket; + } + + protected abstract JTable getTable(); + + protected T getSelectedComponent() { + + int col = getTable().getSelectedColumn(); + int row = getTable().getSelectedRow(); + if ( row < 0 || col < 0 ) { + return null; + } + Object tableValue = getTable().getModel().getValueAt(row, col); + if ( tableValue instanceof Pair ) { + Pair selectedComponent = (Pair) tableValue; + return selectedComponent.getV(); + } + return null; + } + +} \ No newline at end of file diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurationPanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurationPanel.java new file mode 100644 index 000000000..66b8ed08a --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurationPanel.java @@ -0,0 +1,193 @@ +package net.sf.openrocket.gui.main.flightconfigpanel; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.EventObject; + +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import javax.swing.SwingUtilities; + +import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.document.Simulation; +import net.sf.openrocket.gui.dialogs.flightconfiguration.RenameConfigDialog; +import net.sf.openrocket.gui.main.BasicFrame; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.FlightConfigurableComponent; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.util.StateChangeListener; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class FlightConfigurationPanel extends JPanel implements StateChangeListener { + + private static final Logger log = LoggerFactory.getLogger(FlightConfigurationPanel.class); + private static final Translator trans = Application.getTranslator(); + + private final OpenRocketDocument document; + private final Rocket rocket; + + private final JButton newConfButton, renameConfButton, removeConfButton, copyConfButton; + + private final JTabbedPane tabs; + private final MotorConfigurationPanel motorConfigurationPanel; + private final RecoveryConfigurationPanel recoveryConfigurationPanel; + private final SeparationConfigurationPanel separationConfigurationPanel; + + @Override + public void stateChanged(EventObject e) { + updateButtonState(); + } + + public FlightConfigurationPanel(OpenRocketDocument doc) { + super(new MigLayout("fill")); + + this.document = doc; + this.rocket = doc.getRocket(); + + JPanel panel = new JPanel(new MigLayout("fill")); + + //// Tabs for advanced view. + tabs = new JTabbedPane(); + this.add(tabs, "grow, spanx, wrap"); + + //// Motor tabs + motorConfigurationPanel = new MotorConfigurationPanel(this, rocket); + tabs.add(trans.get("edtmotorconfdlg.lbl.Motortab"), motorConfigurationPanel); + //// Recovery tab + recoveryConfigurationPanel = new RecoveryConfigurationPanel(this, rocket); + tabs.add(trans.get("edtmotorconfdlg.lbl.Recoverytab"), recoveryConfigurationPanel); + + //// Stage tab + separationConfigurationPanel = new SeparationConfigurationPanel(this, rocket); + tabs.add(trans.get("edtmotorconfdlg.lbl.Stagetab"), separationConfigurationPanel); + + newConfButton = new JButton(trans.get("edtmotorconfdlg.but.Newconfiguration")); + newConfButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + addConfiguration(); + configurationChanged(); + } + + }); + + panel.add(newConfButton); + + renameConfButton = new JButton(trans.get("edtmotorconfdlg.but.Renameconfiguration")); + renameConfButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + renameConfiguration(); + configurationChanged(); + } + }); + panel.add(renameConfButton); + + removeConfButton = new JButton(trans.get("edtmotorconfdlg.but.Removeconfiguration")); + removeConfButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + removeConfiguration(); + configurationChanged(); + } + }); + panel.add(removeConfButton); + + copyConfButton = new JButton(trans.get("edtmotorconfdlg.but.Copyconfiguration")); + copyConfButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + copyConfiguration(); + configurationChanged(); + } + }); + panel.add(copyConfButton, "wrap para"); + + this.add(panel, "growx"); + updateButtonState(); + + this.rocket.getDefaultConfiguration().addChangeListener(this); + } + + private void addConfiguration() { + String newId = rocket.newFlightConfigurationID(); + rocket.getDefaultConfiguration().setFlightConfigurationID(newId); + + // Create a new simulation for this configuration. + createSimulationForNewConfiguration(); + + configurationChanged(); + } + + private void copyConfiguration() { + String currentId = rocket.getDefaultConfiguration().getFlightConfigurationID(); + + // currentID is the currently selected configuration. + String newConfigId = rocket.newFlightConfigurationID(); + String oldName = rocket.getFlightConfigurationName(currentId); + + for (RocketComponent c : rocket) { + if (c instanceof FlightConfigurableComponent) { + ((FlightConfigurableComponent) c).cloneFlightConfiguration(currentId, newConfigId); + } + } + rocket.setFlightConfigurationName(currentId, oldName); + rocket.getDefaultConfiguration().setFlightConfigurationID(newConfigId); + + // Create a new simulation for this configuration. + createSimulationForNewConfiguration(); + + configurationChanged(); + } + + private void renameConfiguration() { + new RenameConfigDialog(SwingUtilities.getWindowAncestor(this), rocket).setVisible(true); + } + + private void removeConfiguration() { + String currentId = rocket.getDefaultConfiguration().getFlightConfigurationID(); + if (currentId == null) + return; + rocket.removeFlightConfigurationID(currentId); + rocket.getDefaultConfiguration().setFlightConfigurationID(null); + configurationChanged(); + } + + /** + * prereq - assumes that the new configuration has been set as the default configuration. + */ + private void createSimulationForNewConfiguration() { + Simulation newSim = new Simulation(rocket); + OpenRocketDocument doc = BasicFrame.findDocument(rocket); + newSim.setName(doc.getNextSimulationName()); + doc.addSimulation(newSim); + } + + private void configurationChanged() { + motorConfigurationPanel.fireTableDataChanged(); + recoveryConfigurationPanel.fireTableDataChanged(); + separationConfigurationPanel.fireTableDataChanged(); + updateButtonState(); + } + + private void updateButtonState() { + String currentId = rocket.getDefaultConfiguration().getFlightConfigurationID(); + removeConfButton.setEnabled(currentId != null); + renameConfButton.setEnabled(currentId != null); + copyConfButton.setEnabled(currentId != null); + + } + + public void setCurrentConfiguration(String id) { + rocket.getDefaultConfiguration().setFlightConfigurationID(id); + updateButtonState(); + } + +} diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/MotorConfigurationPanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/MotorConfigurationPanel.java new file mode 100644 index 000000000..2914ba557 --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/MotorConfigurationPanel.java @@ -0,0 +1,295 @@ +package net.sf.openrocket.gui.main.flightconfigpanel; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.SwingUtilities; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.table.DefaultTableCellRenderer; + +import net.sf.openrocket.gui.dialogs.flightconfiguration.IgnitionSelectionDialog; +import net.sf.openrocket.gui.dialogs.motor.MotorChooserDialog; +import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.rocketcomponent.IgnitionConfiguration; +import net.sf.openrocket.rocketcomponent.MotorConfiguration; +import net.sf.openrocket.rocketcomponent.MotorMount; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.unit.UnitGroup; +import net.sf.openrocket.util.Chars; +import net.sf.openrocket.util.Coordinate; +import net.sf.openrocket.util.Pair; + +public class MotorConfigurationPanel extends FlightConfigurablePanel { + + private static final String NONE = trans.get("edtmotorconfdlg.tbl.None"); + + private final JButton selectMotorButton, removeMotorButton, selectIgnitionButton, resetIgnitionButton; + + protected final JTable configurationTable; + protected final MotorConfigurationTableModel configurationTableModel; + + MotorConfigurationPanel(final FlightConfigurationPanel flightConfigurationPanel, Rocket rocket) { + super(flightConfigurationPanel,rocket); + + //// Motor selection table. + configurationTableModel = new MotorConfigurationTableModel(rocket); + configurationTable = new JTable(configurationTableModel); + configurationTable.setCellSelectionEnabled(true); + configurationTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + configurationTable.setDefaultRenderer(Object.class, new MotorTableCellRenderer()); + + configurationTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + updateButtonState(); + int selectedColumn = configurationTable.getSelectedColumn(); + if (e.getClickCount() == 2) { + if (selectedColumn > 0) { + selectMotor(); + } + } + } + }); + + configurationTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { + + @Override + public void valueChanged(ListSelectionEvent e) { + if ( e.getValueIsAdjusting() ) { + return; + } + int firstrow = e.getFirstIndex(); + int lastrow = e.getLastIndex(); + ListSelectionModel model = (ListSelectionModel) e.getSource(); + for( int row = firstrow; row <= lastrow; row ++) { + if ( model.isSelectedIndex(row) ) { + String id = (String) configurationTableModel.getValueAt(row, 0); + flightConfigurationPanel.setCurrentConfiguration(id); + return; + } + } + } + + }); + + JScrollPane scroll = new JScrollPane(configurationTable); + this.add(scroll, "grow, wrap"); + + //// Select motor + selectMotorButton = new JButton(trans.get("MotorConfigurationPanel.btn.selectMotor")); + selectMotorButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + selectMotor(); + } + }); + this.add(selectMotorButton, "split, sizegroup button"); + + //// Remove motor button + removeMotorButton = new JButton(trans.get("MotorConfigurationPanel.btn.removeMotor")); + removeMotorButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + removeMotor(); + } + }); + this.add(removeMotorButton, "sizegroup button"); + + //// Select Ignition button + selectIgnitionButton = new JButton(trans.get("MotorConfigurationPanel.btn.selectIgnition")); + selectIgnitionButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + selectIgnition(); + } + }); + this.add(selectIgnitionButton, "sizegroup button"); + + //// Reset Ignition button + resetIgnitionButton = new JButton(trans.get("MotorConfigurationPanel.btn.resetIgnition")); + resetIgnitionButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + resetIgnition(); + } + }); + this.add(resetIgnitionButton, "sizegroup button, wrap"); + + updateButtonState(); + + } + + @Override + protected JTable getTable() { + return configurationTable; + } + + public void fireTableDataChanged() { + int selected = configurationTable.getSelectedRow(); + configurationTableModel.fireTableDataChanged(); + if (selected >= 0) { + selected = Math.min(selected, configurationTable.getRowCount() - 1); + configurationTable.getSelectionModel().setSelectionInterval(selected, selected); + } + updateButtonState(); + } + + private void updateButtonState() { + String currentID = rocket.getDefaultConfiguration().getFlightConfigurationID(); + MotorMount currentMount = getSelectedComponent(); + selectMotorButton.setEnabled(currentMount != null && currentID != null); + removeMotorButton.setEnabled(currentMount != null && currentID != null); + selectIgnitionButton.setEnabled(currentMount != null && currentID != null); + resetIgnitionButton.setEnabled(currentMount != null && currentID != null); + } + + + private void selectMotor() { + String id = rocket.getDefaultConfiguration().getFlightConfigurationID(); + MotorMount mount = getSelectedComponent(); + if (id == null || mount == null) + return; + + MotorConfiguration config = mount.getMotorConfiguration().get(id); + + MotorChooserDialog dialog = new MotorChooserDialog( + config.getMotor(), + config.getEjectionDelay(), + mount.getMotorMountDiameter(), + SwingUtilities.getWindowAncestor(flightConfigurationPanel)); + dialog.setVisible(true); + Motor m = dialog.getSelectedMotor(); + double d = dialog.getSelectedDelay(); + + if (m != null) { + config = new MotorConfiguration(); + config.setMotor(m); + config.setEjectionDelay(d); + mount.getMotorConfiguration().set(id, config); + } + + fireTableDataChanged(); + } + + private void removeMotor() { + String id = rocket.getDefaultConfiguration().getFlightConfigurationID(); + MotorMount mount = getSelectedComponent(); + if (id == null || mount == null) + return; + + mount.getMotorConfiguration().resetDefault(id); + + fireTableDataChanged(); + } + + private void selectIgnition() { + String currentID = rocket.getDefaultConfiguration().getFlightConfigurationID(); + MotorMount currentMount = getSelectedComponent(); + if (currentID == null || currentMount == null) + return; + + IgnitionSelectionDialog dialog = new IgnitionSelectionDialog( + SwingUtilities.getWindowAncestor(this.flightConfigurationPanel), + rocket, + currentMount); + dialog.setVisible(true); + + fireTableDataChanged(); + } + + + private void resetIgnition() { + String currentID = rocket.getDefaultConfiguration().getFlightConfigurationID(); + MotorMount currentMount = getSelectedComponent(); + if (currentID == null || currentMount == null) + return; + + currentMount.getIgnitionConfiguration().resetDefault(currentID); + + fireTableDataChanged(); + } + + + private class MotorTableCellRenderer extends DefaultTableCellRenderer { + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + if (!(c instanceof JLabel)) { + return c; + } + JLabel label = (JLabel) c; + + switch (column) { + case 0: { + label.setText(descriptor.format(rocket, (String) value)); + return label; + } + default: { + Pair v = (Pair) value; + String id = v.getU(); + MotorMount mount = v.getV(); + MotorConfiguration motorConfig = mount.getMotorConfiguration().get(id); + String motorString = getMotorSpecification(mount, motorConfig); + String ignitionString = getIgnitionEventString(id, mount); + label.setText(motorString + " " + ignitionString); + return label; + } + } + } + + private String getMotorSpecification(MotorMount mount, MotorConfiguration motorConfig) { + Motor motor = motorConfig.getMotor(); + + if (motor == null) + return NONE; + + String str = motor.getDesignation(motorConfig.getEjectionDelay()); + int count = getMountMultiplicity(mount); + if (count > 1) { + str = "" + count + Chars.TIMES + " " + str; + } + return str; + } + + private int getMountMultiplicity(MotorMount mount) { + RocketComponent c = (RocketComponent) mount; + return c.toAbsolute(Coordinate.NUL).length; + } + + + + private String getIgnitionEventString(String id, MotorMount mount) { + IgnitionConfiguration ignitionConfig = mount.getIgnitionConfiguration().get(id); + IgnitionConfiguration.IgnitionEvent ignitionEvent = ignitionConfig.getIgnitionEvent(); + + Double ignitionDelay = ignitionConfig.getIgnitionDelay(); + boolean isDefault = mount.getIgnitionConfiguration().isDefault(id); + + String str = trans.get("MotorMount.IgnitionEvent.short." + ignitionEvent.name()); + if (ignitionDelay > 0.001) { + str = str + " + " + UnitGroup.UNITS_SHORT_TIME.toStringUnit(ignitionDelay); + } + if (isDefault) { + String def = trans.get("MotorConfigurationTableModel.table.ignition.default"); + str = def.replace("{0}", str); + } + return str; + } + + } + +} diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/MotorConfigurationTableModel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/MotorConfigurationTableModel.java new file mode 100644 index 000000000..90896afbb --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/MotorConfigurationTableModel.java @@ -0,0 +1,92 @@ +package net.sf.openrocket.gui.main.flightconfigpanel; + +import java.util.ArrayList; +import java.util.List; + +import javax.swing.table.AbstractTableModel; + +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.MotorMount; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.util.Pair; + +/** + * The table model for selecting and editing the motor configurations. + */ +class MotorConfigurationTableModel extends AbstractTableModel { + + private static final Translator trans = Application.getTranslator(); + + private static final String CONFIGURATION = "Configuration"; + + private final Rocket rocket; + + private List motorMounts = new ArrayList(); + + + public MotorConfigurationTableModel(Rocket rocket) { + this.rocket = rocket; + + initializeMotorMounts(); + + } + + private void initializeMotorMounts() { + motorMounts.clear(); + for (RocketComponent c : rocket) { + if (c instanceof MotorMount) { + if (((MotorMount) c).isMotorMount()) { + motorMounts.add((MotorMount) c); + } + } + } + + } + + @Override + public int getColumnCount() { + return motorMounts.size() + 1; + } + + @Override + public int getRowCount() { + return rocket.getFlightConfigurationIDs().length - 1; + } + + @Override + public Object getValueAt(int row, int column) { + String id = getConfiguration(row); + switch (column) { + case 0: { + return id; + } + default: { + int mountIndex = column - 1; + MotorMount mount = motorMounts.get(mountIndex); + return new Pair(id, mount); + } + } + } + + @Override + public String getColumnName(int column) { + switch (column) { + case 0: { + return CONFIGURATION; + } + default: { + int mountIndex = column - 1; + MotorMount mount = motorMounts.get(mountIndex); + return mount.toString(); + } + } + } + + private String getConfiguration(int row) { + String id = rocket.getFlightConfigurationIDs()[row + 1]; + return id; + } + +} \ No newline at end of file diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java new file mode 100644 index 000000000..49e7c76c3 --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java @@ -0,0 +1,209 @@ +package net.sf.openrocket.gui.main.flightconfigpanel; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +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.ListSelectionModel; +import javax.swing.SwingUtilities; +import javax.swing.table.DefaultTableCellRenderer; + +import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.formatting.RocketDescriptor; +import net.sf.openrocket.gui.dialogs.flightconfiguration.DeploymentSelectionDialog; +import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.DeploymentConfiguration; +import net.sf.openrocket.rocketcomponent.DeploymentConfiguration.DeployEvent; +import net.sf.openrocket.rocketcomponent.RecoveryDevice; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.unit.UnitGroup; +import net.sf.openrocket.util.Pair; + +public class RecoveryConfigurationPanel extends JPanel { + + Translator trans = Application.getTranslator(); + private RocketDescriptor descriptor = Application.getInjector().getInstance(RocketDescriptor.class); + + private final FlightConfigurationPanel flightConfigurationPanel; + final Rocket rocket; + + private final RecoveryTableModel recoveryTableModel; + private final JTable recoveryTable; + private final JButton selectDeploymentButton; + private final JButton resetDeploymentButton; + + + RecoveryConfigurationPanel(FlightConfigurationPanel flightConfigurationPanel, Rocket rocket) { + super(new MigLayout("fill")); + this.flightConfigurationPanel = flightConfigurationPanel; + this.rocket = rocket; + + //// Recovery selection + recoveryTableModel = new RecoveryTableModel(rocket); + recoveryTable = new JTable(recoveryTableModel); + recoveryTable.setCellSelectionEnabled(true); + recoveryTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + recoveryTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + updateButtonState(); + + if (e.getClickCount() == 2) { + // Double-click edits + selectDeployment(); + } + } + }); + recoveryTable.setDefaultRenderer(Object.class, new RecoveryTableCellRenderer()); + + JScrollPane scroll = new JScrollPane(recoveryTable); + this.add(scroll, "span, grow, wrap"); + + //// Select deployment + selectDeploymentButton = new JButton(trans.get("edtmotorconfdlg.but.Selectdeployment")); + selectDeploymentButton.setEnabled(false); + selectDeploymentButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + selectDeployment(); + } + }); + this.add(selectDeploymentButton, "split, sizegroup button"); + + //// Reset deployment + resetDeploymentButton = new JButton(trans.get("edtmotorconfdlg.but.Resetdeployment")); + resetDeploymentButton.setEnabled(false); + resetDeploymentButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + resetDeployment(); + } + }); + this.add(resetDeploymentButton, "sizegroup button, wrap"); + } + + public void fireTableDataChanged() { + int selected = recoveryTable.getSelectedRow(); + recoveryTableModel.fireTableDataChanged(); + if (selected >= 0) { + selected = Math.min(selected, recoveryTable.getRowCount() - 1); + recoveryTable.getSelectionModel().setSelectionInterval(selected, selected); + } + updateButtonState(); + } + + private void selectDeployment() { + RecoveryDevice c = getSelectedComponent(); + if (c == null) { + return; + } + JDialog d = new DeploymentSelectionDialog(SwingUtilities.getWindowAncestor(this), rocket, c); + d.setVisible(true); + fireTableDataChanged(); + } + + private void resetDeployment() { + RecoveryDevice c = getSelectedComponent(); + if (c == null) { + return; + } + String id = rocket.getDefaultConfiguration().getFlightConfigurationID(); + c.getDeploymentConfiguration().resetDefault(id); + fireTableDataChanged(); + } + + public void updateButtonState() { + boolean componentSelected = getSelectedComponent() != null; + selectDeploymentButton.setEnabled(componentSelected); + resetDeploymentButton.setEnabled(componentSelected); + } + + + private RecoveryDevice getSelectedComponent() { + int col = recoveryTable.getSelectedColumn(); + int row = recoveryTable.getSelectedRow(); + if ( row < 0 || col < 0 ) { + return null; + } + Object tableValue = recoveryTable.getModel().getValueAt(row, col); + if ( tableValue instanceof Pair ) { + Pair selected = (Pair) tableValue; + return selected.getV(); + } + return null; + } + + class RecoveryTableCellRenderer extends DefaultTableCellRenderer { + + RecoveryTableCellRenderer() {} + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + if (!(c instanceof JLabel)) { + return c; + } + JLabel label = (JLabel) c; + + switch (column) { + case 0: { + label.setText(descriptor.format(rocket, (String) value)); + regular(label); + return label; + } + default: { + Pair v = (Pair) value; + String id = v.getU(); + RecoveryDevice recovery = v.getV(); + DeploymentConfiguration deployConfig = recovery.getDeploymentConfiguration().get(id); + String spec = getDeploymentSpecification(deployConfig); + label.setText(spec); + if (recovery.getDeploymentConfiguration().isDefault(id)) { + shaded(label); + } else { + regular(label); + } + break; + } + } + return label; + } + + private void shaded(JLabel label) { + GUIUtil.changeFontStyle(label, Font.ITALIC); + label.setForeground(Color.GRAY); + } + + private void regular(JLabel label) { + GUIUtil.changeFontStyle(label, Font.PLAIN); + label.setForeground(Color.BLACK); + } + + private String getDeploymentSpecification( DeploymentConfiguration config ) { + String str; + + str = trans.get("RecoveryDevice.DeployEvent.short." + config.getDeployEvent().name()); + if (config.getDeployEvent() == DeployEvent.ALTITUDE) { + str += " " + UnitGroup.UNITS_DISTANCE.toStringUnit(config.getDeployAltitude()); + } + if (config.getDeployDelay() > 0.001) { + str += " + " + UnitGroup.UNITS_SHORT_TIME.toStringUnit(config.getDeployDelay()); + } + + return str; + } + } + +} diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryTableModel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryTableModel.java new file mode 100644 index 000000000..acf76281f --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryTableModel.java @@ -0,0 +1,92 @@ +package net.sf.openrocket.gui.main.flightconfigpanel; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.swing.table.AbstractTableModel; + +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.RecoveryDevice; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.util.Pair; + +class RecoveryTableModel extends AbstractTableModel { + + private static final Translator trans = Application.getTranslator(); + + private final Rocket rocket; + + private final List recoveryDevices = new ArrayList(); + + private static final String CONFIGURATION = "Configuration"; + + + /** + * @param recoveryConfigurationPanel + */ + RecoveryTableModel(Rocket rocket) { + this.rocket = rocket; + + initialize(); + } + + private void initialize() { + recoveryDevices.clear(); + Iterator it = rocket.iterator(); + while (it.hasNext()) { + RocketComponent c = it.next(); + if (c instanceof RecoveryDevice) { + recoveryDevices.add( (RecoveryDevice) c); + } + } + } + + @Override + public int getRowCount() { + return rocket.getFlightConfigurationIDs().length - 1; + } + + @Override + public int getColumnCount() { + return recoveryDevices.size() + 1; + } + + @Override + public Object getValueAt(int row, int column) { + String id = getConfiguration(row); + switch (column) { + case 0: { + return id; + } + default: { + int index = column - 1; + RecoveryDevice d = recoveryDevices.get(index); + return new Pair(id, d); + } + } + } + + @Override + public String getColumnName(int column) { + switch (column) { + case 0: { + return CONFIGURATION; + } + default: { + int index = column - 1; + RecoveryDevice d = recoveryDevices.get(index); + return d.toString(); + + } + } + } + + private String getConfiguration(int row) { + String id = rocket.getFlightConfigurationIDs()[row + 1]; + return id; + } + +} \ No newline at end of file diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/SeparationConfigurationPanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/SeparationConfigurationPanel.java new file mode 100644 index 000000000..e6233f0ad --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/SeparationConfigurationPanel.java @@ -0,0 +1,205 @@ +package net.sf.openrocket.gui.main.flightconfigpanel; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +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.ListSelectionModel; +import javax.swing.SwingUtilities; +import javax.swing.table.DefaultTableCellRenderer; + +import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.formatting.RocketDescriptor; +import net.sf.openrocket.gui.dialogs.flightconfiguration.SeparationSelectionDialog; +import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.Stage; +import net.sf.openrocket.rocketcomponent.StageSeparationConfiguration; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.unit.UnitGroup; +import net.sf.openrocket.util.Pair; + +public class SeparationConfigurationPanel extends JPanel { + + static final Translator trans = Application.getTranslator(); + private RocketDescriptor descriptor = Application.getInjector().getInstance(RocketDescriptor.class); + + private final FlightConfigurationPanel flightConfigurationPanel; + final Rocket rocket; + + private final JTable separationTable; + private final SeparationTableModel separationTableModel; + private final JButton selectSeparationButton; + private final JButton resetDeploymentButton; + + + SeparationConfigurationPanel(FlightConfigurationPanel flightConfigurationPanel, Rocket rocket) { + super(new MigLayout("fill")); + this.flightConfigurationPanel = flightConfigurationPanel; + this.rocket = rocket; + + + //// Recovery selection + separationTableModel = new SeparationTableModel(rocket); + separationTable = new JTable(separationTableModel); + separationTable.setCellSelectionEnabled(true); + separationTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + separationTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + updateButtonState(); + if (e.getClickCount() == 2) { + // Double-click edits + selectDeployment(); + } + } + }); + separationTable.setDefaultRenderer(Object.class, new SeparationTableCellRenderer()); + + JScrollPane scroll = new JScrollPane(separationTable); + this.add(scroll, "span, grow, wrap"); + + //// Select deployment + selectSeparationButton = new JButton(trans.get("edtmotorconfdlg.but.Selectseparation")); + selectSeparationButton.setEnabled(false); + selectSeparationButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + selectDeployment(); + } + }); + this.add(selectSeparationButton, "split, sizegroup button"); + + //// Reset deployment + resetDeploymentButton = new JButton(trans.get("edtmotorconfdlg.but.Resetseparation")); + resetDeploymentButton.setEnabled(false); + resetDeploymentButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + resetDeployment(); + } + }); + this.add(resetDeploymentButton, "sizegroup button, wrap"); + + } + + public void fireTableDataChanged() { + int selected = separationTable.getSelectedRow(); + separationTableModel.fireTableDataChanged(); + if (selected >= 0) { + selected = Math.min(selected, separationTable.getRowCount() - 1); + separationTable.getSelectionModel().setSelectionInterval(selected, selected); + } + updateButtonState(); + } + + private Stage getSelectedComponent() { + int col = separationTable.getSelectedColumn(); + int row = separationTable.getSelectedRow(); + if ( row < 0 || col < 0 ) { + return null; + } + Object tableValue = separationTable.getModel().getValueAt(row, col); + if ( tableValue instanceof Pair ) { + Pair selected = (Pair) tableValue; + return selected.getV(); + } + return null; + } + + private void selectDeployment() { + Stage stage = getSelectedComponent(); + if (stage == null) { + return; + } + JDialog d = new SeparationSelectionDialog(SwingUtilities.getWindowAncestor(this), rocket, stage); + d.setVisible(true); + fireTableDataChanged(); + } + + private void resetDeployment() { + Stage stage = getSelectedComponent(); + if (stage == null) { + return; + } + String id = rocket.getDefaultConfiguration().getFlightConfigurationID(); + stage.getStageSeparationConfiguration().resetDefault(id); + fireTableDataChanged(); + } + public void updateButtonState() { + boolean componentSelected = getSelectedComponent() != null; + selectSeparationButton.setEnabled(componentSelected); + resetDeploymentButton.setEnabled(componentSelected); + } + + private class SeparationTableCellRenderer extends DefaultTableCellRenderer { + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + if (!(c instanceof JLabel)) { + return c; + } + JLabel label = (JLabel) c; + + switch (column) { + case 0: { + label.setText(descriptor.format(rocket, (String) value)); + regular(label); + return label; + } + default: { + Pair v = (Pair) value; + String id = v.getU(); + Stage stage = v.getV(); + StageSeparationConfiguration sepConfig = stage.getStageSeparationConfiguration().get(id); + String spec = getSeparationSpecification(sepConfig); + label.setText(spec); + if (stage.getStageSeparationConfiguration().isDefault(id)) { + shaded(label); + } else { + regular(label); + } + break; + } + } + return label; + + } + + private void shaded(JLabel label) { + GUIUtil.changeFontStyle(label, Font.ITALIC); + label.setForeground(Color.GRAY); + } + + private void regular(JLabel label) { + GUIUtil.changeFontStyle(label, Font.PLAIN); + label.setForeground(Color.BLACK); + } + + private String getSeparationSpecification( StageSeparationConfiguration sepConfig ) { + String str; + + str = sepConfig.getSeparationEvent().toString(); + if (sepConfig.getSeparationDelay() > 0.001) { + str += " + " + UnitGroup.UNITS_SHORT_TIME.toStringUnit(sepConfig.getSeparationDelay()); + } + + return str; + + } + } + + +} diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/SeparationTableModel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/SeparationTableModel.java new file mode 100644 index 000000000..07333d0b5 --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/SeparationTableModel.java @@ -0,0 +1,97 @@ +package net.sf.openrocket.gui.main.flightconfigpanel; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.swing.table.AbstractTableModel; + +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.RecoveryDevice; +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.startup.Application; +import net.sf.openrocket.unit.UnitGroup; +import net.sf.openrocket.util.Pair; + +class SeparationTableModel extends AbstractTableModel { + + private static final Translator trans = Application.getTranslator(); + + private final Rocket rocket; + + private final List stages = new ArrayList(); + + private static final String CONFIGURATION = "Configuration"; + + SeparationTableModel(Rocket rocket ) { + this.rocket = rocket; + + initialize(); + } + + private void initialize() { + stages.clear(); + Iterator it = rocket.iterator(); + { + int stageIndex = -1; + while (it.hasNext()) { + RocketComponent c = it.next(); + if (c instanceof Stage) { + if (stageIndex >= 0) { + stages.add( (Stage) c); + } + stageIndex++; + } + } + } + } + + + @Override + public int getRowCount() { + return rocket.getFlightConfigurationIDs().length - 1; + } + + @Override + public int getColumnCount() { + return stages.size() + 1; + } + @Override + public Object getValueAt(int row, int column) { + String id = getConfiguration(row); + switch (column) { + case 0: { + return id; + } + default: { + int index = column - 1; + Stage d = stages.get(index); + return new Pair(id, d); + } + } + } + + @Override + public String getColumnName(int column) { + switch (column) { + case 0: { + return CONFIGURATION; + } + default: { + int index = column - 1; + Stage d = stages.get(index); + return d.toString(); + + } + } + } + + private String getConfiguration(int row) { + String id = rocket.getFlightConfigurationIDs()[row + 1]; + return id; + } + +} \ No newline at end of file