added svn:ignores
[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                                                 label.setIcon(Icons.SIMULATION_STATUS_ICON_MAP.get(status));
230                                                 
231
232                                                 // Set warning marker
233                                                 if (status == Simulation.Status.NOT_SIMULATED ||
234                                                                 status == Simulation.Status.EXTERNAL) {
235                                                         
236                                                         label.setText("");
237                                                         
238                                                 } else {
239                                                         
240                                                         WarningSet w = document.getSimulation(row).getSimulatedWarnings();
241                                                         if (w == null) {
242                                                                 label.setText("");
243                                                         } else if (w.isEmpty()) {
244                                                                 label.setForeground(OK_COLOR);
245                                                                 label.setText(OK_TEXT);
246                                                         } else {
247                                                                 label.setForeground(WARNING_COLOR);
248                                                                 label.setText(WARNING_TEXT);
249                                                         }
250                                                 }
251                                                 
252                                                 return label;
253                                         }
254                                         
255                                         @Override
256                                         public int getExactWidth() {
257                                                 return 36;
258                                         }
259                                         
260                                         @Override
261                                         public Class<?> getColumnClass() {
262                                                 return JLabel.class;
263                                         }
264                                 },
265
266                                 //// Simulation name
267                                 new Column("Name") {
268                                         @Override
269                                         public Object getValueAt(int row) {
270                                                 if (row < 0 || row >= document.getSimulationCount())
271                                                         return null;
272                                                 return document.getSimulation(row).getName();
273                                         }
274                                         
275                                         @Override
276                                         public int getDefaultWidth() {
277                                                 return 125;
278                                         }
279                                 },
280
281                                 //// Simulation motors
282                                 new Column("Motors") {
283                                         @Override
284                                         public Object getValueAt(int row) {
285                                                 if (row < 0 || row >= document.getSimulationCount())
286                                                         return null;
287                                                 return document.getSimulation(row).getConfiguration()
288                                                                 .getMotorConfigurationDescription();
289                                         }
290                                         
291                                         @Override
292                                         public int getDefaultWidth() {
293                                                 return 125;
294                                         }
295                                 },
296
297                                 //// Apogee
298                                 new Column("Apogee") {
299                                         @Override
300                                         public Object getValueAt(int row) {
301                                                 if (row < 0 || row >= document.getSimulationCount())
302                                                         return null;
303                                                 
304                                                 FlightData data = document.getSimulation(row).getSimulatedData();
305                                                 if (data == null)
306                                                         return null;
307                                                 
308                                                 return UnitGroup.UNITS_DISTANCE.getDefaultUnit().toStringUnit(
309                                                                 data.getMaxAltitude());
310                                         }
311                                 },
312
313                                 //// Maximum velocity
314                                 new Column("Max. velocity") {
315                                         @Override
316                                         public Object getValueAt(int row) {
317                                                 if (row < 0 || row >= document.getSimulationCount())
318                                                         return null;
319                                                 
320                                                 FlightData data = document.getSimulation(row).getSimulatedData();
321                                                 if (data == null)
322                                                         return null;
323                                                 
324                                                 return UnitGroup.UNITS_VELOCITY.getDefaultUnit().toStringUnit(
325                                                                 data.getMaxVelocity());
326                                         }
327                                 },
328
329                                 //// Maximum acceleration
330                                 new Column("Max. acceleration") {
331                                         @Override
332                                         public Object getValueAt(int row) {
333                                                 if (row < 0 || row >= document.getSimulationCount())
334                                                         return null;
335                                                 
336                                                 FlightData data = document.getSimulation(row).getSimulatedData();
337                                                 if (data == null)
338                                                         return null;
339                                                 
340                                                 return UnitGroup.UNITS_ACCELERATION.getDefaultUnit().toStringUnit(
341                                                                 data.getMaxAcceleration());
342                                         }
343                                 },
344
345                                 //// Time to apogee
346                                 new Column("Time to apogee") {
347                                         @Override
348                                         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.getTimeToApogee());
358                                         }
359                                 },
360
361                                 //// Flight time
362                                 new Column("Flight time") {
363                                         @Override
364                                         public Object getValueAt(int row) {
365                                                 if (row < 0 || row >= document.getSimulationCount())
366                                                         return null;
367                                                 
368                                                 FlightData data = document.getSimulation(row).getSimulatedData();
369                                                 if (data == null)
370                                                         return null;
371                                                 
372                                                 return UnitGroup.UNITS_FLIGHT_TIME.getDefaultUnit().toStringUnit(
373                                                                 data.getFlightTime());
374                                         }
375                                 },
376
377                                 //// Ground hit velocity
378                                 new Column("Ground hit velocity") {
379                                         @Override
380                                         public Object getValueAt(int row) {
381                                                 if (row < 0 || row >= document.getSimulationCount())
382                                                         return null;
383                                                 
384                                                 FlightData data = document.getSimulation(row).getSimulatedData();
385                                                 if (data == null)
386                                                         return null;
387                                                 
388                                                 return UnitGroup.UNITS_VELOCITY.getDefaultUnit().toStringUnit(
389                                                                 data.getGroundHitVelocity());
390                                         }
391                                 }
392
393                 ) {
394                         @Override
395                         public int getRowCount() {
396                                 return document.getSimulationCount();
397                         }
398                 };
399                 
400                 // Override processKeyBinding so that the JTable does not catch
401                 // key bindings used in menu accelerators
402                 simulationTable = new JTable(simulationTableModel) {
403                         @Override
404                         protected boolean processKeyBinding(KeyStroke ks,
405                                         KeyEvent e,
406                                         int condition,
407                                         boolean pressed) {
408                                 return false;
409                         }
410                 };
411                 simulationTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
412                 simulationTable.setDefaultRenderer(Object.class, new JLabelRenderer());
413                 simulationTableModel.setColumnWidths(simulationTable.getColumnModel());
414                 
415
416                 // Mouse listener to act on double-clicks
417                 simulationTable.addMouseListener(new MouseAdapter() {
418                         @Override
419                         public void mouseClicked(MouseEvent e) {
420                                 if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) {
421                                         int selected = simulationTable.getSelectedRow();
422                                         if (selected < 0)
423                                                 return;
424                                         
425                                         selected = simulationTable.convertRowIndexToModel(selected);
426                                         simulationTable.clearSelection();
427                                         simulationTable.addRowSelectionInterval(selected, selected);
428                                         
429                                         openDialog(document.getSimulations().get(selected),
430                                                         SimulationEditDialog.DEFAULT);
431                                 }
432                         }
433                 });
434                 
435                 document.addDocumentChangeListener(new DocumentChangeListener() {
436                         @Override
437                         public void documentChanged(DocumentChangeEvent event) {
438                                 if (!(event instanceof SimulationChangeEvent))
439                                         return;
440                                 simulationTableModel.fireTableDataChanged();
441                         }
442                 });
443                 
444
445
446
447                 // Fire table change event when the rocket changes
448                 document.getRocket().addComponentChangeListener(new ComponentChangeListener() {
449                         @Override
450                         public void componentChanged(ComponentChangeEvent e) {
451                                 fireMaintainSelection();
452                         }
453                 });
454                 
455
456                 JScrollPane scrollpane = new JScrollPane(simulationTable);
457                 this.add(scrollpane, "spanx, grow, wrap rel");
458                 
459
460         }
461         
462         
463         public ListSelectionModel getSimulationListSelectionModel() {
464                 return simulationTable.getSelectionModel();
465         }
466         
467         private void openDialog(final Simulation sim, int position) {
468                 new SimulationEditDialog(SwingUtilities.getWindowAncestor(this), sim, position)
469                                 .setVisible(true);
470                 fireMaintainSelection();
471         }
472         
473         private void fireMaintainSelection() {
474                 int[] selection = simulationTable.getSelectedRows();
475                 simulationTableModel.fireTableDataChanged();
476                 for (int row : selection) {
477                         if (row >= simulationTableModel.getRowCount())
478                                 break;
479                         simulationTable.addRowSelectionInterval(row, row);
480                 }
481         }
482         
483         
484         private class JLabelRenderer extends DefaultTableCellRenderer {
485                 
486                 @Override
487                 public Component getTableCellRendererComponent(JTable table,
488                                 Object value, boolean isSelected, boolean hasFocus, int row,
489                                 int column) {
490                         
491                         if (row < 0 || row >= document.getSimulationCount())
492                                 return super.getTableCellRendererComponent(table, value,
493                                                 isSelected, hasFocus, row, column);
494                         
495                         // A JLabel is self-contained and has set its own tool tip
496                         if (value instanceof JLabel) {
497                                 JLabel label = (JLabel) value;
498                                 if (isSelected)
499                                         label.setBackground(table.getSelectionBackground());
500                                 else
501                                         label.setBackground(table.getBackground());
502                                 label.setOpaque(true);
503                                 
504                                 label.setToolTipText(getSimulationToolTip(document.getSimulation(row)));
505                                 return label;
506                         }
507                         
508                         Component component = super.getTableCellRendererComponent(table, value,
509                                         isSelected, hasFocus, row, column);
510                         
511                         if (component instanceof JComponent) {
512                                 ((JComponent) component).setToolTipText(getSimulationToolTip(
513                                                 document.getSimulation(row)));
514                         }
515                         return component;
516                 }
517                 
518                 private String getSimulationToolTip(Simulation sim) {
519                         String tip;
520                         FlightData data = sim.getSimulatedData();
521                         
522                         tip = "<html><b>" + sim.getName() + "</b><br>";
523                         switch (sim.getStatus()) {
524                         case UPTODATE:
525                                 tip += "<i>Up to date</i><br>";
526                                 break;
527                         
528                         case LOADED:
529                                 tip += "<i>Data loaded from a file</i><br>";
530                                 break;
531                         
532                         case OUTDATED:
533                                 tip += "<i><font color=\"red\">Data is out of date</font></i><br>";
534                                 tip += "Click <i><b>Run simulations</b></i> to simulate.<br>";
535                                 break;
536                         
537                         case EXTERNAL:
538                                 tip += "<i>Imported data</i><br>";
539                                 return tip;
540                                 
541                         case NOT_SIMULATED:
542                                 tip += "<i>Not simulated yet</i><br>";
543                                 tip += "Click <i><b>Run simulations</b></i> to simulate.";
544                                 return tip;
545                         }
546                         
547                         if (data == null) {
548                                 tip += "No simulation data available.";
549                                 return tip;
550                         }
551                         WarningSet warnings = data.getWarningSet();
552                         
553                         if (warnings.isEmpty()) {
554                                 tip += "<font color=\"gray\">No warnings.</font>";
555                                 return tip;
556                         }
557                         
558                         tip += "<font color=\"red\">Warnings:</font>";
559                         for (Warning w : warnings) {
560                                 tip += "<br>" + w.toString();
561                         }
562                         
563                         return tip;
564                 }
565                 
566         }
567 }