DGP - added Radial Angle to launch lug on Rocksim import
[debian/openrocket] / src / net / sf / openrocket / gui / main / SimulationPanel.java
1 package net.sf.openrocket.gui.main;
2
3
4 import java.awt.Color;
5 import java.awt.Component;
6 import java.awt.event.ActionEvent;
7 import java.awt.event.ActionListener;
8 import java.awt.event.KeyEvent;
9 import java.awt.event.MouseAdapter;
10 import java.awt.event.MouseEvent;
11 import java.util.Arrays;
12
13 import javax.swing.JButton;
14 import javax.swing.JCheckBox;
15 import javax.swing.JComponent;
16 import javax.swing.JLabel;
17 import javax.swing.JOptionPane;
18 import javax.swing.JPanel;
19 import javax.swing.JScrollPane;
20 import javax.swing.JTable;
21 import javax.swing.KeyStroke;
22 import javax.swing.ListSelectionModel;
23 import javax.swing.SwingUtilities;
24 import javax.swing.table.DefaultTableCellRenderer;
25
26 import net.miginfocom.swing.MigLayout;
27 import net.sf.openrocket.aerodynamics.Warning;
28 import net.sf.openrocket.aerodynamics.WarningSet;
29 import net.sf.openrocket.document.OpenRocketDocument;
30 import net.sf.openrocket.document.Simulation;
31 import net.sf.openrocket.document.events.DocumentChangeEvent;
32 import net.sf.openrocket.document.events.DocumentChangeListener;
33 import net.sf.openrocket.document.events.SimulationChangeEvent;
34 import net.sf.openrocket.gui.adaptors.Column;
35 import net.sf.openrocket.gui.adaptors.ColumnTableModel;
36 import net.sf.openrocket.gui.components.StyledLabel;
37 import net.sf.openrocket.logging.LogHelper;
38 import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
39 import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
40 import net.sf.openrocket.simulation.FlightData;
41 import net.sf.openrocket.startup.Application;
42 import net.sf.openrocket.unit.UnitGroup;
43 import net.sf.openrocket.util.Icons;
44 import net.sf.openrocket.util.Prefs;
45
46 public class SimulationPanel extends JPanel {
47         private static final LogHelper log = Application.getLogger();
48         
49         private static final Color WARNING_COLOR = Color.RED;
50         private static final String WARNING_TEXT = "\uFF01"; // Fullwidth exclamation mark
51         
52         private static final Color OK_COLOR = new Color(60, 150, 0);
53         private static final String OK_TEXT = "\u2714"; // Heavy check mark
54         
55
56
57         private final OpenRocketDocument document;
58         
59         private final ColumnTableModel simulationTableModel;
60         private final JTable simulationTable;
61         
62         
63         public SimulationPanel(OpenRocketDocument doc) {
64                 super(new MigLayout("fill", "[grow][][][][][][grow]"));
65                 
66                 JButton button;
67                 
68
69                 this.document = doc;
70                 
71
72
73                 ////////  The simulation action buttons
74                 
75                 button = new JButton("New simulation");
76                 button.setToolTipText("Add a new simulation");
77                 button.addActionListener(new ActionListener() {
78                         @Override
79                         public void actionPerformed(ActionEvent e) {
80                                 Simulation sim = new Simulation(document.getRocket());
81                                 sim.setName(document.getNextSimulationName());
82                                 
83                                 int n = document.getSimulationCount();
84                                 document.addSimulation(sim);
85                                 simulationTableModel.fireTableDataChanged();
86                                 simulationTable.clearSelection();
87                                 simulationTable.addRowSelectionInterval(n, n);
88                                 
89                                 openDialog(sim, SimulationEditDialog.EDIT);
90                         }
91                 });
92                 this.add(button, "skip 1, gapright para");
93                 
94                 button = new JButton("Edit simulation");
95                 button.setToolTipText("Edit the selected simulation");
96                 button.addActionListener(new ActionListener() {
97                         @Override
98                         public void actionPerformed(ActionEvent e) {
99                                 int selected = simulationTable.getSelectedRow();
100                                 if (selected < 0)
101                                         return; // TODO: MEDIUM: "None selected" dialog
102                                         
103                                 selected = simulationTable.convertRowIndexToModel(selected);
104                                 simulationTable.clearSelection();
105                                 simulationTable.addRowSelectionInterval(selected, selected);
106                                 
107                                 openDialog(document.getSimulations().get(selected), SimulationEditDialog.EDIT);
108                         }
109                 });
110                 this.add(button, "gapright para");
111                 
112                 button = new JButton("Run simulations");
113                 button.setToolTipText("Re-run the selected simulations");
114                 button.addActionListener(new ActionListener() {
115                         @Override
116                         public void actionPerformed(ActionEvent e) {
117                                 int[] selection = simulationTable.getSelectedRows();
118                                 if (selection.length == 0)
119                                         return; // TODO: LOW: "None selected" dialog
120                                         
121                                 Simulation[] sims = new Simulation[selection.length];
122                                 for (int i = 0; i < selection.length; i++) {
123                                         selection[i] = simulationTable.convertRowIndexToModel(selection[i]);
124                                         sims[i] = document.getSimulation(selection[i]);
125                                 }
126                                 
127                                 long t = System.currentTimeMillis();
128                                 new SimulationRunDialog(SwingUtilities.getWindowAncestor(
129                                                         SimulationPanel.this), sims).setVisible(true);
130                                 log.info("Running simulations took " + (System.currentTimeMillis() - t) + " ms");
131                                 fireMaintainSelection();
132                         }
133                 });
134                 this.add(button, "gapright para");
135                 
136                 button = new JButton("Delete simulations");
137                 button.setToolTipText("Delete the selected simulations");
138                 button.addActionListener(new ActionListener() {
139                         @Override
140                         public void actionPerformed(ActionEvent e) {
141                                 int[] selection = simulationTable.getSelectedRows();
142                                 if (selection.length == 0)
143                                         return; // TODO: LOW: "None selected" dialog
144                                         
145                                 // Verify deletion
146                                 boolean verify = Prefs.NODE.getBoolean(Prefs.CONFIRM_DELETE_SIMULATION, true);
147                                 if (verify) {
148                                         
149                                         JPanel panel = new JPanel(new MigLayout());
150                                         JCheckBox dontAsk = new JCheckBox("Do not ask me again");
151                                         panel.add(dontAsk, "wrap");
152                                         panel.add(new StyledLabel("You can change the default operation in the " +
153                                                                 "preferences.", -2));
154                                         
155                                         int ret = JOptionPane.showConfirmDialog(SimulationPanel.this,
156                                                                 new Object[] {
157                                                                                 "Delete the selected simulations?",
158                                                                                 "<html><i>This operation cannot be undone.</i>",
159                                                                                 "",
160                                                                                 panel },
161                                                                 "Delete simulations",
162                                                                 JOptionPane.OK_CANCEL_OPTION,
163                                                                 JOptionPane.WARNING_MESSAGE);
164                                         if (ret != JOptionPane.OK_OPTION)
165                                                 return;
166                                         
167                                         if (dontAsk.isSelected()) {
168                                                 Prefs.NODE.putBoolean(Prefs.CONFIRM_DELETE_SIMULATION, false);
169                                         }
170                                 }
171                                 
172                                 // Delete simulations
173                                 for (int i = 0; i < selection.length; i++) {
174                                         selection[i] = simulationTable.convertRowIndexToModel(selection[i]);
175                                 }
176                                 Arrays.sort(selection);
177                                 for (int i = selection.length - 1; i >= 0; i--) {
178                                         document.removeSimulation(selection[i]);
179                                 }
180                                 simulationTableModel.fireTableDataChanged();
181                         }
182                 });
183                 this.add(button, "gapright para");
184                 
185
186                 button = new JButton("Plot / export");
187                 //              button = new JButton("Plot flight");
188                 button.addActionListener(new ActionListener() {
189                         @Override
190                         public void actionPerformed(ActionEvent e) {
191                                 int selected = simulationTable.getSelectedRow();
192                                 if (selected < 0)
193                                         return; // TODO: MEDIUM: "None selected" dialog
194                                         
195                                 selected = simulationTable.convertRowIndexToModel(selected);
196                                 simulationTable.clearSelection();
197                                 simulationTable.addRowSelectionInterval(selected, selected);
198                                 
199                                 openDialog(document.getSimulations().get(selected), SimulationEditDialog.PLOT);
200                         }
201                 });
202                 this.add(button, "wrap para");
203                 
204
205
206
207                 ////////  The simulation table
208                 
209                 simulationTableModel = new ColumnTableModel(
210
211                 ////  Status and warning column
212                                 new Column("") {
213                                         private JLabel label = null;
214                                         
215                                         @Override
216                                         public Object getValueAt(int row) {
217                                                 if (row < 0 || row >= document.getSimulationCount())
218                                                         return null;
219                                                 
220                                                 // Initialize the label
221                                                 if (label == null) {
222                                                         label = new StyledLabel(2f);
223                                                         label.setIconTextGap(1);
224                                                         //                                                      label.setFont(label.getFont().deriveFont(Font.BOLD));
225                                                 }
226                                                 
227                                                 // Set simulation status icon
228                                                 Simulation.Status status = document.getSimulation(row).getStatus();
229                                                 System.out.println("status=" + status);
230                                                 label.setIcon(Icons.SIMULATION_STATUS_ICON_MAP.get(status));
231                                                 
232
233                                                 // Set warning marker
234                                                 if (status == Simulation.Status.NOT_SIMULATED ||
235                                                                 status == Simulation.Status.EXTERNAL) {
236                                                         
237                                                         label.setText("");
238                                                         
239                                                 } else {
240                                                         
241                                                         WarningSet w = document.getSimulation(row).getSimulatedWarnings();
242                                                         if (w == null) {
243                                                                 label.setText("");
244                                                         } else if (w.isEmpty()) {
245                                                                 label.setForeground(OK_COLOR);
246                                                                 label.setText(OK_TEXT);
247                                                         } else {
248                                                                 label.setForeground(WARNING_COLOR);
249                                                                 label.setText(WARNING_TEXT);
250                                                         }
251                                                 }
252                                                 
253                                                 return label;
254                                         }
255                                         
256                                         @Override
257                                         public int getExactWidth() {
258                                                 return 32;
259                                         }
260                                         
261                                         @Override
262                                         public Class<?> getColumnClass() {
263                                                 return JLabel.class;
264                                         }
265                                 },
266
267                                 //// Simulation name
268                                 new Column("Name") {
269                                         @Override
270                                         public Object getValueAt(int row) {
271                                                 if (row < 0 || row >= document.getSimulationCount())
272                                                         return null;
273                                                 return document.getSimulation(row).getName();
274                                         }
275                                         
276                                         @Override
277                                         public int getDefaultWidth() {
278                                                 return 125;
279                                         }
280                                 },
281
282                                 //// Simulation motors
283                                 new Column("Motors") {
284                                         @Override
285                                         public Object getValueAt(int row) {
286                                                 if (row < 0 || row >= document.getSimulationCount())
287                                                         return null;
288                                                 return document.getSimulation(row).getConfiguration()
289                                                                 .getMotorConfigurationDescription();
290                                         }
291                                         
292                                         @Override
293                                         public int getDefaultWidth() {
294                                                 return 125;
295                                         }
296                                 },
297
298                                 //// Apogee
299                                 new Column("Apogee") {
300                                         @Override
301                                         public Object getValueAt(int row) {
302                                                 if (row < 0 || row >= document.getSimulationCount())
303                                                         return null;
304                                                 
305                                                 FlightData data = document.getSimulation(row).getSimulatedData();
306                                                 if (data == null)
307                                                         return null;
308                                                 
309                                                 return UnitGroup.UNITS_DISTANCE.getDefaultUnit().toStringUnit(
310                                                                 data.getMaxAltitude());
311                                         }
312                                 },
313
314                                 //// Maximum velocity
315                                 new Column("Max. velocity") {
316                                         @Override
317                                         public Object getValueAt(int row) {
318                                                 if (row < 0 || row >= document.getSimulationCount())
319                                                         return null;
320                                                 
321                                                 FlightData data = document.getSimulation(row).getSimulatedData();
322                                                 if (data == null)
323                                                         return null;
324                                                 
325                                                 return UnitGroup.UNITS_VELOCITY.getDefaultUnit().toStringUnit(
326                                                                 data.getMaxVelocity());
327                                         }
328                                 },
329
330                                 //// Maximum acceleration
331                                 new Column("Max. acceleration") {
332                                         @Override
333                                         public Object getValueAt(int row) {
334                                                 if (row < 0 || row >= document.getSimulationCount())
335                                                         return null;
336                                                 
337                                                 FlightData data = document.getSimulation(row).getSimulatedData();
338                                                 if (data == null)
339                                                         return null;
340                                                 
341                                                 return UnitGroup.UNITS_ACCELERATION.getDefaultUnit().toStringUnit(
342                                                                 data.getMaxAcceleration());
343                                         }
344                                 },
345
346                                 //// Time to apogee
347                                 new Column("Time to apogee") {
348                                         @Override
349                                         public Object getValueAt(int row) {
350                                                 if (row < 0 || row >= document.getSimulationCount())
351                                                         return null;
352                                                 
353                                                 FlightData data = document.getSimulation(row).getSimulatedData();
354                                                 if (data == null)
355                                                         return null;
356                                                 
357                                                 return UnitGroup.UNITS_FLIGHT_TIME.getDefaultUnit().toStringUnit(
358                                                                 data.getTimeToApogee());
359                                         }
360                                 },
361
362                                 //// Flight time
363                                 new Column("Flight time") {
364                                         @Override
365                                         public Object getValueAt(int row) {
366                                                 if (row < 0 || row >= document.getSimulationCount())
367                                                         return null;
368                                                 
369                                                 FlightData data = document.getSimulation(row).getSimulatedData();
370                                                 if (data == null)
371                                                         return null;
372                                                 
373                                                 return UnitGroup.UNITS_FLIGHT_TIME.getDefaultUnit().toStringUnit(
374                                                                 data.getFlightTime());
375                                         }
376                                 },
377
378                                 //// Ground hit velocity
379                                 new Column("Ground hit velocity") {
380                                         @Override
381                                         public Object getValueAt(int row) {
382                                                 if (row < 0 || row >= document.getSimulationCount())
383                                                         return null;
384                                                 
385                                                 FlightData data = document.getSimulation(row).getSimulatedData();
386                                                 if (data == null)
387                                                         return null;
388                                                 
389                                                 return UnitGroup.UNITS_VELOCITY.getDefaultUnit().toStringUnit(
390                                                                 data.getGroundHitVelocity());
391                                         }
392                                 }
393
394                 ) {
395                         @Override
396                         public int getRowCount() {
397                                 return document.getSimulationCount();
398                         }
399                 };
400                 
401                 // Override processKeyBinding so that the JTable does not catch
402                 // key bindings used in menu accelerators
403                 simulationTable = new JTable(simulationTableModel) {
404                         @Override
405                         protected boolean processKeyBinding(KeyStroke ks,
406                                         KeyEvent e,
407                                         int condition,
408                                         boolean pressed) {
409                                 return false;
410                         }
411                 };
412                 simulationTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
413                 simulationTable.setDefaultRenderer(Object.class, new JLabelRenderer());
414                 simulationTableModel.setColumnWidths(simulationTable.getColumnModel());
415                 
416
417                 // Mouse listener to act on double-clicks
418                 simulationTable.addMouseListener(new MouseAdapter() {
419                         @Override
420                         public void mouseClicked(MouseEvent e) {
421                                 if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) {
422                                         int selected = simulationTable.getSelectedRow();
423                                         if (selected < 0)
424                                                 return;
425                                         
426                                         selected = simulationTable.convertRowIndexToModel(selected);
427                                         simulationTable.clearSelection();
428                                         simulationTable.addRowSelectionInterval(selected, selected);
429                                         
430                                         openDialog(document.getSimulations().get(selected),
431                                                         SimulationEditDialog.DEFAULT);
432                                 }
433                         }
434                 });
435                 
436                 document.addDocumentChangeListener(new DocumentChangeListener() {
437                         @Override
438                         public void documentChanged(DocumentChangeEvent event) {
439                                 if (!(event instanceof SimulationChangeEvent))
440                                         return;
441                                 simulationTableModel.fireTableDataChanged();
442                         }
443                 });
444                 
445
446
447
448                 // Fire table change event when the rocket changes
449                 document.getRocket().addComponentChangeListener(new ComponentChangeListener() {
450                         @Override
451                         public void componentChanged(ComponentChangeEvent e) {
452                                 fireMaintainSelection();
453                         }
454                 });
455                 
456
457                 JScrollPane scrollpane = new JScrollPane(simulationTable);
458                 this.add(scrollpane, "spanx, grow, wrap rel");
459                 
460
461         }
462         
463         
464         public ListSelectionModel getSimulationListSelectionModel() {
465                 return simulationTable.getSelectionModel();
466         }
467         
468         private void openDialog(final Simulation sim, int position) {
469                 new SimulationEditDialog(SwingUtilities.getWindowAncestor(this), sim, position)
470                                 .setVisible(true);
471                 fireMaintainSelection();
472         }
473         
474         private void fireMaintainSelection() {
475                 int[] selection = simulationTable.getSelectedRows();
476                 simulationTableModel.fireTableDataChanged();
477                 for (int row : selection) {
478                         if (row >= simulationTableModel.getRowCount())
479                                 break;
480                         simulationTable.addRowSelectionInterval(row, row);
481                 }
482         }
483         
484         
485         private class JLabelRenderer extends DefaultTableCellRenderer {
486                 
487                 @Override
488                 public Component getTableCellRendererComponent(JTable table,
489                                 Object value, boolean isSelected, boolean hasFocus, int row,
490                                 int column) {
491                         
492                         if (row < 0 || row >= document.getSimulationCount())
493                                 return super.getTableCellRendererComponent(table, value,
494                                                 isSelected, hasFocus, row, column);
495                         
496                         // A JLabel is self-contained and has set its own tool tip
497                         if (value instanceof JLabel) {
498                                 JLabel label = (JLabel) value;
499                                 if (isSelected)
500                                         label.setBackground(table.getSelectionBackground());
501                                 else
502                                         label.setBackground(table.getBackground());
503                                 label.setOpaque(true);
504                                 
505                                 label.setToolTipText(getSimulationToolTip(document.getSimulation(row)));
506                                 return label;
507                         }
508                         
509                         Component component = super.getTableCellRendererComponent(table, value,
510                                         isSelected, hasFocus, row, column);
511                         
512                         if (component instanceof JComponent) {
513                                 ((JComponent) component).setToolTipText(getSimulationToolTip(
514                                                 document.getSimulation(row)));
515                         }
516                         return component;
517                 }
518                 
519                 private String getSimulationToolTip(Simulation sim) {
520                         String tip;
521                         FlightData data = sim.getSimulatedData();
522                         
523                         tip = "<html><b>" + sim.getName() + "</b><br>";
524                         switch (sim.getStatus()) {
525                         case UPTODATE:
526                                 tip += "<i>Up to date</i><br>";
527                                 break;
528                         
529                         case LOADED:
530                                 tip += "<i>Data loaded from a file</i><br>";
531                                 break;
532                         
533                         case OUTDATED:
534                                 tip += "<i><font color=\"red\">Data is out of date</font></i><br>";
535                                 tip += "Click <i><b>Run simulations</b></i> to simulate.<br>";
536                                 break;
537                         
538                         case EXTERNAL:
539                                 tip += "<i>Imported data</i><br>";
540                                 return tip;
541                                 
542                         case NOT_SIMULATED:
543                                 tip += "<i>Not simulated yet</i><br>";
544                                 tip += "Click <i><b>Run simulations</b></i> to simulate.";
545                                 return tip;
546                         }
547                         
548                         if (data == null) {
549                                 tip += "No simulation data available.";
550                                 return tip;
551                         }
552                         WarningSet warnings = data.getWarningSet();
553                         
554                         if (warnings.isEmpty()) {
555                                 tip += "<font color=\"gray\">No warnings.</font>";
556                                 return tip;
557                         }
558                         
559                         tip += "<font color=\"red\">Warnings:</font>";
560                         for (Warning w : warnings) {
561                                 tip += "<br>" + w.toString();
562                         }
563                         
564                         return tip;
565                 }
566                 
567         }
568 }