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