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;
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 // FIXME: Bugs when expected branch is not present
123 configurationSelector.addItemListener(new ItemListener() {
125 public void itemStateChanged(ItemEvent e) {
128 PlotConfiguration conf = (PlotConfiguration) configurationSelector.getSelectedItem();
129 if (conf == CUSTOM_CONFIGURATION)
132 setConfiguration(conf.clone().resetUnits());
137 //// Preset plot configurations:
138 this.add(new JLabel(trans.get("simplotpanel.lbl.Presetplotconf")), "spanx, split");
139 this.add(configurationSelector, "growx, wrap 20lp");
146 this.add(new JLabel(trans.get("simplotpanel.lbl.Xaxistype")), "spanx, split");
147 domainTypeSelector = new JComboBox(types);
148 domainTypeSelector.setSelectedItem(configuration.getDomainAxisType());
149 domainTypeSelector.addItemListener(new ItemListener() {
151 public void itemStateChanged(ItemEvent e) {
154 FlightDataType type = (FlightDataType) domainTypeSelector.getSelectedItem();
155 configuration.setDomainAxisType(type);
156 domainUnitSelector.setUnitGroup(type.getUnitGroup());
157 domainUnitSelector.setSelectedUnit(configuration.getDomainAxisUnit());
161 this.add(domainTypeSelector, "gapright para");
164 this.add(new JLabel(trans.get("simplotpanel.lbl.Unit")));
165 domainUnitSelector = new UnitSelector(configuration.getDomainAxisType().getUnitGroup());
166 domainUnitSelector.setSelectedUnit(configuration.getDomainAxisUnit());
167 domainUnitSelector.addItemListener(new ItemListener() {
169 public void itemStateChanged(ItemEvent e) {
172 configuration.setDomainAxisUnit(domainUnitSelector.getSelectedUnit());
175 this.add(domainUnitSelector, "width 40lp, gapright para");
177 //// The data will be plotted in time order even if the X axis type is not time.
178 DescriptionArea desc = new DescriptionArea(trans.get("simplotpanel.Desc"), 2, -2f);
179 desc.setViewportBorder(BorderFactory.createEmptyBorder());
180 this.add(desc, "width 1px, growx 1, wrap unrel");
184 //// Y axis selector panel
186 this.add(new JLabel(trans.get("simplotpanel.lbl.Yaxistypes")));
188 this.add(new JLabel(trans.get("simplotpanel.lbl.Flightevents")), "wrap rel");
190 typeSelectorPanel = new JPanel(new MigLayout("gapy rel"));
191 JScrollPane scroll = new JScrollPane(typeSelectorPanel);
192 this.add(scroll, "spany 2, height 10px, grow 100, gapright para");
196 eventTableModel = new FlightEventTableModel();
197 JTable table = new JTable(eventTableModel);
198 table.setTableHeader(null);
199 table.setShowVerticalLines(false);
200 table.setRowSelectionAllowed(false);
201 table.setColumnSelectionAllowed(false);
203 TableColumnModel columnModel = table.getColumnModel();
204 TableColumn col0 = columnModel.getColumn(0);
205 int w = table.getRowHeight() + 2;
207 col0.setPreferredWidth(w);
209 table.addMouseListener(new GUIUtil.BooleanTableClickListener(table));
210 this.add(new JScrollPane(table), "height 10px, width 200lp, grow 1, wrap rel");
213 //// All + None buttons
214 JButton button = new JButton(trans.get("simplotpanel.but.All"));
215 button.addActionListener(new ActionListener() {
217 public void actionPerformed(ActionEvent e) {
218 for (FlightEvent.Type t : FlightEvent.Type.values())
219 configuration.setEvent(t, true);
220 eventTableModel.fireTableDataChanged();
223 this.add(button, "split 2, gapleft para, gapright para, growx, sizegroup buttons");
226 button = new JButton(trans.get("simplotpanel.but.None"));
227 button.addActionListener(new ActionListener() {
229 public void actionPerformed(ActionEvent e) {
230 for (FlightEvent.Type t : FlightEvent.Type.values())
231 configuration.setEvent(t, false);
232 eventTableModel.fireTableDataChanged();
235 this.add(button, "gapleft para, gapright para, growx, sizegroup buttons, wrap para");
239 //// New Y axis plot type
240 button = new JButton(trans.get("simplotpanel.but.NewYaxisplottype"));
241 button.addActionListener(new ActionListener() {
243 public void actionPerformed(ActionEvent e) {
244 if (configuration.getTypeCount() >= 15) {
245 JOptionPane.showMessageDialog(SimulationPlotPanel.this,
246 //// A maximum of 15 plots is allowed.
248 trans.get("simplotpanel.OptionPane.lbl1"),
249 trans.get("simplotpanel.OptionPane.lbl2"),
250 JOptionPane.ERROR_MESSAGE);
254 // Select new type smartly
255 FlightDataType type = null;
256 for (FlightDataType t :
257 simulation.getSimulatedData().getBranch(0).getTypes()) {
259 boolean used = false;
260 if (configuration.getDomainAxisType().equals(t)) {
263 for (int i = 0; i < configuration.getTypeCount(); i++) {
264 if (configuration.getType(i).equals(t)) {
277 type = simulation.getSimulatedData().getBranch(0).getTypes()[0];
281 configuration.addPlotDataType(type);
286 this.add(button, "spanx, split");
289 this.add(new JPanel(), "growx");
292 button = new JButton(trans.get("simplotpanel.but.Plotflight"));
293 button.addActionListener(new ActionListener() {
295 public void actionPerformed(ActionEvent e) {
296 if (configuration.getTypeCount() == 0) {
297 JOptionPane.showMessageDialog(SimulationPlotPanel.this,
298 trans.get("error.noPlotSelected"),
299 trans.get("error.noPlotSelected.title"),
300 JOptionPane.ERROR_MESSAGE);
303 defaultConfiguration = configuration.clone();
304 SimulationPlotDialog.showPlot(SwingUtilities.getWindowAncestor(SimulationPlotPanel.this),
305 simulation, configuration);
308 this.add(button, "right");
315 private void setConfiguration(PlotConfiguration conf) {
317 boolean modified = false;
319 configuration = conf.clone();
320 if (!Utils.contains(types, configuration.getDomainAxisType())) {
321 configuration.setDomainAxisType(types[0]);
325 for (int i = 0; i < configuration.getTypeCount(); i++) {
326 if (!Utils.contains(types, configuration.getType(i))) {
327 configuration.removePlotDataType(i);
334 configuration.setName(CUSTOM);
340 private void setToCustom() {
342 configuration.setName(CUSTOM);
343 configurationSelector.setSelectedItem(CUSTOM_CONFIGURATION);
348 private void updatePlots() {
349 domainTypeSelector.setSelectedItem(configuration.getDomainAxisType());
350 domainUnitSelector.setUnitGroup(configuration.getDomainAxisType().getUnitGroup());
351 domainUnitSelector.setSelectedUnit(configuration.getDomainAxisUnit());
353 typeSelectorPanel.removeAll();
354 for (int i = 0; i < configuration.getTypeCount(); i++) {
355 FlightDataType type = configuration.getType(i);
356 Unit unit = configuration.getUnit(i);
357 int axis = configuration.getAxis(i);
359 typeSelectorPanel.add(new PlotTypeSelector(i, type, unit, axis), "wrap");
362 typeSelectorPanel.repaint();
364 eventTableModel.fireTableDataChanged();
371 * A JPanel which configures a single plot of a PlotConfiguration.
373 private class PlotTypeSelector extends JPanel {
374 private final String[] POSITIONS = { AUTO_NAME, LEFT_NAME, RIGHT_NAME };
376 private final int index;
377 private JComboBox typeSelector;
378 private UnitSelector unitSelector;
379 private JComboBox axisSelector;
382 public PlotTypeSelector(int index, FlightDataType type) {
383 this(index, type, null, -1);
386 public PlotTypeSelector(int plotIndex, FlightDataType type, Unit unit, int position) {
387 super(new MigLayout("ins 0"));
389 this.index = plotIndex;
391 typeSelector = new JComboBox(types);
392 typeSelector.setSelectedItem(type);
393 typeSelector.addItemListener(new ItemListener() {
395 public void itemStateChanged(ItemEvent e) {
398 FlightDataType type = (FlightDataType) typeSelector.getSelectedItem();
399 configuration.setPlotDataType(index, type);
400 unitSelector.setUnitGroup(type.getUnitGroup());
401 unitSelector.setSelectedUnit(configuration.getUnit(index));
405 this.add(typeSelector, "gapright para");
408 this.add(new JLabel(trans.get("simplotpanel.lbl.Unit")));
409 unitSelector = new UnitSelector(type.getUnitGroup());
411 unitSelector.setSelectedUnit(unit);
412 unitSelector.addItemListener(new ItemListener() {
414 public void itemStateChanged(ItemEvent e) {
417 Unit unit = unitSelector.getSelectedUnit();
418 configuration.setPlotDataUnit(index, unit);
421 this.add(unitSelector, "width 40lp, gapright para");
424 this.add(new JLabel(trans.get("simplotpanel.lbl.Axis")));
425 axisSelector = new JComboBox(POSITIONS);
426 if (position == LEFT)
427 axisSelector.setSelectedIndex(1);
428 else if (position == RIGHT)
429 axisSelector.setSelectedIndex(2);
431 axisSelector.setSelectedIndex(0);
432 axisSelector.addItemListener(new ItemListener() {
434 public void itemStateChanged(ItemEvent e) {
437 int axis = axisSelector.getSelectedIndex() - 1;
438 configuration.setPlotDataAxis(index, axis);
441 this.add(axisSelector);
444 JButton button = new JButton(Icons.DELETE);
445 //// Remove this plot
446 button.setToolTipText(trans.get("simplotpanel.but.ttip.Removethisplot"));
447 button.setBorderPainted(false);
448 button.addActionListener(new ActionListener() {
450 public void actionPerformed(ActionEvent e) {
451 configuration.removePlotDataType(index);
456 this.add(button, "gapright 0");
462 private class FlightEventTableModel extends AbstractTableModel {
463 private final FlightEvent.Type[] eventTypes;
465 public FlightEventTableModel() {
466 EnumSet<FlightEvent.Type> set = EnumSet.noneOf(FlightEvent.Type.class);
467 for (int i = 0; i < simulation.getSimulatedData().getBranchCount(); i++) {
468 for (FlightEvent e : simulation.getSimulatedData().getBranch(i).getEvents()) {
469 set.add(e.getType());
472 set.remove(FlightEvent.Type.ALTITUDE);
473 int count = set.size();
475 eventTypes = new FlightEvent.Type[count];
477 for (FlightEvent.Type t : FlightEvent.Type.values()) {
478 if (set.contains(t)) {
486 public int getColumnCount() {
491 public int getRowCount() {
492 return eventTypes.length;
496 public Class<?> getColumnClass(int column) {
499 return Boolean.class;
505 throw new IndexOutOfBoundsException("column=" + column);
510 public Object getValueAt(int row, int column) {
513 return new Boolean(configuration.isEventActive(eventTypes[row]));
516 return eventTypes[row].toString();
519 throw new IndexOutOfBoundsException("column=" + column);
524 public boolean isCellEditable(int row, int column) {
529 public void setValueAt(Object value, int row, int column) {
530 if (column != 0 || !(value instanceof Boolean)) {
531 throw new IllegalArgumentException("column=" + column + ", value=" + value);
534 configuration.setEvent(eventTypes[row], (Boolean) value);
535 this.fireTableCellUpdated(row, column);