62f79d6d3dcc79f4d09c925fe992310c86b6331f
[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 
290                                         trans.get("SimExpPan.Fileexists.desc1") + file.getName() + 
291                                         //// \" exists.  Overwrite?
292                                         trans.get("SimExpPan.Fileexists.desc2"), 
293                                         //// File exists
294                                         trans.get("SimExpPan.Fileexists.title"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
295                         if (ret != JOptionPane.YES_OPTION)
296                                 return;
297                 }
298
299                 String commentChar = commentCharacter.getSelectedItem().toString();
300                 String fieldSep = fieldSeparator.getSelectedItem().toString();
301                 boolean simulationComment = simulationComments.isSelected();
302                 boolean fieldComment = fieldNameComments.isSelected();
303                 boolean eventComment = eventComments.isSelected();
304                 
305                 // Store preferences and export
306                 int n = 0;
307                 Prefs.setDefaultDirectory(chooser.getCurrentDirectory());
308                 for (int i=0; i < selected.length; i++) {
309                         Prefs.setExportSelected(types[i], selected[i]);
310                         if (selected[i])
311                                 n++;
312                 }
313                 Prefs.putString(Prefs.EXPORT_FIELD_SEPARATOR, fieldSep);
314                 Prefs.putString(Prefs.EXPORT_COMMENT_CHARACTER, commentChar);
315                 Prefs.putBoolean(Prefs.EXPORT_EVENT_COMMENTS, eventComment);
316                 Prefs.putBoolean(Prefs.EXPORT_FIELD_NAME_COMMENT, fieldComment);
317                 Prefs.putBoolean(Prefs.EXPORT_SIMULATION_COMMENT, simulationComment);
318                 
319                 
320                 FlightDataType[] fieldTypes = new FlightDataType[n];
321                 Unit[] fieldUnits = new Unit[n];
322                 int pos = 0;
323                 for (int i=0; i < selected.length; i++) {
324                         if (selected[i]) {
325                                 fieldTypes[pos] = types[i];
326                                 fieldUnits[pos] = units[i];
327                                 pos++;
328                         }
329                 }
330                 
331                 if (fieldSep.equals(SPACE)) {
332                         fieldSep = " ";
333                 } else if (fieldSep.equals(TAB)) {
334                         fieldSep = "\t";
335                 }
336                 
337                 
338                 SaveCSVWorker.export(file, simulation, branch, fieldTypes, fieldUnits, fieldSep, 
339                                 commentChar, simulationComment, fieldComment, eventComment, 
340                                 SwingUtilities.getWindowAncestor(this));
341         }
342         
343         
344         private void updateSelectedCount() {
345                 int total = selected.length;
346                 int n = 0;
347                 String str;
348                 
349                 for (int i=0; i < selected.length; i++) {
350                         if (selected[i])
351                                 n++;
352                 }
353                 
354                 if (n == 1) {
355                         //// Exporting 1 variable out of 
356                         str = trans.get("SimExpPan.ExportingVar.desc1") + " " + total + ".";
357                 } else {
358                         //// Exporting 
359                         //// variables out of
360                         str = trans.get("SimExpPan.ExportingVar.desc2") + " "+n+" " + 
361                         trans.get("SimExpPan.ExportingVar.desc3") + " " + total + ".";
362                 }
363
364                 selectedCountLabel.setText(str);
365         }
366         
367         
368         
369         /**
370          * A table cell renderer that uses another renderer and sets the background and
371          * foreground of the returned component based on the selection of the variable.
372          */
373         private class SelectionBackgroundCellRenderer implements TableCellRenderer {
374
375                 private final TableCellRenderer renderer;
376                 
377                 public SelectionBackgroundCellRenderer(TableCellRenderer renderer) {
378                         this.renderer = renderer;
379                 }
380                 
381                 @Override
382                 public Component getTableCellRendererComponent(JTable table, Object value,
383                                 boolean isSelected, boolean hasFocus, int row, int column) {
384                         
385                         Component component = renderer.getTableCellRendererComponent(table, 
386                                         value, isSelected, hasFocus, row, column);
387                         
388                         if (selected[row]) {
389                                 component.setBackground(table.getSelectionBackground());
390                                 component.setForeground(table.getSelectionForeground());
391                         } else {
392                                 component.setBackground(table.getBackground());
393                                 component.setForeground(table.getForeground());
394                         }
395                         
396                         return component;
397                 }
398                 
399         }
400         
401         
402         /**
403          * The table model for the variable selection.
404          */
405         private class SelectionTableModel extends AbstractTableModel {
406                 private static final int SELECTED = 0;
407                 private static final int NAME = 1;
408                 private static final int UNIT = 2;
409
410                 @Override
411                 public int getColumnCount() {
412                         return 3;
413                 }
414
415                 @Override
416                 public int getRowCount() {
417                         return types.length;
418                 }
419                 
420                 @Override
421                 public String getColumnName(int column) {
422                         switch (column) {
423                         case SELECTED:
424                                 return "";
425                         case NAME:
426                                 //// Variable
427                                 return trans.get("SimExpPan.Col.Variable");
428                         case UNIT:
429                                 //// Unit
430                                 return trans.get("SimExpPan.Col.Unit");
431                         default:
432                                 throw new IndexOutOfBoundsException("column=" + column);
433                         }
434                         
435                 }
436                 
437                 @Override
438                 public Class<?> getColumnClass(int column) {
439                         switch (column) {
440                         case SELECTED:
441                                 return Boolean.class;
442                         case NAME:
443                                 return FlightDataType.class;
444                         case UNIT:
445                                 return Unit.class;
446                         default:
447                                 throw new IndexOutOfBoundsException("column=" + column);
448                         }
449                 }
450
451                 @Override
452                 public Object getValueAt(int row, int column) {
453
454                         switch (column) {
455                         case SELECTED:
456                                 return selected[row];
457                                 
458                         case NAME:
459                                 return types[row];
460                                 
461                         case UNIT:
462                                 return units[row];
463                                 
464                         default:
465                                 throw new IndexOutOfBoundsException("column="+column);
466                         }
467                         
468                 }
469
470                 @Override
471                 public void setValueAt(Object value, int row, int column) {
472                         
473                         switch (column) {
474                         case SELECTED:
475                                 selected[row] = (Boolean)value;
476                                 this.fireTableRowsUpdated(row, row);
477                                 updateSelectedCount();
478                                 break;
479                                 
480                         case NAME:
481                                 break;
482                                 
483                         case UNIT:
484                                 units[row] = (Unit)value;
485                                 break;
486                                 
487                         default:
488                                 throw new IndexOutOfBoundsException("column="+column);
489                         }
490                         
491                 }
492
493                 @Override
494                 public boolean isCellEditable(int row, int column) {
495                         switch (column) {
496                         case SELECTED:
497                                 return true;
498                                 
499                         case NAME:
500                                 return false;
501                                 
502                         case UNIT:
503                                 return types[row].getUnitGroup().getUnitCount() > 1;
504                                 
505                         default:
506                                 throw new IndexOutOfBoundsException("column="+column);
507                         }
508                 }
509                 
510                 public void selectAll() {
511                         Arrays.fill(selected, true);
512                         updateSelectedCount();
513                         this.fireTableDataChanged();
514                 }
515                 
516                 public void selectNone() {
517                         Arrays.fill(selected, false);
518                         updateSelectedCount();
519                         this.fireTableDataChanged();
520                 }
521                 
522         }
523         
524 }