06544f52676938211927e56925044e36a4995b9e
[debian/openrocket] / core / 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.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;
36
37 /**
38  * Panel that displays the simulation plot options to the user.
39  * 
40  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
41  */
42 public class SimulationPlotPanel extends JPanel {
43         private static final Translator trans = Application.getTranslator();
44         
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;
49         
50         //// Auto
51         public static final String AUTO_NAME = trans.get("simplotpanel.AUTO_NAME");
52         //// Left
53         public static final String LEFT_NAME = trans.get("simplotpanel.LEFT_NAME");
54         //// Right
55         public static final String RIGHT_NAME = trans.get("simplotpanel.RIGHT_NAME");
56         
57         //// Custom
58         private static final String CUSTOM = trans.get("simplotpanel.CUSTOM");
59         
60         /** The "Custom" configuration - not to be used for anything other than the title. */
61         private static final PlotConfiguration CUSTOM_CONFIGURATION;
62         static {
63                 CUSTOM_CONFIGURATION = new PlotConfiguration(CUSTOM);
64         }
65         
66         /** The array of presets for the combo box. */
67         private static final PlotConfiguration[] PRESET_ARRAY;
68         static {
69                 PRESET_ARRAY = Arrays.copyOf(PlotConfiguration.DEFAULT_CONFIGURATIONS,
70                                 PlotConfiguration.DEFAULT_CONFIGURATIONS.length + 1);
71                 PRESET_ARRAY[PRESET_ARRAY.length - 1] = CUSTOM_CONFIGURATION;
72         }
73         
74
75
76         /** The current default configuration, set each time a plot is made. */
77         private static PlotConfiguration defaultConfiguration =
78                         PlotConfiguration.DEFAULT_CONFIGURATIONS[0].resetUnits();
79         
80
81         private final Simulation simulation;
82         private final FlightDataType[] types;
83         private PlotConfiguration configuration;
84         
85
86         private JComboBox configurationSelector;
87         
88         private JComboBox domainTypeSelector;
89         private UnitSelector domainUnitSelector;
90         
91         private JPanel typeSelectorPanel;
92         private FlightEventTableModel eventTableModel;
93         
94
95         private int modifying = 0;
96         
97         
98         public SimulationPlotPanel(final Simulation simulation) {
99                 super(new MigLayout("fill"));
100                 
101                 this.simulation = simulation;
102                 if (simulation.getSimulatedData() == null ||
103                                 simulation.getSimulatedData().getBranchCount() == 0) {
104                         throw new IllegalArgumentException("Simulation contains no data.");
105                 }
106                 FlightDataBranch branch = simulation.getSimulatedData().getBranch(0);
107                 types = branch.getTypes();
108                 
109                 setConfiguration(defaultConfiguration);
110                 
111                 ////  Configuration selector
112                 
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);
118                         }
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                                 setConfiguration(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, wmin 400lp, 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                                 if (configuration.getTypeCount() == 0) {
295                                         JOptionPane.showMessageDialog(SimulationPlotPanel.this,
296                                                         trans.get("error.noPlotSelected"),
297                                                         trans.get("error.noPlotSelected.title"),
298                                                         JOptionPane.ERROR_MESSAGE);
299                                         return;
300                                 }
301                                 defaultConfiguration = configuration.clone();
302                                 SimulationPlotDialog.showPlot(SwingUtilities.getWindowAncestor(SimulationPlotPanel.this),
303                                                 simulation, configuration);
304                         }
305                 });
306                 this.add(button, "right");
307                 
308
309                 updatePlots();
310         }
311         
312         
313         private void setConfiguration(PlotConfiguration conf) {
314                 
315                 boolean modified = false;
316                 
317                 configuration = conf.clone();
318                 if (!Utils.contains(types, configuration.getDomainAxisType())) {
319                         configuration.setDomainAxisType(types[0]);
320                         modified = true;
321                 }
322                 
323                 for (int i = 0; i < configuration.getTypeCount(); i++) {
324                         if (!Utils.contains(types, configuration.getType(i))) {
325                                 configuration.removePlotDataType(i);
326                                 i--;
327                                 modified = true;
328                         }
329                 }
330                 
331                 if (modified) {
332                         configuration.setName(CUSTOM);
333                 }
334                 
335         }
336         
337         
338         private void setToCustom() {
339                 modifying++;
340                 configuration.setName(CUSTOM);
341                 configurationSelector.setSelectedItem(CUSTOM_CONFIGURATION);
342                 modifying--;
343         }
344         
345         
346         private void updatePlots() {
347                 domainTypeSelector.setSelectedItem(configuration.getDomainAxisType());
348                 domainUnitSelector.setUnitGroup(configuration.getDomainAxisType().getUnitGroup());
349                 domainUnitSelector.setSelectedUnit(configuration.getDomainAxisUnit());
350                 
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);
356                         
357                         typeSelectorPanel.add(new PlotTypeSelector(i, type, unit, axis), "wrap");
358                 }
359                 
360                 typeSelectorPanel.repaint();
361                 
362                 eventTableModel.fireTableDataChanged();
363         }
364         
365         
366
367
368         /**
369          * A JPanel which configures a single plot of a PlotConfiguration.
370          */
371         private class PlotTypeSelector extends JPanel {
372                 private final String[] POSITIONS = { AUTO_NAME, LEFT_NAME, RIGHT_NAME };
373                 
374                 private final int index;
375                 private JComboBox typeSelector;
376                 private UnitSelector unitSelector;
377                 private JComboBox axisSelector;
378                 
379                 
380                 public PlotTypeSelector(int plotIndex, FlightDataType type, Unit unit, int position) {
381                         super(new MigLayout("ins 0"));
382                         
383                         this.index = plotIndex;
384                         
385                         typeSelector = new JComboBox(types);
386                         typeSelector.setSelectedItem(type);
387                         typeSelector.addItemListener(new ItemListener() {
388                                 @Override
389                                 public void itemStateChanged(ItemEvent e) {
390                                         if (modifying > 0)
391                                                 return;
392                                         FlightDataType type = (FlightDataType) typeSelector.getSelectedItem();
393                                         configuration.setPlotDataType(index, type);
394                                         unitSelector.setUnitGroup(type.getUnitGroup());
395                                         unitSelector.setSelectedUnit(configuration.getUnit(index));
396                                         setToCustom();
397                                 }
398                         });
399                         this.add(typeSelector, "gapright para");
400                         
401                         //// Unit:
402                         this.add(new JLabel(trans.get("simplotpanel.lbl.Unit")));
403                         unitSelector = new UnitSelector(type.getUnitGroup());
404                         if (unit != null)
405                                 unitSelector.setSelectedUnit(unit);
406                         unitSelector.addItemListener(new ItemListener() {
407                                 @Override
408                                 public void itemStateChanged(ItemEvent e) {
409                                         if (modifying > 0)
410                                                 return;
411                                         Unit unit = unitSelector.getSelectedUnit();
412                                         configuration.setPlotDataUnit(index, unit);
413                                 }
414                         });
415                         this.add(unitSelector, "width 40lp, gapright para");
416                         
417                         //// Axis:
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);
424                         else
425                                 axisSelector.setSelectedIndex(0);
426                         axisSelector.addItemListener(new ItemListener() {
427                                 @Override
428                                 public void itemStateChanged(ItemEvent e) {
429                                         if (modifying > 0)
430                                                 return;
431                                         int axis = axisSelector.getSelectedIndex() - 1;
432                                         configuration.setPlotDataAxis(index, axis);
433                                 }
434                         });
435                         this.add(axisSelector);
436                         
437
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() {
443                                 @Override
444                                 public void actionPerformed(ActionEvent e) {
445                                         configuration.removePlotDataType(index);
446                                         setToCustom();
447                                         updatePlots();
448                                 }
449                         });
450                         this.add(button, "gapright 0");
451                 }
452         }
453         
454         
455
456         private class FlightEventTableModel extends AbstractTableModel {
457                 private final FlightEvent.Type[] eventTypes;
458                 
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());
464                                 }
465                         }
466                         set.remove(FlightEvent.Type.ALTITUDE);
467                         int count = set.size();
468                         
469                         eventTypes = new FlightEvent.Type[count];
470                         int pos = 0;
471                         for (FlightEvent.Type t : FlightEvent.Type.values()) {
472                                 if (set.contains(t)) {
473                                         eventTypes[pos] = t;
474                                         pos++;
475                                 }
476                         }
477                 }
478                 
479                 @Override
480                 public int getColumnCount() {
481                         return 2;
482                 }
483                 
484                 @Override
485                 public int getRowCount() {
486                         return eventTypes.length;
487                 }
488                 
489                 @Override
490                 public Class<?> getColumnClass(int column) {
491                         switch (column) {
492                         case 0:
493                                 return Boolean.class;
494                                 
495                         case 1:
496                                 return String.class;
497                                 
498                         default:
499                                 throw new IndexOutOfBoundsException("column=" + column);
500                         }
501                 }
502                 
503                 @Override
504                 public Object getValueAt(int row, int column) {
505                         switch (column) {
506                         case 0:
507                                 return new Boolean(configuration.isEventActive(eventTypes[row]));
508                                 
509                         case 1:
510                                 return eventTypes[row].toString();
511                                 
512                         default:
513                                 throw new IndexOutOfBoundsException("column=" + column);
514                         }
515                 }
516                 
517                 @Override
518                 public boolean isCellEditable(int row, int column) {
519                         return column == 0;
520                 }
521                 
522                 @Override
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);
526                         }
527                         
528                         configuration.setEvent(eventTypes[row], (Boolean) value);
529                         this.fireTableCellUpdated(row, column);
530                 }
531         }
532 }