create changelog entry
[debian/openrocket] / core / 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.JFileChooser;
12 import javax.swing.JLabel;
13 import javax.swing.JPanel;
14 import javax.swing.JScrollPane;
15 import javax.swing.JTable;
16 import javax.swing.SwingUtilities;
17 import javax.swing.table.AbstractTableModel;
18 import javax.swing.table.TableCellRenderer;
19 import javax.swing.table.TableColumn;
20 import javax.swing.table.TableColumnModel;
21
22 import net.miginfocom.swing.MigLayout;
23 import net.sf.openrocket.document.Simulation;
24 import net.sf.openrocket.gui.util.FileHelper;
25 import net.sf.openrocket.gui.util.GUIUtil;
26 import net.sf.openrocket.gui.util.SwingPreferences;
27 import net.sf.openrocket.gui.util.SaveCSVWorker;
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
36 public class SimulationExportPanel extends JPanel {
37         
38         private static final String SPACE = "SPACE";
39         private static final String TAB = "TAB";
40         private static final Translator trans = Application.getTranslator();
41         
42         private static final int OPTION_SIMULATION_COMMENTS = 0;
43         private static final int OPTION_FIELD_DESCRIPTIONS = 1;
44         private static final int OPTION_FLIGHT_EVENTS = 2;
45         
46         private final JTable table;
47         private final SelectionTableModel tableModel;
48         private final JLabel selectedCountLabel;
49         
50         private final Simulation simulation;
51         private final FlightDataBranch branch;
52         
53         private final boolean[] selected;
54         private final FlightDataType[] types;
55         private final Unit[] units;
56         
57         private final CsvOptionPanel csvOptions;
58         
59         
60         public SimulationExportPanel(Simulation sim) {
61                 super(new MigLayout("fill, flowy"));
62                 
63                 JPanel panel;
64                 JButton button;
65                 
66
67                 this.simulation = sim;
68                 
69                 // TODO: MEDIUM: Only exports primary branch
70                 
71                 final FlightData data = simulation.getSimulatedData();
72                 
73                 // Check that data exists
74                 if (data == null || data.getBranchCount() == 0 ||
75                                 data.getBranch(0).getTypes().length == 0) {
76                         throw new IllegalArgumentException("No data for panel");
77                 }
78                 
79
80                 // Create the data model
81                 branch = data.getBranch(0);
82                 
83                 types = branch.getTypes();
84                 Arrays.sort(types);
85                 
86                 selected = new boolean[types.length];
87                 units = new Unit[types.length];
88                 for (int i = 0; i < types.length; i++) {
89                         selected[i] = ((SwingPreferences) Application.getPreferences()).isExportSelected(types[i]);
90                         units[i] = types[i].getUnitGroup().getDefaultUnit();
91                 }
92                 
93
94                 //// Create the panel
95                 
96
97                 // Set up the variable selection table
98                 tableModel = new SelectionTableModel();
99                 table = new JTable(tableModel);
100                 table.setDefaultRenderer(Object.class,
101                                 new SelectionBackgroundCellRenderer(table.getDefaultRenderer(Object.class)));
102                 table.setDefaultRenderer(Boolean.class,
103                                 new SelectionBackgroundCellRenderer(table.getDefaultRenderer(Boolean.class)));
104                 table.setRowSelectionAllowed(false);
105                 table.setColumnSelectionAllowed(false);
106                 
107                 table.setDefaultEditor(Unit.class, new UnitCellEditor() {
108                         @Override
109                         protected UnitGroup getUnitGroup(Unit value, int row, int column) {
110                                 return types[row].getUnitGroup();
111                         }
112                 });
113                 
114                 // Set column widths
115                 TableColumnModel columnModel = table.getColumnModel();
116                 TableColumn col = columnModel.getColumn(0);
117                 int w = table.getRowHeight();
118                 col.setMinWidth(w);
119                 col.setPreferredWidth(w);
120                 col.setMaxWidth(w);
121                 
122                 col = columnModel.getColumn(1);
123                 col.setPreferredWidth(200);
124                 
125                 col = columnModel.getColumn(2);
126                 col.setPreferredWidth(100);
127                 
128                 table.addMouseListener(new GUIUtil.BooleanTableClickListener(table));
129                 
130                 // Add table
131                 panel = new JPanel(new MigLayout("fill"));
132                 panel.setBorder(BorderFactory.createTitledBorder(trans.get("SimExpPan.border.Vartoexport")));
133                 
134                 panel.add(new JScrollPane(table), "wmin 300lp, width 300lp, height 1, grow 100, wrap");
135                 
136                 // Select all/none buttons
137                 button = new JButton(trans.get("SimExpPan.but.Selectall"));
138                 button.addActionListener(new ActionListener() {
139                         @Override
140                         public void actionPerformed(ActionEvent e) {
141                                 tableModel.selectAll();
142                         }
143                 });
144                 panel.add(button, "split 2, growx 1, sizegroup selectbutton");
145                 
146                 button = new JButton(trans.get("SimExpPan.but.Selectnone"));
147                 button.addActionListener(new ActionListener() {
148                         @Override
149                         public void actionPerformed(ActionEvent e) {
150                                 tableModel.selectNone();
151                         }
152                 });
153                 panel.add(button, "growx 1, sizegroup selectbutton, wrap");
154                 
155
156                 selectedCountLabel = new JLabel();
157                 updateSelectedCount();
158                 panel.add(selectedCountLabel);
159                 
160                 this.add(panel, "grow 100, wrap");
161                 
162
163                 // These need to be in the order of the OPTIONS_XXX indices
164                 csvOptions = new CsvOptionPanel(SimulationExportPanel.class,
165                                 trans.get("SimExpPan.checkbox.Includesimudesc"),
166                                 trans.get("SimExpPan.checkbox.ttip.Includesimudesc"),
167                                 trans.get("SimExpPan.checkbox.Includefielddesc"),
168                                 trans.get("SimExpPan.checkbox.ttip.Includefielddesc"),
169                                 trans.get("SimExpPan.checkbox.Incflightevents"),
170                                 trans.get("SimExpPan.checkbox.ttip.Incflightevents"));
171                 
172                 this.add(csvOptions, "spany, split, growx 1");
173                 
174
175                 // Space-filling panel
176                 panel = new JPanel();
177                 this.add(panel, "width 1, height 1, grow 1");
178                 
179
180                 // Export button
181                 button = new JButton(trans.get("SimExpPan.but.Exporttofile"));
182                 button.addActionListener(new ActionListener() {
183                         @Override
184                         public void actionPerformed(ActionEvent e) {
185                                 doExport();
186                         }
187                 });
188                 this.add(button, "gapbottom para, gapright para, right");
189                 
190         }
191         
192         
193         private void doExport() {
194                 JFileChooser chooser = new JFileChooser();
195                 chooser.setFileFilter(FileHelper.CSV_FILE_FILTER);
196                 chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory());
197                 
198                 if (chooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION)
199                         return;
200                 
201                 File file = chooser.getSelectedFile();
202                 if (file == null)
203                         return;
204                 
205                 file = FileHelper.ensureExtension(file, "csv");
206                 if (!FileHelper.confirmWrite(file, this)) {
207                         return;
208                 }
209                 
210
211                 String commentChar = csvOptions.getCommentCharacter();
212                 String fieldSep = csvOptions.getFieldSeparator();
213                 boolean simulationComment = csvOptions.getSelectionOption(OPTION_SIMULATION_COMMENTS);
214                 boolean fieldComment = csvOptions.getSelectionOption(OPTION_FIELD_DESCRIPTIONS);
215                 boolean eventComment = csvOptions.getSelectionOption(OPTION_FLIGHT_EVENTS);
216                 csvOptions.storePreferences();
217                 
218                 // Store preferences and export
219                 int n = 0;
220                 ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(chooser.getCurrentDirectory());
221                 for (int i = 0; i < selected.length; i++) {
222                         ((SwingPreferences) Application.getPreferences()).setExportSelected(types[i], selected[i]);
223                         if (selected[i])
224                                 n++;
225                 }
226                 
227
228                 FlightDataType[] fieldTypes = new FlightDataType[n];
229                 Unit[] fieldUnits = new Unit[n];
230                 int pos = 0;
231                 for (int i = 0; i < selected.length; i++) {
232                         if (selected[i]) {
233                                 fieldTypes[pos] = types[i];
234                                 fieldUnits[pos] = units[i];
235                                 pos++;
236                         }
237                 }
238                 
239                 if (fieldSep.equals(SPACE)) {
240                         fieldSep = " ";
241                 } else if (fieldSep.equals(TAB)) {
242                         fieldSep = "\t";
243                 }
244                 
245
246                 SaveCSVWorker.export(file, simulation, branch, fieldTypes, fieldUnits, fieldSep,
247                                 commentChar, simulationComment, fieldComment, eventComment,
248                                 SwingUtilities.getWindowAncestor(this));
249         }
250         
251         
252         private void updateSelectedCount() {
253                 int total = selected.length;
254                 int n = 0;
255                 String str;
256                 
257                 for (int i = 0; i < selected.length; i++) {
258                         if (selected[i])
259                                 n++;
260                 }
261                 
262                 if (n == 1) {
263                         //// Exporting 1 variable out of 
264                         str = trans.get("SimExpPan.ExportingVar.desc1") + " " + total + ".";
265                 } else {
266                         //// Exporting 
267                         //// variables out of
268                         str = trans.get("SimExpPan.ExportingVar.desc2") + " " + n + " " +
269                                         trans.get("SimExpPan.ExportingVar.desc3") + " " + total + ".";
270                 }
271                 
272                 selectedCountLabel.setText(str);
273         }
274         
275         
276
277         /**
278          * A table cell renderer that uses another renderer and sets the background and
279          * foreground of the returned component based on the selection of the variable.
280          */
281         private class SelectionBackgroundCellRenderer implements TableCellRenderer {
282                 
283                 private final TableCellRenderer renderer;
284                 
285                 public SelectionBackgroundCellRenderer(TableCellRenderer renderer) {
286                         this.renderer = renderer;
287                 }
288                 
289                 @Override
290                 public Component getTableCellRendererComponent(JTable table, Object value,
291                                 boolean isSelected, boolean hasFocus, int row, int column) {
292                         
293                         Component component = renderer.getTableCellRendererComponent(table,
294                                         value, isSelected, hasFocus, row, column);
295                         
296                         if (selected[row]) {
297                                 component.setBackground(table.getSelectionBackground());
298                                 component.setForeground(table.getSelectionForeground());
299                         } else {
300                                 component.setBackground(table.getBackground());
301                                 component.setForeground(table.getForeground());
302                         }
303                         
304                         return component;
305                 }
306                 
307         }
308         
309         
310         /**
311          * The table model for the variable selection.
312          */
313         private class SelectionTableModel extends AbstractTableModel {
314                 private static final int SELECTED = 0;
315                 private static final int NAME = 1;
316                 private static final int UNIT = 2;
317                 
318                 @Override
319                 public int getColumnCount() {
320                         return 3;
321                 }
322                 
323                 @Override
324                 public int getRowCount() {
325                         return types.length;
326                 }
327                 
328                 @Override
329                 public String getColumnName(int column) {
330                         switch (column) {
331                         case SELECTED:
332                                 return "";
333                         case NAME:
334                                 //// Variable
335                                 return trans.get("SimExpPan.Col.Variable");
336                         case UNIT:
337                                 //// Unit
338                                 return trans.get("SimExpPan.Col.Unit");
339                         default:
340                                 throw new IndexOutOfBoundsException("column=" + column);
341                         }
342                         
343                 }
344                 
345                 @Override
346                 public Class<?> getColumnClass(int column) {
347                         switch (column) {
348                         case SELECTED:
349                                 return Boolean.class;
350                         case NAME:
351                                 return FlightDataType.class;
352                         case UNIT:
353                                 return Unit.class;
354                         default:
355                                 throw new IndexOutOfBoundsException("column=" + column);
356                         }
357                 }
358                 
359                 @Override
360                 public Object getValueAt(int row, int column) {
361                         
362                         switch (column) {
363                         case SELECTED:
364                                 return selected[row];
365                                 
366                         case NAME:
367                                 return types[row];
368                                 
369                         case UNIT:
370                                 return units[row];
371                                 
372                         default:
373                                 throw new IndexOutOfBoundsException("column=" + column);
374                         }
375                         
376                 }
377                 
378                 @Override
379                 public void setValueAt(Object value, int row, int column) {
380                         
381                         switch (column) {
382                         case SELECTED:
383                                 selected[row] = (Boolean) value;
384                                 this.fireTableRowsUpdated(row, row);
385                                 updateSelectedCount();
386                                 break;
387                         
388                         case NAME:
389                                 break;
390                         
391                         case UNIT:
392                                 units[row] = (Unit) value;
393                                 break;
394                         
395                         default:
396                                 throw new IndexOutOfBoundsException("column=" + column);
397                         }
398                         
399                 }
400                 
401                 @Override
402                 public boolean isCellEditable(int row, int column) {
403                         switch (column) {
404                         case SELECTED:
405                                 return true;
406                                 
407                         case NAME:
408                                 return false;
409                                 
410                         case UNIT:
411                                 return types[row].getUnitGroup().getUnitCount() > 1;
412                                 
413                         default:
414                                 throw new IndexOutOfBoundsException("column=" + column);
415                         }
416                 }
417                 
418                 public void selectAll() {
419                         Arrays.fill(selected, true);
420                         updateSelectedCount();
421                         this.fireTableDataChanged();
422                 }
423                 
424                 public void selectNone() {
425                         Arrays.fill(selected, false);
426                         updateSelectedCount();
427                         this.fireTableDataChanged();
428                 }
429                 
430         }
431         
432 }