1 package net.sf.openrocket.gui.plot;
3 import java.awt.event.ActionEvent;
4 import java.awt.event.ActionListener;
5 import java.awt.event.ItemEvent;
6 import java.awt.event.ItemListener;
7 import java.util.Arrays;
8 import java.util.EnumSet;
10 import javax.swing.BorderFactory;
11 import javax.swing.JButton;
12 import javax.swing.JComboBox;
13 import javax.swing.JLabel;
14 import javax.swing.JOptionPane;
15 import javax.swing.JPanel;
16 import javax.swing.JScrollPane;
17 import javax.swing.JTable;
18 import javax.swing.SwingUtilities;
19 import javax.swing.table.AbstractTableModel;
20 import javax.swing.table.TableColumn;
21 import javax.swing.table.TableColumnModel;
23 import net.miginfocom.swing.MigLayout;
24 import net.sf.openrocket.document.Simulation;
25 import net.sf.openrocket.gui.components.DescriptionArea;
26 import net.sf.openrocket.gui.components.UnitSelector;
27 import net.sf.openrocket.l10n.Translator;
28 import net.sf.openrocket.simulation.FlightDataBranch;
29 import net.sf.openrocket.simulation.FlightDataType;
30 import net.sf.openrocket.simulation.FlightEvent;
31 import net.sf.openrocket.startup.Application;
32 import net.sf.openrocket.unit.Unit;
33 import net.sf.openrocket.util.GUIUtil;
34 import net.sf.openrocket.util.Icons;
36 public class SimulationPlotPanel extends JPanel {
37 private static final Translator trans = Application.getTranslator();
39 // TODO: LOW: Should these be somewhere else?
40 public static final int AUTO = -1;
41 public static final int LEFT = 0;
42 public static final int RIGHT = 1;
45 public static final String AUTO_NAME = trans.get("simplotpanel.AUTO_NAME");
47 public static final String LEFT_NAME = trans.get("simplotpanel.LEFT_NAME");
49 public static final String RIGHT_NAME = trans.get("simplotpanel.RIGHT_NAME");
52 private static final String CUSTOM = trans.get("simplotpanel.CUSTOM");
54 /** The "Custom" configuration - not to be used for anything other than the title. */
55 private static final PlotConfiguration CUSTOM_CONFIGURATION;
57 CUSTOM_CONFIGURATION = new PlotConfiguration(CUSTOM);
60 /** The array of presets for the combo box. */
61 private static final PlotConfiguration[] PRESET_ARRAY;
63 PRESET_ARRAY = Arrays.copyOf(PlotConfiguration.DEFAULT_CONFIGURATIONS,
64 PlotConfiguration.DEFAULT_CONFIGURATIONS.length + 1);
65 PRESET_ARRAY[PRESET_ARRAY.length - 1] = CUSTOM_CONFIGURATION;
70 /** The current default configuration, set each time a plot is made. */
71 private static PlotConfiguration defaultConfiguration =
72 PlotConfiguration.DEFAULT_CONFIGURATIONS[0].resetUnits();
75 private final Simulation simulation;
76 private final FlightDataType[] types;
77 private PlotConfiguration configuration;
80 private JComboBox configurationSelector;
82 private JComboBox domainTypeSelector;
83 private UnitSelector domainUnitSelector;
85 private JPanel typeSelectorPanel;
86 private FlightEventTableModel eventTableModel;
89 private int modifying = 0;
92 public SimulationPlotPanel(final Simulation simulation) {
93 super(new MigLayout("fill"));
95 this.simulation = simulation;
96 if (simulation.getSimulatedData() == null ||
97 simulation.getSimulatedData().getBranchCount() == 0) {
98 throw new IllegalArgumentException("Simulation contains no data.");
100 FlightDataBranch branch = simulation.getSimulatedData().getBranch(0);
101 types = branch.getTypes();
103 // TODO: LOW: Revert to custom if data type is not available.
104 configuration = defaultConfiguration.clone();
107 //// Configuration selector
109 // Setup the combo box
110 configurationSelector = new JComboBox(PRESET_ARRAY);
111 for (PlotConfiguration config : PRESET_ARRAY) {
112 if (config.getName().equals(configuration.getName())) {
113 configurationSelector.setSelectedItem(config);
116 configurationSelector.addItemListener(new ItemListener() {
118 public void itemStateChanged(ItemEvent e) {
121 PlotConfiguration conf = (PlotConfiguration) configurationSelector.getSelectedItem();
122 if (conf == CUSTOM_CONFIGURATION)
125 configuration = conf.clone().resetUnits();
130 //// Preset plot configurations:
131 this.add(new JLabel(trans.get("simplotpanel.lbl.Presetplotconf")), "spanx, split");
132 this.add(configurationSelector, "growx, wrap 20lp");
139 this.add(new JLabel(trans.get("simplotpanel.lbl.Xaxistype")), "spanx, split");
140 domainTypeSelector = new JComboBox(types);
141 domainTypeSelector.setSelectedItem(configuration.getDomainAxisType());
142 domainTypeSelector.addItemListener(new ItemListener() {
144 public void itemStateChanged(ItemEvent e) {
147 FlightDataType type = (FlightDataType) domainTypeSelector.getSelectedItem();
148 configuration.setDomainAxisType(type);
149 domainUnitSelector.setUnitGroup(type.getUnitGroup());
150 domainUnitSelector.setSelectedUnit(configuration.getDomainAxisUnit());
154 this.add(domainTypeSelector, "gapright para");
157 this.add(new JLabel(trans.get("simplotpanel.lbl.Unit")));
158 domainUnitSelector = new UnitSelector(configuration.getDomainAxisType().getUnitGroup());
159 domainUnitSelector.setSelectedUnit(configuration.getDomainAxisUnit());
160 domainUnitSelector.addItemListener(new ItemListener() {
162 public void itemStateChanged(ItemEvent e) {
165 configuration.setDomainAxisUnit(domainUnitSelector.getSelectedUnit());
168 this.add(domainUnitSelector, "width 40lp, gapright para");
170 //// The data will be plotted in time order even if the X axis type is not time.
171 DescriptionArea desc = new DescriptionArea(trans.get("simplotpanel.Desc"), 2, -2f);
172 desc.setViewportBorder(BorderFactory.createEmptyBorder());
173 this.add(desc, "width 1px, growx 1, wrap unrel");
177 //// Y axis selector panel
179 this.add(new JLabel(trans.get("simplotpanel.lbl.Yaxistypes")));
181 this.add(new JLabel(trans.get("simplotpanel.lbl.Flightevents")), "wrap rel");
183 typeSelectorPanel = new JPanel(new MigLayout("gapy rel"));
184 JScrollPane scroll = new JScrollPane(typeSelectorPanel);
185 this.add(scroll, "spany 2, height 10px, grow 100, gapright para");
189 eventTableModel = new FlightEventTableModel();
190 JTable table = new JTable(eventTableModel);
191 table.setTableHeader(null);
192 table.setShowVerticalLines(false);
193 table.setRowSelectionAllowed(false);
194 table.setColumnSelectionAllowed(false);
196 TableColumnModel columnModel = table.getColumnModel();
197 TableColumn col0 = columnModel.getColumn(0);
198 int w = table.getRowHeight() + 2;
200 col0.setPreferredWidth(w);
202 table.addMouseListener(new GUIUtil.BooleanTableClickListener(table));
203 this.add(new JScrollPane(table), "height 10px, width 200lp, grow 1, wrap rel");
206 //// All + None buttons
207 JButton button = new JButton(trans.get("simplotpanel.but.All"));
208 button.addActionListener(new ActionListener() {
210 public void actionPerformed(ActionEvent e) {
211 for (FlightEvent.Type t : FlightEvent.Type.values())
212 configuration.setEvent(t, true);
213 eventTableModel.fireTableDataChanged();
216 this.add(button, "split 2, gapleft para, gapright para, growx, sizegroup buttons");
219 button = new JButton(trans.get("simplotpanel.but.None"));
220 button.addActionListener(new ActionListener() {
222 public void actionPerformed(ActionEvent e) {
223 for (FlightEvent.Type t : FlightEvent.Type.values())
224 configuration.setEvent(t, false);
225 eventTableModel.fireTableDataChanged();
228 this.add(button, "gapleft para, gapright para, growx, sizegroup buttons, wrap para");
232 //// New Y axis plot type
233 button = new JButton(trans.get("simplotpanel.but.NewYaxisplottype"));
234 button.addActionListener(new ActionListener() {
236 public void actionPerformed(ActionEvent e) {
237 if (configuration.getTypeCount() >= 15) {
238 JOptionPane.showMessageDialog(SimulationPlotPanel.this,
239 //// A maximum of 15 plots is allowed.
241 trans.get("simplotpanel.OptionPane.lbl1"),
242 trans.get("simplotpanel.OptionPane.lbl2"),
243 JOptionPane.ERROR_MESSAGE);
247 // Select new type smartly
248 FlightDataType type = null;
249 for (FlightDataType t :
250 simulation.getSimulatedData().getBranch(0).getTypes()) {
252 boolean used = false;
253 if (configuration.getDomainAxisType().equals(t)) {
256 for (int i = 0; i < configuration.getTypeCount(); i++) {
257 if (configuration.getType(i).equals(t)) {
270 type = simulation.getSimulatedData().getBranch(0).getTypes()[0];
274 configuration.addPlotDataType(type);
279 this.add(button, "spanx, split");
282 this.add(new JPanel(), "growx");
285 button = new JButton(trans.get("simplotpanel.but.Plotflight"));
286 button.addActionListener(new ActionListener() {
288 public void actionPerformed(ActionEvent e) {
289 defaultConfiguration = configuration.clone();
290 PlotDialog.showPlot(SwingUtilities.getWindowAncestor(SimulationPlotPanel.this),
291 simulation, configuration);
294 this.add(button, "right");
301 private void setToCustom() {
303 configuration.setName(CUSTOM);
304 configurationSelector.setSelectedItem(CUSTOM_CONFIGURATION);
309 private void updatePlots() {
310 domainTypeSelector.setSelectedItem(configuration.getDomainAxisType());
311 domainUnitSelector.setUnitGroup(configuration.getDomainAxisType().getUnitGroup());
312 domainUnitSelector.setSelectedUnit(configuration.getDomainAxisUnit());
314 typeSelectorPanel.removeAll();
315 for (int i = 0; i < configuration.getTypeCount(); i++) {
316 FlightDataType type = configuration.getType(i);
317 Unit unit = configuration.getUnit(i);
318 int axis = configuration.getAxis(i);
320 typeSelectorPanel.add(new PlotTypeSelector(i, type, unit, axis), "wrap");
323 typeSelectorPanel.repaint();
325 eventTableModel.fireTableDataChanged();
332 * A JPanel which configures a single plot of a PlotConfiguration.
334 private class PlotTypeSelector extends JPanel {
335 private final String[] POSITIONS = { AUTO_NAME, LEFT_NAME, RIGHT_NAME };
337 private final int index;
338 private JComboBox typeSelector;
339 private UnitSelector unitSelector;
340 private JComboBox axisSelector;
343 public PlotTypeSelector(int index, FlightDataType type) {
344 this(index, type, null, -1);
347 public PlotTypeSelector(int plotIndex, FlightDataType type, Unit unit, int position) {
348 super(new MigLayout("ins 0"));
350 this.index = plotIndex;
352 typeSelector = new JComboBox(types);
353 typeSelector.setSelectedItem(type);
354 typeSelector.addItemListener(new ItemListener() {
356 public void itemStateChanged(ItemEvent e) {
359 FlightDataType type = (FlightDataType) typeSelector.getSelectedItem();
360 configuration.setPlotDataType(index, type);
361 unitSelector.setUnitGroup(type.getUnitGroup());
362 unitSelector.setSelectedUnit(configuration.getUnit(index));
366 this.add(typeSelector, "gapright para");
369 this.add(new JLabel(trans.get("simplotpanel.lbl.Unit")));
370 unitSelector = new UnitSelector(type.getUnitGroup());
372 unitSelector.setSelectedUnit(unit);
373 unitSelector.addItemListener(new ItemListener() {
375 public void itemStateChanged(ItemEvent e) {
378 Unit unit = (Unit) unitSelector.getSelectedUnit();
379 configuration.setPlotDataUnit(index, unit);
382 this.add(unitSelector, "width 40lp, gapright para");
385 this.add(new JLabel(trans.get("simplotpanel.lbl.Axis")));
386 axisSelector = new JComboBox(POSITIONS);
387 if (position == LEFT)
388 axisSelector.setSelectedIndex(1);
389 else if (position == RIGHT)
390 axisSelector.setSelectedIndex(2);
392 axisSelector.setSelectedIndex(0);
393 axisSelector.addItemListener(new ItemListener() {
395 public void itemStateChanged(ItemEvent e) {
398 int axis = axisSelector.getSelectedIndex() - 1;
399 configuration.setPlotDataAxis(index, axis);
402 this.add(axisSelector);
405 JButton button = new JButton(Icons.DELETE);
406 //// Remove this plot
407 button.setToolTipText(trans.get("simplotpanel.but.ttip.Removethisplot"));
408 button.setBorderPainted(false);
409 button.addActionListener(new ActionListener() {
411 public void actionPerformed(ActionEvent e) {
412 configuration.removePlotDataType(index);
417 this.add(button, "gapright 0");
423 private class FlightEventTableModel extends AbstractTableModel {
424 private final FlightEvent.Type[] eventTypes;
426 public FlightEventTableModel() {
427 EnumSet<FlightEvent.Type> set = EnumSet.noneOf(FlightEvent.Type.class);
428 for (int i = 0; i < simulation.getSimulatedData().getBranchCount(); i++) {
429 for (FlightEvent e : simulation.getSimulatedData().getBranch(i).getEvents()) {
430 set.add(e.getType());
433 set.remove(FlightEvent.Type.ALTITUDE);
434 int count = set.size();
436 eventTypes = new FlightEvent.Type[count];
438 for (FlightEvent.Type t : FlightEvent.Type.values()) {
439 if (set.contains(t)) {
447 public int getColumnCount() {
452 public int getRowCount() {
453 return eventTypes.length;
457 public Class<?> getColumnClass(int column) {
460 return Boolean.class;
466 throw new IndexOutOfBoundsException("column=" + column);
471 public Object getValueAt(int row, int column) {
474 return new Boolean(configuration.isEventActive(eventTypes[row]));
477 return eventTypes[row].toString();
480 throw new IndexOutOfBoundsException("column=" + column);
485 public boolean isCellEditable(int row, int column) {
490 public void setValueAt(Object value, int row, int column) {
491 if (column != 0 || !(value instanceof Boolean)) {
492 throw new IllegalArgumentException("column=" + column + ", value=" + value);
495 configuration.setEvent(eventTypes[row], (Boolean) value);
496 this.fireTableCellUpdated(row, column);