1 package net.sf.openrocket.gui.components;
3 import java.awt.Component;
4 import java.awt.event.ActionEvent;
5 import java.awt.event.ActionListener;
7 import java.util.Arrays;
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;
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.Prefs;
33 import net.sf.openrocket.util.SaveCSVWorker;
35 public class SimulationExportPanel extends JPanel {
37 private static final String SPACE = "SPACE";
38 private static final String TAB = "TAB";
40 private static final FileFilter CSV_FILE_FILTER = new FileFilter() {
42 public String getDescription() {
43 return "Comma Separated Files (*.csv)";
46 public boolean accept(File f) {
49 String name = f.getName().toLowerCase();
50 return name.endsWith(".csv");
56 private final JTable table;
57 private final SelectionTableModel tableModel;
58 private final JLabel selectedCountLabel;
60 private final Simulation simulation;
61 private final FlightDataBranch branch;
63 private final boolean[] selected;
64 private final FlightDataBranch.Type[] types;
65 private final Unit[] units;
67 private final JComboBox fieldSeparator;
68 private final JCheckBox simulationComments;
69 private final JCheckBox fieldNameComments;
70 private final JCheckBox eventComments;
71 private final JComboBox commentCharacter;
74 public SimulationExportPanel(Simulation sim) {
75 super(new MigLayout("fill, flowy"));
83 this.simulation = sim;
85 // TODO: MEDIUM: Only exports primary branch
87 final FlightData data = simulation.getSimulatedData();
89 // Check that data exists
90 if (data == null || data.getBranchCount() == 0 ||
91 data.getBranch(0).getTypes().length == 0) {
92 throw new IllegalArgumentException("No data for panel");
96 // Create the data model
97 branch = data.getBranch(0);
99 types = branch.getTypes();
102 selected = new boolean[types.length];
103 units = new Unit[types.length];
104 for (int i = 0; i < types.length; i++) {
105 selected[i] = Prefs.isExportSelected(types[i]);
106 units[i] = types[i].getUnitGroup().getDefaultUnit();
110 //// Create the panel
113 // Set up the variable selection table
114 tableModel = new SelectionTableModel();
115 table = new JTable(tableModel);
116 table.setDefaultRenderer(Object.class,
117 new SelectionBackgroundCellRenderer(table.getDefaultRenderer(Object.class)));
118 table.setDefaultRenderer(Boolean.class,
119 new SelectionBackgroundCellRenderer(table.getDefaultRenderer(Boolean.class)));
120 table.setRowSelectionAllowed(false);
121 table.setColumnSelectionAllowed(false);
123 table.setDefaultEditor(Unit.class, new UnitCellEditor() {
125 protected UnitGroup getUnitGroup(Unit value, int row, int column) {
126 return types[row].getUnitGroup();
131 TableColumnModel columnModel = table.getColumnModel();
132 TableColumn col = columnModel.getColumn(0);
133 int w = table.getRowHeight();
135 col.setPreferredWidth(w);
138 col = columnModel.getColumn(1);
139 col.setPreferredWidth(200);
141 col = columnModel.getColumn(2);
142 col.setPreferredWidth(100);
146 panel = new JPanel(new MigLayout("fill"));
147 panel.setBorder(BorderFactory.createTitledBorder("Variables to export"));
149 panel.add(new JScrollPane(table), "wmin 300lp, width 300lp, height 1, grow 100, wrap");
151 // Select all/none buttons
152 button = new JButton("Select all");
153 button.addActionListener(new ActionListener() {
155 public void actionPerformed(ActionEvent e) {
156 tableModel.selectAll();
159 panel.add(button, "split 2, growx 1, sizegroup selectbutton");
161 button = new JButton("Select none");
162 button.addActionListener(new ActionListener() {
164 public void actionPerformed(ActionEvent e) {
165 tableModel.selectNone();
168 panel.add(button, "growx 1, sizegroup selectbutton, wrap");
171 selectedCountLabel = new JLabel();
172 updateSelectedCount();
173 panel.add(selectedCountLabel);
175 this.add(panel, "grow 100, wrap");
179 // Field separator panel
180 panel = new JPanel(new MigLayout("fill"));
181 panel.setBorder(BorderFactory.createTitledBorder("Field separator"));
183 label = new JLabel("Field separator string:");
184 tip = "<html>The string used to separate the fields in the exported file.<br>" +
185 "Use ',' for a Comma Separated Values (CSV) file.";
186 label.setToolTipText(tip);
189 fieldSeparator = new JComboBox(new String[] { ",", ";", SPACE, TAB });
190 fieldSeparator.setEditable(true);
191 fieldSeparator.setSelectedItem(Prefs.getString(Prefs.EXPORT_FIELD_SEPARATOR,
193 fieldSeparator.setToolTipText(tip);
194 panel.add(fieldSeparator);
196 this.add(panel, "spany, split, growx 1");
201 // Comments separator panel
202 panel = new JPanel(new MigLayout("fill"));
203 panel.setBorder(BorderFactory.createTitledBorder("Comments"));
205 simulationComments = new JCheckBox("Include simulation description");
206 simulationComments.setToolTipText("Include a comment at the beginning of the file " +
207 "describing the simulation.");
208 simulationComments.setSelected(Prefs.getBoolean(Prefs.EXPORT_SIMULATION_COMMENT,
210 panel.add(simulationComments, "wrap");
213 fieldNameComments = new JCheckBox("Include field descriptions");
214 fieldNameComments.setToolTipText("Include a comment line with the descriptions of " +
215 "the exported variables.");
216 fieldNameComments.setSelected(Prefs.getBoolean(Prefs.EXPORT_FIELD_NAME_COMMENT, true));
217 panel.add(fieldNameComments, "wrap");
220 eventComments = new JCheckBox("Include flight events");
221 eventComments.setToolTipText("Include a comment line for every flight event.");
222 eventComments.setSelected(Prefs.getBoolean(Prefs.EXPORT_EVENT_COMMENTS, true));
223 panel.add(eventComments, "wrap");
226 label = new JLabel("Comment character:");
227 tip = "The character(s) that mark a comment line.";
228 label.setToolTipText(tip);
229 panel.add(label, "split 2");
231 commentCharacter = new JComboBox(new String[] { "#", "%", ";" });
232 commentCharacter.setEditable(true);
233 commentCharacter.setSelectedItem(Prefs.getString(Prefs.EXPORT_COMMENT_CHARACTER, "#"));
234 commentCharacter.setToolTipText(tip);
235 panel.add(commentCharacter);
237 this.add(panel, "growx 1");
240 // Space-filling panel
241 panel = new JPanel();
242 this.add(panel, "width 1, height 1, grow 1");
246 button = new JButton("Export to file...");
247 button.addActionListener(new ActionListener() {
249 public void actionPerformed(ActionEvent e) {
253 this.add(button, "gapbottom para, gapright para, right");
258 private void doExport() {
259 JFileChooser chooser = new JFileChooser();
260 chooser.setFileFilter(CSV_FILE_FILTER);
261 chooser.setCurrentDirectory(Prefs.getDefaultDirectory());
263 if (chooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION)
266 File file = chooser.getSelectedFile();
270 if (file.getName().indexOf('.') < 0) {
271 String name = file.getAbsolutePath();
272 name = name + ".csv";
273 file = new File(name);
277 int ret = JOptionPane.showConfirmDialog(this,
278 "File \"" + file.getName() + "\" exists. Overwrite?",
279 "File exists", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
280 if (ret != JOptionPane.YES_OPTION)
284 String commentChar = commentCharacter.getSelectedItem().toString();
285 String fieldSep = fieldSeparator.getSelectedItem().toString();
286 boolean simulationComment = simulationComments.isSelected();
287 boolean fieldComment = fieldNameComments.isSelected();
288 boolean eventComment = eventComments.isSelected();
290 // Store preferences and export
292 Prefs.setDefaultDirectory(chooser.getCurrentDirectory());
293 for (int i=0; i < selected.length; i++) {
294 Prefs.setExportSelected(types[i], selected[i]);
298 Prefs.putString(Prefs.EXPORT_FIELD_SEPARATOR, fieldSep);
299 Prefs.putString(Prefs.EXPORT_COMMENT_CHARACTER, commentChar);
300 Prefs.putBoolean(Prefs.EXPORT_EVENT_COMMENTS, eventComment);
301 Prefs.putBoolean(Prefs.EXPORT_FIELD_NAME_COMMENT, fieldComment);
302 Prefs.putBoolean(Prefs.EXPORT_SIMULATION_COMMENT, simulationComment);
305 FlightDataBranch.Type[] fieldTypes = new FlightDataBranch.Type[n];
306 Unit[] fieldUnits = new Unit[n];
308 for (int i=0; i < selected.length; i++) {
310 fieldTypes[pos] = types[i];
311 fieldUnits[pos] = units[i];
316 if (fieldSep.equals(SPACE)) {
318 } else if (fieldSep.equals(TAB)) {
323 SaveCSVWorker.export(file, simulation, branch, fieldTypes, fieldUnits, fieldSep,
324 commentChar, simulationComment, fieldComment, eventComment,
325 SwingUtilities.getWindowAncestor(this));
329 private void updateSelectedCount() {
330 int total = selected.length;
334 for (int i=0; i < selected.length; i++) {
340 str = "Exporting 1 variable out of " + total + ".";
342 str = "Exporting "+n+" variables out of " + total + ".";
345 selectedCountLabel.setText(str);
351 * A table cell renderer that uses another renderer and sets the background and
352 * foreground of the returned component based on the selection of the variable.
354 private class SelectionBackgroundCellRenderer implements TableCellRenderer {
356 private final TableCellRenderer renderer;
358 public SelectionBackgroundCellRenderer(TableCellRenderer renderer) {
359 this.renderer = renderer;
363 public Component getTableCellRendererComponent(JTable table, Object value,
364 boolean isSelected, boolean hasFocus, int row, int column) {
366 Component component = renderer.getTableCellRendererComponent(table,
367 value, isSelected, hasFocus, row, column);
370 component.setBackground(table.getSelectionBackground());
371 component.setForeground(table.getSelectionForeground());
373 component.setBackground(table.getBackground());
374 component.setForeground(table.getForeground());
384 * The table model for the variable selection.
386 private class SelectionTableModel extends AbstractTableModel {
387 private static final int SELECTED = 0;
388 private static final int NAME = 1;
389 private static final int UNIT = 2;
392 public int getColumnCount() {
397 public int getRowCount() {
402 public String getColumnName(int column) {
411 throw new IndexOutOfBoundsException("column=" + column);
417 public Class<?> getColumnClass(int column) {
420 return Boolean.class;
422 return FlightDataBranch.Type.class;
426 throw new IndexOutOfBoundsException("column=" + column);
431 public Object getValueAt(int row, int column) {
435 return selected[row];
444 throw new IndexOutOfBoundsException("column="+column);
450 public void setValueAt(Object value, int row, int column) {
454 selected[row] = (Boolean)value;
455 this.fireTableRowsUpdated(row, row);
456 updateSelectedCount();
463 units[row] = (Unit)value;
467 throw new IndexOutOfBoundsException("column="+column);
473 public boolean isCellEditable(int row, int column) {
482 return types[row].getUnitGroup().getUnitCount() > 1;
485 throw new IndexOutOfBoundsException("column="+column);
489 public void selectAll() {
490 Arrays.fill(selected, true);
491 updateSelectedCount();
492 this.fireTableDataChanged();
495 public void selectNone() {
496 Arrays.fill(selected, false);
497 updateSelectedCount();
498 this.fireTableDataChanged();