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