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