664d32da21383bb65cd63951b1ef6de9c4d71937
[debian/openrocket] / src / net / sf / openrocket / gui / plot / SimulationPlotPanel.java
1 package net.sf.openrocket.gui.plot;
2
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;
9
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;
22
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
36 /**
37  * Panel that displays the simulation plot options to the user.
38  * 
39  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
40  */
41 public class SimulationPlotPanel extends JPanel {
42         private static final Translator trans = Application.getTranslator();
43         
44         // TODO: LOW: Should these be somewhere else?
45         public static final int AUTO = -1;
46         public static final int LEFT = 0;
47         public static final int RIGHT = 1;
48         
49         //// Auto
50         public static final String AUTO_NAME = trans.get("simplotpanel.AUTO_NAME");
51         //// Left
52         public static final String LEFT_NAME = trans.get("simplotpanel.LEFT_NAME");
53         //// Right
54         public static final String RIGHT_NAME = trans.get("simplotpanel.RIGHT_NAME");
55         
56         //// Custom
57         private static final String CUSTOM = trans.get("simplotpanel.CUSTOM");
58         
59         /** The "Custom" configuration - not to be used for anything other than the title. */
60         private static final PlotConfiguration CUSTOM_CONFIGURATION;
61         static {
62                 CUSTOM_CONFIGURATION = new PlotConfiguration(CUSTOM);
63         }
64         
65         /** The array of presets for the combo box. */
66         private static final PlotConfiguration[] PRESET_ARRAY;
67         static {
68                 PRESET_ARRAY = Arrays.copyOf(PlotConfiguration.DEFAULT_CONFIGURATIONS,
69                                 PlotConfiguration.DEFAULT_CONFIGURATIONS.length + 1);
70                 PRESET_ARRAY[PRESET_ARRAY.length - 1] = CUSTOM_CONFIGURATION;
71         }
72         
73
74
75         /** The current default configuration, set each time a plot is made. */
76         private static PlotConfiguration defaultConfiguration =
77                         PlotConfiguration.DEFAULT_CONFIGURATIONS[0].resetUnits();
78         
79
80         private final Simulation simulation;
81         private final FlightDataType[] types;
82         private PlotConfiguration configuration;
83         
84
85         private JComboBox configurationSelector;
86         
87         private JComboBox domainTypeSelector;
88         private UnitSelector domainUnitSelector;
89         
90         private JPanel typeSelectorPanel;
91         private FlightEventTableModel eventTableModel;
92         
93
94         private int modifying = 0;
95         
96         
97         public SimulationPlotPanel(final Simulation simulation) {
98                 super(new MigLayout("fill"));
99                 
100                 this.simulation = simulation;
101                 if (simulation.getSimulatedData() == null ||
102                                 simulation.getSimulatedData().getBranchCount() == 0) {
103                         throw new IllegalArgumentException("Simulation contains no data.");
104                 }
105                 FlightDataBranch branch = simulation.getSimulatedData().getBranch(0);
106                 types = branch.getTypes();
107                 
108                 // TODO: LOW: Revert to custom if data type is not available.
109                 configuration = defaultConfiguration.clone();
110                 
111
112                 ////  Configuration selector
113                 
114                 // Setup the combo box
115                 configurationSelector = new JComboBox(PRESET_ARRAY);
116                 for (PlotConfiguration config : PRESET_ARRAY) {
117                         if (config.getName().equals(configuration.getName())) {
118                                 configurationSelector.setSelectedItem(config);
119                         }
120                 }
121                 configurationSelector.addItemListener(new ItemListener() {
122                         @Override
123                         public void itemStateChanged(ItemEvent e) {
124                                 if (modifying > 0)
125                                         return;
126                                 PlotConfiguration conf = (PlotConfiguration) configurationSelector.getSelectedItem();
127                                 if (conf == CUSTOM_CONFIGURATION)
128                                         return;
129                                 modifying++;
130                                 configuration = conf.clone().resetUnits();
131                                 updatePlots();
132                                 modifying--;
133                         }
134                 });
135                 //// Preset plot configurations:
136                 this.add(new JLabel(trans.get("simplotpanel.lbl.Presetplotconf")), "spanx, split");
137                 this.add(configurationSelector, "growx, wrap 20lp");
138                 
139
140
141                 //// X axis
142                 
143                 //// X axis type:
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() {
148                         @Override
149                         public void itemStateChanged(ItemEvent e) {
150                                 if (modifying > 0)
151                                         return;
152                                 FlightDataType type = (FlightDataType) domainTypeSelector.getSelectedItem();
153                                 configuration.setDomainAxisType(type);
154                                 domainUnitSelector.setUnitGroup(type.getUnitGroup());
155                                 domainUnitSelector.setSelectedUnit(configuration.getDomainAxisUnit());
156                                 setToCustom();
157                         }
158                 });
159                 this.add(domainTypeSelector, "gapright para");
160                 
161                 //// Unit:
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() {
166                         @Override
167                         public void itemStateChanged(ItemEvent e) {
168                                 if (modifying > 0)
169                                         return;
170                                 configuration.setDomainAxisUnit(domainUnitSelector.getSelectedUnit());
171                         }
172                 });
173                 this.add(domainUnitSelector, "width 40lp, gapright para");
174                 
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");
179                 
180
181
182                 //// Y axis selector panel
183                 //// Y axis types:
184                 this.add(new JLabel(trans.get("simplotpanel.lbl.Yaxistypes")));
185                 //// Flight events:
186                 this.add(new JLabel(trans.get("simplotpanel.lbl.Flightevents")), "wrap rel");
187                 
188                 typeSelectorPanel = new JPanel(new MigLayout("gapy rel"));
189                 JScrollPane scroll = new JScrollPane(typeSelectorPanel);
190                 this.add(scroll, "spany 2, height 10px, grow 100, gapright para");
191                 
192
193                 //// Flight events
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);
200                 
201                 TableColumnModel columnModel = table.getColumnModel();
202                 TableColumn col0 = columnModel.getColumn(0);
203                 int w = table.getRowHeight() + 2;
204                 col0.setMinWidth(w);
205                 col0.setPreferredWidth(w);
206                 col0.setMaxWidth(w);
207                 table.addMouseListener(new GUIUtil.BooleanTableClickListener(table));
208                 this.add(new JScrollPane(table), "height 10px, width 200lp, grow 1, wrap rel");
209                 
210
211                 ////  All + None buttons
212                 JButton button = new JButton(trans.get("simplotpanel.but.All"));
213                 button.addActionListener(new ActionListener() {
214                         @Override
215                         public void actionPerformed(ActionEvent e) {
216                                 for (FlightEvent.Type t : FlightEvent.Type.values())
217                                         configuration.setEvent(t, true);
218                                 eventTableModel.fireTableDataChanged();
219                         }
220                 });
221                 this.add(button, "split 2, gapleft para, gapright para, growx, sizegroup buttons");
222                 
223                 //// None
224                 button = new JButton(trans.get("simplotpanel.but.None"));
225                 button.addActionListener(new ActionListener() {
226                         @Override
227                         public void actionPerformed(ActionEvent e) {
228                                 for (FlightEvent.Type t : FlightEvent.Type.values())
229                                         configuration.setEvent(t, false);
230                                 eventTableModel.fireTableDataChanged();
231                         }
232                 });
233                 this.add(button, "gapleft para, gapright para, growx, sizegroup buttons, wrap para");
234                 
235
236
237                 //// New Y axis plot type
238                 button = new JButton(trans.get("simplotpanel.but.NewYaxisplottype"));
239                 button.addActionListener(new ActionListener() {
240                         @Override
241                         public void actionPerformed(ActionEvent e) {
242                                 if (configuration.getTypeCount() >= 15) {
243                                         JOptionPane.showMessageDialog(SimulationPlotPanel.this,
244                                                         //// A maximum of 15 plots is allowed.
245                                                         //// Cannot add plot
246                                                         trans.get("simplotpanel.OptionPane.lbl1"),
247                                                         trans.get("simplotpanel.OptionPane.lbl2"),
248                                                         JOptionPane.ERROR_MESSAGE);
249                                         return;
250                                 }
251                                 
252                                 // Select new type smartly
253                                 FlightDataType type = null;
254                                 for (FlightDataType t :
255                                         simulation.getSimulatedData().getBranch(0).getTypes()) {
256                                                 
257                                                 boolean used = false;
258                                                 if (configuration.getDomainAxisType().equals(t)) {
259                                                         used = true;
260                                                 } else {
261                                                         for (int i = 0; i < configuration.getTypeCount(); i++) {
262                                                                 if (configuration.getType(i).equals(t)) {
263                                                                         used = true;
264                                                                         break;
265                                                                 }
266                                                         }
267                                                 }
268                                                 
269                                                 if (!used) {
270                                                         type = t;
271                                                         break;
272                                                 }
273                                         }
274                                         if (type == null) {
275                                                 type = simulation.getSimulatedData().getBranch(0).getTypes()[0];
276                                         }
277                                         
278                                         // Add new type
279                                         configuration.addPlotDataType(type);
280                                         setToCustom();
281                                         updatePlots();
282                                 }
283                 });
284                 this.add(button, "spanx, split");
285                 
286
287                 this.add(new JPanel(), "growx");
288                 
289                 //// Plot flight
290                 button = new JButton(trans.get("simplotpanel.but.Plotflight"));
291                 button.addActionListener(new ActionListener() {
292                         @Override
293                         public void actionPerformed(ActionEvent e) {
294                                 defaultConfiguration = configuration.clone();
295                                 SimulationPlotDialog.showPlot(SwingUtilities.getWindowAncestor(SimulationPlotPanel.this),
296                                                 simulation, configuration);
297                         }
298                 });
299                 this.add(button, "right");
300                 
301
302                 updatePlots();
303         }
304         
305         
306         private void setToCustom() {
307                 modifying++;
308                 configuration.setName(CUSTOM);
309                 configurationSelector.setSelectedItem(CUSTOM_CONFIGURATION);
310                 modifying--;
311         }
312         
313         
314         private void updatePlots() {
315                 domainTypeSelector.setSelectedItem(configuration.getDomainAxisType());
316                 domainUnitSelector.setUnitGroup(configuration.getDomainAxisType().getUnitGroup());
317                 domainUnitSelector.setSelectedUnit(configuration.getDomainAxisUnit());
318                 
319                 typeSelectorPanel.removeAll();
320                 for (int i = 0; i < configuration.getTypeCount(); i++) {
321                         FlightDataType type = configuration.getType(i);
322                         Unit unit = configuration.getUnit(i);
323                         int axis = configuration.getAxis(i);
324                         
325                         typeSelectorPanel.add(new PlotTypeSelector(i, type, unit, axis), "wrap");
326                 }
327                 
328                 typeSelectorPanel.repaint();
329                 
330                 eventTableModel.fireTableDataChanged();
331         }
332         
333         
334
335
336         /**
337          * A JPanel which configures a single plot of a PlotConfiguration.
338          */
339         private class PlotTypeSelector extends JPanel {
340                 private final String[] POSITIONS = { AUTO_NAME, LEFT_NAME, RIGHT_NAME };
341                 
342                 private final int index;
343                 private JComboBox typeSelector;
344                 private UnitSelector unitSelector;
345                 private JComboBox axisSelector;
346                 
347                 
348                 public PlotTypeSelector(int index, FlightDataType type) {
349                         this(index, type, null, -1);
350                 }
351                 
352                 public PlotTypeSelector(int plotIndex, FlightDataType type, Unit unit, int position) {
353                         super(new MigLayout("ins 0"));
354                         
355                         this.index = plotIndex;
356                         
357                         typeSelector = new JComboBox(types);
358                         typeSelector.setSelectedItem(type);
359                         typeSelector.addItemListener(new ItemListener() {
360                                 @Override
361                                 public void itemStateChanged(ItemEvent e) {
362                                         if (modifying > 0)
363                                                 return;
364                                         FlightDataType type = (FlightDataType) typeSelector.getSelectedItem();
365                                         configuration.setPlotDataType(index, type);
366                                         unitSelector.setUnitGroup(type.getUnitGroup());
367                                         unitSelector.setSelectedUnit(configuration.getUnit(index));
368                                         setToCustom();
369                                 }
370                         });
371                         this.add(typeSelector, "gapright para");
372                         
373                         //// Unit:
374                         this.add(new JLabel(trans.get("simplotpanel.lbl.Unit")));
375                         unitSelector = new UnitSelector(type.getUnitGroup());
376                         if (unit != null)
377                                 unitSelector.setSelectedUnit(unit);
378                         unitSelector.addItemListener(new ItemListener() {
379                                 @Override
380                                 public void itemStateChanged(ItemEvent e) {
381                                         if (modifying > 0)
382                                                 return;
383                                         Unit unit = unitSelector.getSelectedUnit();
384                                         configuration.setPlotDataUnit(index, unit);
385                                 }
386                         });
387                         this.add(unitSelector, "width 40lp, gapright para");
388                         
389                         //// Axis:
390                         this.add(new JLabel(trans.get("simplotpanel.lbl.Axis")));
391                         axisSelector = new JComboBox(POSITIONS);
392                         if (position == LEFT)
393                                 axisSelector.setSelectedIndex(1);
394                         else if (position == RIGHT)
395                                 axisSelector.setSelectedIndex(2);
396                         else
397                                 axisSelector.setSelectedIndex(0);
398                         axisSelector.addItemListener(new ItemListener() {
399                                 @Override
400                                 public void itemStateChanged(ItemEvent e) {
401                                         if (modifying > 0)
402                                                 return;
403                                         int axis = axisSelector.getSelectedIndex() - 1;
404                                         configuration.setPlotDataAxis(index, axis);
405                                 }
406                         });
407                         this.add(axisSelector);
408                         
409
410                         JButton button = new JButton(Icons.DELETE);
411                         //// Remove this plot
412                         button.setToolTipText(trans.get("simplotpanel.but.ttip.Removethisplot"));
413                         button.setBorderPainted(false);
414                         button.addActionListener(new ActionListener() {
415                                 @Override
416                                 public void actionPerformed(ActionEvent e) {
417                                         configuration.removePlotDataType(index);
418                                         setToCustom();
419                                         updatePlots();
420                                 }
421                         });
422                         this.add(button, "gapright 0");
423                 }
424         }
425         
426         
427
428         private class FlightEventTableModel extends AbstractTableModel {
429                 private final FlightEvent.Type[] eventTypes;
430                 
431                 public FlightEventTableModel() {
432                         EnumSet<FlightEvent.Type> set = EnumSet.noneOf(FlightEvent.Type.class);
433                         for (int i = 0; i < simulation.getSimulatedData().getBranchCount(); i++) {
434                                 for (FlightEvent e : simulation.getSimulatedData().getBranch(i).getEvents()) {
435                                         set.add(e.getType());
436                                 }
437                         }
438                         set.remove(FlightEvent.Type.ALTITUDE);
439                         int count = set.size();
440                         
441                         eventTypes = new FlightEvent.Type[count];
442                         int pos = 0;
443                         for (FlightEvent.Type t : FlightEvent.Type.values()) {
444                                 if (set.contains(t)) {
445                                         eventTypes[pos] = t;
446                                         pos++;
447                                 }
448                         }
449                 }
450                 
451                 @Override
452                 public int getColumnCount() {
453                         return 2;
454                 }
455                 
456                 @Override
457                 public int getRowCount() {
458                         return eventTypes.length;
459                 }
460                 
461                 @Override
462                 public Class<?> getColumnClass(int column) {
463                         switch (column) {
464                         case 0:
465                                 return Boolean.class;
466                                 
467                         case 1:
468                                 return String.class;
469                                 
470                         default:
471                                 throw new IndexOutOfBoundsException("column=" + column);
472                         }
473                 }
474                 
475                 @Override
476                 public Object getValueAt(int row, int column) {
477                         switch (column) {
478                         case 0:
479                                 return new Boolean(configuration.isEventActive(eventTypes[row]));
480                                 
481                         case 1:
482                                 return eventTypes[row].toString();
483                                 
484                         default:
485                                 throw new IndexOutOfBoundsException("column=" + column);
486                         }
487                 }
488                 
489                 @Override
490                 public boolean isCellEditable(int row, int column) {
491                         return column == 0;
492                 }
493                 
494                 @Override
495                 public void setValueAt(Object value, int row, int column) {
496                         if (column != 0 || !(value instanceof Boolean)) {
497                                 throw new IllegalArgumentException("column=" + column + ", value=" + value);
498                         }
499                         
500                         configuration.setEvent(eventTypes[row], (Boolean) value);
501                         this.fireTableCellUpdated(row, column);
502                 }
503         }
504 }