ffdfa1481b3c7b966a3cf3726e3000ce80f911db
[debian/openrocket] / src / net / sf / openrocket / gui / components / SimulationExportPanel.java
1 package net.sf.openrocket.gui.components;
2
3 import java.awt.Component;
4 import java.awt.event.ActionEvent;
5 import java.awt.event.ActionListener;
6 import java.io.File;
7 import java.util.Arrays;
8
9 import javax.swing.BorderFactory;
10 import javax.swing.JButton;
11 import javax.swing.JCheckBox;
12 import javax.swing.JComboBox;
13 import javax.swing.JFileChooser;
14 import javax.swing.JLabel;
15 import javax.swing.JOptionPane;
16 import javax.swing.JPanel;
17 import javax.swing.JScrollPane;
18 import javax.swing.JTable;
19 import javax.swing.SwingUtilities;
20 import javax.swing.filechooser.FileFilter;
21 import javax.swing.table.AbstractTableModel;
22 import javax.swing.table.TableCellRenderer;
23 import javax.swing.table.TableColumn;
24 import javax.swing.table.TableColumnModel;
25
26 import net.miginfocom.swing.MigLayout;
27 import net.sf.openrocket.document.Simulation;
28 import net.sf.openrocket.l10n.Translator;
29 import net.sf.openrocket.simulation.FlightData;
30 import net.sf.openrocket.simulation.FlightDataBranch;
31 import net.sf.openrocket.simulation.FlightDataType;
32 import net.sf.openrocket.startup.Application;
33 import net.sf.openrocket.unit.Unit;
34 import net.sf.openrocket.unit.UnitGroup;
35 import net.sf.openrocket.util.GUIUtil;
36 import net.sf.openrocket.util.Prefs;
37 import net.sf.openrocket.util.SaveCSVWorker;
38
39 public class SimulationExportPanel extends JPanel {
40
41         private static final String SPACE = "SPACE";
42         private static final String TAB = "TAB";
43         private static final Translator trans = Application.getTranslator();
44         
45         private static final FileFilter CSV_FILE_FILTER = new FileFilter() {
46                 @Override
47                 public String getDescription() {
48                         //// Comma Separated Files (*.csv)
49                         return trans.get("SimExpPan.desc");
50                 }
51                 @Override
52                 public boolean accept(File f) {
53                         if (f.isDirectory())
54                                 return true;
55                         String name = f.getName().toLowerCase();
56                         return name.endsWith(".csv");
57                 }
58     };
59
60         
61         
62         private final JTable table;
63         private final SelectionTableModel tableModel;
64         private final JLabel selectedCountLabel;
65         
66         private final Simulation simulation;
67         private final FlightDataBranch branch;
68         
69         private final boolean[] selected;
70         private final FlightDataType[] types;
71         private final Unit[] units;
72         
73         private final JComboBox fieldSeparator;
74         private final JCheckBox simulationComments;
75         private final JCheckBox fieldNameComments;
76         private final JCheckBox eventComments;
77         private final JComboBox commentCharacter;
78         
79         
80         public SimulationExportPanel(Simulation sim) {
81                 super(new MigLayout("fill, flowy"));
82
83                 JLabel label;
84                 JPanel panel;
85                 JButton button;
86                 String tip;
87                 
88                 
89                 this.simulation = sim;
90                 
91                 // TODO: MEDIUM: Only exports primary branch
92                 
93                 final FlightData data = simulation.getSimulatedData();
94
95                 // Check that data exists
96                 if (data == null  || data.getBranchCount() == 0 ||
97                                 data.getBranch(0).getTypes().length == 0) {
98                         throw new IllegalArgumentException("No data for panel");
99                 }
100                 
101                 
102                 // Create the data model
103                 branch = data.getBranch(0);
104
105                 types = branch.getTypes();
106                 Arrays.sort(types);
107                 
108                 selected = new boolean[types.length];
109                 units = new Unit[types.length];
110                 for (int i = 0; i < types.length; i++) {
111                         selected[i] = Prefs.isExportSelected(types[i]);
112                         units[i] = types[i].getUnitGroup().getDefaultUnit();
113                 }
114                 
115                 
116                 //// Create the panel
117                 
118                 
119                 // Set up the variable selection table
120                 tableModel = new SelectionTableModel();
121                 table = new JTable(tableModel);
122                 table.setDefaultRenderer(Object.class, 
123                                 new SelectionBackgroundCellRenderer(table.getDefaultRenderer(Object.class)));
124                 table.setDefaultRenderer(Boolean.class, 
125                                 new SelectionBackgroundCellRenderer(table.getDefaultRenderer(Boolean.class)));
126                 table.setRowSelectionAllowed(false);
127                 table.setColumnSelectionAllowed(false);
128                 
129                 table.setDefaultEditor(Unit.class, new UnitCellEditor() {
130                         @Override
131                         protected UnitGroup getUnitGroup(Unit value, int row, int column) {
132                                 return types[row].getUnitGroup();
133                         }
134                 });
135                 
136                 // Set column widths
137                 TableColumnModel columnModel = table.getColumnModel();
138                 TableColumn col = columnModel.getColumn(0);
139                 int w = table.getRowHeight();
140                 col.setMinWidth(w);
141                 col.setPreferredWidth(w);
142                 col.setMaxWidth(w);
143                 
144                 col = columnModel.getColumn(1);
145                 col.setPreferredWidth(200);
146                 
147                 col = columnModel.getColumn(2);
148                 col.setPreferredWidth(100);
149
150                 table.addMouseListener(new GUIUtil.BooleanTableClickListener(table));
151                 
152                 // Add table
153                 panel = new JPanel(new MigLayout("fill"));
154                 panel.setBorder(BorderFactory.createTitledBorder(trans.get("SimExpPan.border.Vartoexport")));
155                 
156                 panel.add(new JScrollPane(table), "wmin 300lp, width 300lp, height 1, grow 100, wrap");
157                 
158                 // Select all/none buttons
159                 button = new JButton(trans.get("SimExpPan.but.Selectall"));
160                 button.addActionListener(new ActionListener() {
161                         @Override
162                         public void actionPerformed(ActionEvent e) {
163                                 tableModel.selectAll();
164                         }
165                 });
166                 panel.add(button, "split 2, growx 1, sizegroup selectbutton");
167                 
168                 button = new JButton(trans.get("SimExpPan.but.Selectnone"));
169                 button.addActionListener(new ActionListener() {
170                         @Override
171                         public void actionPerformed(ActionEvent e) {
172                                 tableModel.selectNone();
173                         }
174                 });
175                 panel.add(button, "growx 1, sizegroup selectbutton, wrap");
176                 
177                 
178                 selectedCountLabel = new JLabel();
179                 updateSelectedCount();
180                 panel.add(selectedCountLabel);
181                 
182                 this.add(panel, "grow 100, wrap");
183                 
184                 
185                 
186                 // Field separator panel
187                 panel = new JPanel(new MigLayout("fill"));
188                 panel.setBorder(BorderFactory.createTitledBorder(trans.get("SimExpPan.border.Fieldsep")));
189                 
190                 label = new JLabel(trans.get("SimExpPan.lbl.Fieldsepstr"));
191                 //// <html>The string used to separate the fields in the exported file.<br>
192                 //// Use ',' for a Comma Separated Values (CSV) file.
193                 tip = trans.get("SimExpPan.lbl.longA1") +
194                 trans.get("SimExpPan.lbl.longA2");
195                 label.setToolTipText(tip);
196                 panel.add(label);
197                 
198                 fieldSeparator = new JComboBox(new String[] { ",", ";", SPACE, TAB });
199                 fieldSeparator.setEditable(true);
200                 fieldSeparator.setSelectedItem(Prefs.getString(Prefs.EXPORT_FIELD_SEPARATOR, 
201                                 ","));
202                 fieldSeparator.setToolTipText(tip);
203                 panel.add(fieldSeparator);
204                 
205                 this.add(panel, "spany, split, growx 1");
206                 
207                 
208                 
209                 
210                 // Comments separator panel
211                 panel = new JPanel(new MigLayout("fill"));
212                 //// Comments
213                 panel.setBorder(BorderFactory.createTitledBorder(trans.get("SimExpPan.border.Comments")));
214                 
215                 //// Include simulation description
216                 simulationComments = new JCheckBox(trans.get("SimExpPan.checkbox.Includesimudesc"));
217                 //// Include a comment at the beginning of the file describing the simulation.
218                 simulationComments.setToolTipText(trans.get("SimExpPan.checkbox.ttip.Includesimudesc"));
219                 simulationComments.setSelected(Prefs.getBoolean(Prefs.EXPORT_SIMULATION_COMMENT, 
220                                 true));
221                 panel.add(simulationComments, "wrap");
222                 
223                 //// Include field descriptions
224                 fieldNameComments = new JCheckBox(trans.get("SimExpPan.checkbox.Includefielddesc"));
225                 //// Include a comment line with the descriptions of the exported variables.
226                 fieldNameComments.setToolTipText(trans.get("SimExpPan.checkbox.ttip.Includefielddesc"));
227                 fieldNameComments.setSelected(Prefs.getBoolean(Prefs.EXPORT_FIELD_NAME_COMMENT, true));
228                 panel.add(fieldNameComments, "wrap");
229                 
230                 
231                 eventComments = new JCheckBox(trans.get("SimExpPan.checkbox.Incflightevents"));
232                 eventComments.setToolTipText(trans.get("SimExpPan.checkbox.ttip.Incflightevents"));
233                 eventComments.setSelected(Prefs.getBoolean(Prefs.EXPORT_EVENT_COMMENTS, true));
234                 panel.add(eventComments, "wrap");
235                 
236                 
237                 label = new JLabel(trans.get("SimExpPan.lbl.Commentchar"));
238                 tip = trans.get("SimExpPan.lbl.ttip.Commentchar");
239                 label.setToolTipText(tip);
240                 panel.add(label, "split 2");
241                 
242                 commentCharacter = new JComboBox(new String[] { "#", "%", ";" });
243                 commentCharacter.setEditable(true);
244                 commentCharacter.setSelectedItem(Prefs.getString(Prefs.EXPORT_COMMENT_CHARACTER, "#"));
245                 commentCharacter.setToolTipText(tip);
246                 panel.add(commentCharacter);
247                 
248                 this.add(panel, "growx 1");
249
250                 
251                 // Space-filling panel
252                 panel = new JPanel();
253                 this.add(panel, "width 1, height 1, grow 1");
254                 
255                 
256                 // Export button
257                 button = new JButton(trans.get("SimExpPan.but.Exporttofile"));
258                 button.addActionListener(new ActionListener() {
259                         @Override
260                         public void actionPerformed(ActionEvent e) {
261                                 doExport();
262                         }
263                 });
264                 this.add(button, "gapbottom para, gapright para, right");
265                 
266         }
267         
268         
269         private void doExport() {
270                 JFileChooser chooser = new JFileChooser();
271                 chooser.setFileFilter(CSV_FILE_FILTER);
272                 chooser.setCurrentDirectory(Prefs.getDefaultDirectory());
273                 
274                 if (chooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION)
275                         return;
276                 
277                 File file = chooser.getSelectedFile();
278                 if (file == null)
279                         return;
280                 
281                 if (file.getName().indexOf('.') < 0) {
282                         String name = file.getAbsolutePath();
283                         name = name + ".csv";
284                         file = new File(name);
285                 }
286
287                 if (file.exists()) {
288                         int ret = JOptionPane.showConfirmDialog(this, 
289                                         "File \"" + file.getName() + "\" exists.  Overwrite?", 
290                                         "File exists", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
291                         if (ret != JOptionPane.YES_OPTION)
292                                 return;
293                 }
294
295                 String commentChar = commentCharacter.getSelectedItem().toString();
296                 String fieldSep = fieldSeparator.getSelectedItem().toString();
297                 boolean simulationComment = simulationComments.isSelected();
298                 boolean fieldComment = fieldNameComments.isSelected();
299                 boolean eventComment = eventComments.isSelected();
300                 
301                 // Store preferences and export
302                 int n = 0;
303                 Prefs.setDefaultDirectory(chooser.getCurrentDirectory());
304                 for (int i=0; i < selected.length; i++) {
305                         Prefs.setExportSelected(types[i], selected[i]);
306                         if (selected[i])
307                                 n++;
308                 }
309                 Prefs.putString(Prefs.EXPORT_FIELD_SEPARATOR, fieldSep);
310                 Prefs.putString(Prefs.EXPORT_COMMENT_CHARACTER, commentChar);
311                 Prefs.putBoolean(Prefs.EXPORT_EVENT_COMMENTS, eventComment);
312                 Prefs.putBoolean(Prefs.EXPORT_FIELD_NAME_COMMENT, fieldComment);
313                 Prefs.putBoolean(Prefs.EXPORT_SIMULATION_COMMENT, simulationComment);
314                 
315                 
316                 FlightDataType[] fieldTypes = new FlightDataType[n];
317                 Unit[] fieldUnits = new Unit[n];
318                 int pos = 0;
319                 for (int i=0; i < selected.length; i++) {
320                         if (selected[i]) {
321                                 fieldTypes[pos] = types[i];
322                                 fieldUnits[pos] = units[i];
323                                 pos++;
324                         }
325                 }
326                 
327                 if (fieldSep.equals(SPACE)) {
328                         fieldSep = " ";
329                 } else if (fieldSep.equals(TAB)) {
330                         fieldSep = "\t";
331                 }
332                 
333                 
334                 SaveCSVWorker.export(file, simulation, branch, fieldTypes, fieldUnits, fieldSep, 
335                                 commentChar, simulationComment, fieldComment, eventComment, 
336                                 SwingUtilities.getWindowAncestor(this));
337         }
338         
339         
340         private void updateSelectedCount() {
341                 int total = selected.length;
342                 int n = 0;
343                 String str;
344                 
345                 for (int i=0; i < selected.length; i++) {
346                         if (selected[i])
347                                 n++;
348                 }
349                 
350                 if (n == 1) {
351                         str = "Exporting 1 variable out of " + total + ".";
352                 } else {
353                         str = "Exporting "+n+" variables out of " + total + ".";
354                 }
355
356                 selectedCountLabel.setText(str);
357         }
358         
359         
360         
361         /**
362          * A table cell renderer that uses another renderer and sets the background and
363          * foreground of the returned component based on the selection of the variable.
364          */
365         private class SelectionBackgroundCellRenderer implements TableCellRenderer {
366
367                 private final TableCellRenderer renderer;
368                 
369                 public SelectionBackgroundCellRenderer(TableCellRenderer renderer) {
370                         this.renderer = renderer;
371                 }
372                 
373                 @Override
374                 public Component getTableCellRendererComponent(JTable table, Object value,
375                                 boolean isSelected, boolean hasFocus, int row, int column) {
376                         
377                         Component component = renderer.getTableCellRendererComponent(table, 
378                                         value, isSelected, hasFocus, row, column);
379                         
380                         if (selected[row]) {
381                                 component.setBackground(table.getSelectionBackground());
382                                 component.setForeground(table.getSelectionForeground());
383                         } else {
384                                 component.setBackground(table.getBackground());
385                                 component.setForeground(table.getForeground());
386                         }
387                         
388                         return component;
389                 }
390                 
391         }
392         
393         
394         /**
395          * The table model for the variable selection.
396          */
397         private class SelectionTableModel extends AbstractTableModel {
398                 private static final int SELECTED = 0;
399                 private static final int NAME = 1;
400                 private static final int UNIT = 2;
401
402                 @Override
403                 public int getColumnCount() {
404                         return 3;
405                 }
406
407                 @Override
408                 public int getRowCount() {
409                         return types.length;
410                 }
411                 
412                 @Override
413                 public String getColumnName(int column) {
414                         switch (column) {
415                         case SELECTED:
416                                 return "";
417                         case NAME:
418                                 return "Variable";
419                         case UNIT:
420                                 return "Unit";
421                         default:
422                                 throw new IndexOutOfBoundsException("column=" + column);
423                         }
424                         
425                 }
426                 
427                 @Override
428                 public Class<?> getColumnClass(int column) {
429                         switch (column) {
430                         case SELECTED:
431                                 return Boolean.class;
432                         case NAME:
433                                 return FlightDataType.class;
434                         case UNIT:
435                                 return Unit.class;
436                         default:
437                                 throw new IndexOutOfBoundsException("column=" + column);
438                         }
439                 }
440
441                 @Override
442                 public Object getValueAt(int row, int column) {
443
444                         switch (column) {
445                         case SELECTED:
446                                 return selected[row];
447                                 
448                         case NAME:
449                                 return types[row];
450                                 
451                         case UNIT:
452                                 return units[row];
453                                 
454                         default:
455                                 throw new IndexOutOfBoundsException("column="+column);
456                         }
457                         
458                 }
459
460                 @Override
461                 public void setValueAt(Object value, int row, int column) {
462                         
463                         switch (column) {
464                         case SELECTED:
465                                 selected[row] = (Boolean)value;
466                                 this.fireTableRowsUpdated(row, row);
467                                 updateSelectedCount();
468                                 break;
469                                 
470                         case NAME:
471                                 break;
472                                 
473                         case UNIT:
474                                 units[row] = (Unit)value;
475                                 break;
476                                 
477                         default:
478                                 throw new IndexOutOfBoundsException("column="+column);
479                         }
480                         
481                 }
482
483                 @Override
484                 public boolean isCellEditable(int row, int column) {
485                         switch (column) {
486                         case SELECTED:
487                                 return true;
488                                 
489                         case NAME:
490                                 return false;
491                                 
492                         case UNIT:
493                                 return types[row].getUnitGroup().getUnitCount() > 1;
494                                 
495                         default:
496                                 throw new IndexOutOfBoundsException("column="+column);
497                         }
498                 }
499                 
500                 public void selectAll() {
501                         Arrays.fill(selected, true);
502                         updateSelectedCount();
503                         this.fireTableDataChanged();
504                 }
505                 
506                 public void selectNone() {
507                         Arrays.fill(selected, false);
508                         updateSelectedCount();
509                         this.fireTableDataChanged();
510                 }
511                 
512         }
513         
514 }