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.gui.util.GUIUtil;
28 import net.sf.openrocket.gui.util.Icons;
29 import net.sf.openrocket.l10n.Translator;
30 import net.sf.openrocket.simulation.FlightDataBranch;
31 import net.sf.openrocket.simulation.FlightDataType;
32 import net.sf.openrocket.simulation.FlightEvent;
33 import net.sf.openrocket.startup.Application;
34 import net.sf.openrocket.unit.Unit;
35 import net.sf.openrocket.util.Utils;
38 * Panel that displays the simulation plot options to the user.
40 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
42 public class SimulationPlotPanel extends JPanel {
43 private static final Translator trans = Application.getTranslator();
45 // TODO: LOW: Should these be somewhere else?
46 public static final int AUTO = -1;
47 public static final int LEFT = 0;
48 public static final int RIGHT = 1;
51 public static final String AUTO_NAME = trans.get("simplotpanel.AUTO_NAME");
53 public static final String LEFT_NAME = trans.get("simplotpanel.LEFT_NAME");
55 public static final String RIGHT_NAME = trans.get("simplotpanel.RIGHT_NAME");
58 private static final String CUSTOM = trans.get("simplotpanel.CUSTOM");
60 /** The "Custom" configuration - not to be used for anything other than the title. */
61 private static final PlotConfiguration CUSTOM_CONFIGURATION;
63 CUSTOM_CONFIGURATION = new PlotConfiguration(CUSTOM);
66 /** The array of presets for the combo box. */
67 private static final PlotConfiguration[] PRESET_ARRAY;
69 PRESET_ARRAY = Arrays.copyOf(PlotConfiguration.DEFAULT_CONFIGURATIONS,
70 PlotConfiguration.DEFAULT_CONFIGURATIONS.length + 1);
71 PRESET_ARRAY[PRESET_ARRAY.length - 1] = CUSTOM_CONFIGURATION;
76 /** The current default configuration, set each time a plot is made. */
77 private static PlotConfiguration defaultConfiguration =
78 PlotConfiguration.DEFAULT_CONFIGURATIONS[0].resetUnits();
81 private final Simulation simulation;
82 private final FlightDataType[] types;
83 private PlotConfiguration configuration;
86 private JComboBox configurationSelector;
88 private JComboBox domainTypeSelector;
89 private UnitSelector domainUnitSelector;
91 private JPanel typeSelectorPanel;
92 private FlightEventTableModel eventTableModel;
95 private int modifying = 0;
98 public SimulationPlotPanel(final Simulation simulation) {
99 super(new MigLayout("fill"));
101 this.simulation = simulation;
102 if (simulation.getSimulatedData() == null ||
103 simulation.getSimulatedData().getBranchCount() == 0) {
104 throw new IllegalArgumentException("Simulation contains no data.");
106 FlightDataBranch branch = simulation.getSimulatedData().getBranch(0);
107 types = branch.getTypes();
109 setConfiguration(defaultConfiguration);
111 //// Configuration selector
113 // Setup the combo box
114 configurationSelector = new JComboBox(PRESET_ARRAY);
115 for (PlotConfiguration config : PRESET_ARRAY) {
116 if (config.getName().equals(configuration.getName())) {
117 configurationSelector.setSelectedItem(config);
121 configurationSelector.addItemListener(new ItemListener() {
123 public void itemStateChanged(ItemEvent e) {
126 PlotConfiguration conf = (PlotConfiguration) configurationSelector.getSelectedItem();
127 if (conf == CUSTOM_CONFIGURATION)
130 setConfiguration(conf.clone().resetUnits());
135 //// Preset plot configurations:
136 this.add(new JLabel(trans.get("simplotpanel.lbl.Presetplotconf")), "spanx, split");
137 this.add(configurationSelector, "growx, wrap 20lp");
144 this.add(new JLabel(trans.get("simplotpanel.lbl.Xaxistype")), "spanx, split");
145 domainTypeSelector = new JComboBox(types);
146 domainTypeSelector.setSelectedItem(configuration.getDomainAxisType());
147 domainTypeSelector.addItemListener(new ItemListener() {
149 public void itemStateChanged(ItemEvent e) {
152 FlightDataType type = (FlightDataType) domainTypeSelector.getSelectedItem();
153 configuration.setDomainAxisType(type);
154 domainUnitSelector.setUnitGroup(type.getUnitGroup());
155 domainUnitSelector.setSelectedUnit(configuration.getDomainAxisUnit());
159 this.add(domainTypeSelector, "gapright para");
162 this.add(new JLabel(trans.get("simplotpanel.lbl.Unit")));
163 domainUnitSelector = new UnitSelector(configuration.getDomainAxisType().getUnitGroup());
164 domainUnitSelector.setSelectedUnit(configuration.getDomainAxisUnit());
165 domainUnitSelector.addItemListener(new ItemListener() {
167 public void itemStateChanged(ItemEvent e) {
170 configuration.setDomainAxisUnit(domainUnitSelector.getSelectedUnit());
173 this.add(domainUnitSelector, "width 40lp, gapright para");
175 //// The data will be plotted in time order even if the X axis type is not time.
176 DescriptionArea desc = new DescriptionArea(trans.get("simplotpanel.Desc"), 2, -2f);
177 desc.setViewportBorder(BorderFactory.createEmptyBorder());
178 this.add(desc, "width 1px, growx 1, wrap unrel");
182 //// Y axis selector panel
184 this.add(new JLabel(trans.get("simplotpanel.lbl.Yaxistypes")));
186 this.add(new JLabel(trans.get("simplotpanel.lbl.Flightevents")), "wrap rel");
188 typeSelectorPanel = new JPanel(new MigLayout("gapy rel"));
189 JScrollPane scroll = new JScrollPane(typeSelectorPanel);
190 this.add(scroll, "spany 2, height 10px, wmin 400lp, grow 100, gapright para");
194 eventTableModel = new FlightEventTableModel();
195 JTable table = new JTable(eventTableModel);
196 table.setTableHeader(null);
197 table.setShowVerticalLines(false);
198 table.setRowSelectionAllowed(false);
199 table.setColumnSelectionAllowed(false);
201 TableColumnModel columnModel = table.getColumnModel();
202 TableColumn col0 = columnModel.getColumn(0);
203 int w = table.getRowHeight() + 2;
205 col0.setPreferredWidth(w);
207 table.addMouseListener(new GUIUtil.BooleanTableClickListener(table));
208 this.add(new JScrollPane(table), "height 10px, width 200lp, grow 1, wrap rel");
211 //// All + None buttons
212 JButton button = new JButton(trans.get("simplotpanel.but.All"));
213 button.addActionListener(new ActionListener() {
215 public void actionPerformed(ActionEvent e) {
216 for (FlightEvent.Type t : FlightEvent.Type.values())
217 configuration.setEvent(t, true);
218 eventTableModel.fireTableDataChanged();
221 this.add(button, "split 2, gapleft para, gapright para, growx, sizegroup buttons");
224 button = new JButton(trans.get("simplotpanel.but.None"));
225 button.addActionListener(new ActionListener() {
227 public void actionPerformed(ActionEvent e) {
228 for (FlightEvent.Type t : FlightEvent.Type.values())
229 configuration.setEvent(t, false);
230 eventTableModel.fireTableDataChanged();
233 this.add(button, "gapleft para, gapright para, growx, sizegroup buttons, wrap para");
237 //// New Y axis plot type
238 button = new JButton(trans.get("simplotpanel.but.NewYaxisplottype"));
239 button.addActionListener(new ActionListener() {
241 public void actionPerformed(ActionEvent e) {
242 if (configuration.getTypeCount() >= 15) {
243 JOptionPane.showMessageDialog(SimulationPlotPanel.this,
244 //// A maximum of 15 plots is allowed.
246 trans.get("simplotpanel.OptionPane.lbl1"),
247 trans.get("simplotpanel.OptionPane.lbl2"),
248 JOptionPane.ERROR_MESSAGE);
252 // Select new type smartly
253 FlightDataType type = null;
254 for (FlightDataType t :
255 simulation.getSimulatedData().getBranch(0).getTypes()) {
257 boolean used = false;
258 if (configuration.getDomainAxisType().equals(t)) {
261 for (int i = 0; i < configuration.getTypeCount(); i++) {
262 if (configuration.getType(i).equals(t)) {
275 type = simulation.getSimulatedData().getBranch(0).getTypes()[0];
279 configuration.addPlotDataType(type);
284 this.add(button, "spanx, split");
287 this.add(new JPanel(), "growx");
290 button = new JButton(trans.get("simplotpanel.but.Plotflight"));
291 button.addActionListener(new ActionListener() {
293 public void actionPerformed(ActionEvent e) {
294 if (configuration.getTypeCount() == 0) {
295 JOptionPane.showMessageDialog(SimulationPlotPanel.this,
296 trans.get("error.noPlotSelected"),
297 trans.get("error.noPlotSelected.title"),
298 JOptionPane.ERROR_MESSAGE);
301 defaultConfiguration = configuration.clone();
302 SimulationPlotDialog.showPlot(SwingUtilities.getWindowAncestor(SimulationPlotPanel.this),
303 simulation, configuration);
306 this.add(button, "right");
313 private void setConfiguration(PlotConfiguration conf) {
315 boolean modified = false;
317 configuration = conf.clone();
318 if (!Utils.contains(types, configuration.getDomainAxisType())) {
319 configuration.setDomainAxisType(types[0]);
323 for (int i = 0; i < configuration.getTypeCount(); i++) {
324 if (!Utils.contains(types, configuration.getType(i))) {
325 configuration.removePlotDataType(i);
332 configuration.setName(CUSTOM);
338 private void setToCustom() {
340 configuration.setName(CUSTOM);
341 configurationSelector.setSelectedItem(CUSTOM_CONFIGURATION);
346 private void updatePlots() {
347 domainTypeSelector.setSelectedItem(configuration.getDomainAxisType());
348 domainUnitSelector.setUnitGroup(configuration.getDomainAxisType().getUnitGroup());
349 domainUnitSelector.setSelectedUnit(configuration.getDomainAxisUnit());
351 typeSelectorPanel.removeAll();
352 for (int i = 0; i < configuration.getTypeCount(); i++) {
353 FlightDataType type = configuration.getType(i);
354 Unit unit = configuration.getUnit(i);
355 int axis = configuration.getAxis(i);
357 typeSelectorPanel.add(new PlotTypeSelector(i, type, unit, axis), "wrap");
360 typeSelectorPanel.repaint();
362 eventTableModel.fireTableDataChanged();
369 * A JPanel which configures a single plot of a PlotConfiguration.
371 private class PlotTypeSelector extends JPanel {
372 private final String[] POSITIONS = { AUTO_NAME, LEFT_NAME, RIGHT_NAME };
374 private final int index;
375 private JComboBox typeSelector;
376 private UnitSelector unitSelector;
377 private JComboBox axisSelector;
380 public PlotTypeSelector(int plotIndex, FlightDataType type, Unit unit, int position) {
381 super(new MigLayout("ins 0"));
383 this.index = plotIndex;
385 typeSelector = new JComboBox(types);
386 typeSelector.setSelectedItem(type);
387 typeSelector.addItemListener(new ItemListener() {
389 public void itemStateChanged(ItemEvent e) {
392 FlightDataType type = (FlightDataType) typeSelector.getSelectedItem();
393 configuration.setPlotDataType(index, type);
394 unitSelector.setUnitGroup(type.getUnitGroup());
395 unitSelector.setSelectedUnit(configuration.getUnit(index));
399 this.add(typeSelector, "gapright para");
402 this.add(new JLabel(trans.get("simplotpanel.lbl.Unit")));
403 unitSelector = new UnitSelector(type.getUnitGroup());
405 unitSelector.setSelectedUnit(unit);
406 unitSelector.addItemListener(new ItemListener() {
408 public void itemStateChanged(ItemEvent e) {
411 Unit unit = unitSelector.getSelectedUnit();
412 configuration.setPlotDataUnit(index, unit);
415 this.add(unitSelector, "width 40lp, gapright para");
418 this.add(new JLabel(trans.get("simplotpanel.lbl.Axis")));
419 axisSelector = new JComboBox(POSITIONS);
420 if (position == LEFT)
421 axisSelector.setSelectedIndex(1);
422 else if (position == RIGHT)
423 axisSelector.setSelectedIndex(2);
425 axisSelector.setSelectedIndex(0);
426 axisSelector.addItemListener(new ItemListener() {
428 public void itemStateChanged(ItemEvent e) {
431 int axis = axisSelector.getSelectedIndex() - 1;
432 configuration.setPlotDataAxis(index, axis);
435 this.add(axisSelector);
438 JButton button = new JButton(Icons.DELETE);
439 //// Remove this plot
440 button.setToolTipText(trans.get("simplotpanel.but.ttip.Removethisplot"));
441 button.setBorderPainted(false);
442 button.addActionListener(new ActionListener() {
444 public void actionPerformed(ActionEvent e) {
445 configuration.removePlotDataType(index);
450 this.add(button, "gapright 0");
456 private class FlightEventTableModel extends AbstractTableModel {
457 private final FlightEvent.Type[] eventTypes;
459 public FlightEventTableModel() {
460 EnumSet<FlightEvent.Type> set = EnumSet.noneOf(FlightEvent.Type.class);
461 for (int i = 0; i < simulation.getSimulatedData().getBranchCount(); i++) {
462 for (FlightEvent e : simulation.getSimulatedData().getBranch(i).getEvents()) {
463 set.add(e.getType());
466 set.remove(FlightEvent.Type.ALTITUDE);
467 int count = set.size();
469 eventTypes = new FlightEvent.Type[count];
471 for (FlightEvent.Type t : FlightEvent.Type.values()) {
472 if (set.contains(t)) {
480 public int getColumnCount() {
485 public int getRowCount() {
486 return eventTypes.length;
490 public Class<?> getColumnClass(int column) {
493 return Boolean.class;
499 throw new IndexOutOfBoundsException("column=" + column);
504 public Object getValueAt(int row, int column) {
507 return new Boolean(configuration.isEventActive(eventTypes[row]));
510 return eventTypes[row].toString();
513 throw new IndexOutOfBoundsException("column=" + column);
518 public boolean isCellEditable(int row, int column) {
523 public void setValueAt(Object value, int row, int column) {
524 if (column != 0 || !(value instanceof Boolean)) {
525 throw new IllegalArgumentException("column=" + column + ", value=" + value);
528 configuration.setEvent(eventTypes[row], (Boolean) value);
529 this.fireTableCellUpdated(row, column);