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