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.GUIUtil;
33 import net.sf.openrocket.util.Prefs;
34 import net.sf.openrocket.util.SaveCSVWorker;
36 public class SimulationExportPanel extends JPanel {
38 private static final String SPACE = "SPACE";
39 private static final String TAB = "TAB";
41 private static final FileFilter CSV_FILE_FILTER = new FileFilter() {
43 public String getDescription() {
44 return "Comma Separated Files (*.csv)";
47 public boolean accept(File f) {
50 String name = f.getName().toLowerCase();
51 return name.endsWith(".csv");
57 private final JTable table;
58 private final SelectionTableModel tableModel;
59 private final JLabel selectedCountLabel;
61 private final Simulation simulation;
62 private final FlightDataBranch branch;
64 private final boolean[] selected;
65 private final FlightDataBranch.Type[] types;
66 private final Unit[] units;
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;
75 public SimulationExportPanel(Simulation sim) {
76 super(new MigLayout("fill, flowy"));
84 this.simulation = sim;
86 // TODO: MEDIUM: Only exports primary branch
88 final FlightData data = simulation.getSimulatedData();
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");
97 // Create the data model
98 branch = data.getBranch(0);
100 types = branch.getTypes();
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();
111 //// Create the panel
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);
124 table.setDefaultEditor(Unit.class, new UnitCellEditor() {
126 protected UnitGroup getUnitGroup(Unit value, int row, int column) {
127 return types[row].getUnitGroup();
132 TableColumnModel columnModel = table.getColumnModel();
133 TableColumn col = columnModel.getColumn(0);
134 int w = table.getRowHeight();
136 col.setPreferredWidth(w);
139 col = columnModel.getColumn(1);
140 col.setPreferredWidth(200);
142 col = columnModel.getColumn(2);
143 col.setPreferredWidth(100);
145 table.addMouseListener(new GUIUtil.BooleanTableClickListener(table));
148 panel = new JPanel(new MigLayout("fill"));
149 panel.setBorder(BorderFactory.createTitledBorder("Variables to export"));
151 panel.add(new JScrollPane(table), "wmin 300lp, width 300lp, height 1, grow 100, wrap");
153 // Select all/none buttons
154 button = new JButton("Select all");
155 button.addActionListener(new ActionListener() {
157 public void actionPerformed(ActionEvent e) {
158 tableModel.selectAll();
161 panel.add(button, "split 2, growx 1, sizegroup selectbutton");
163 button = new JButton("Select none");
164 button.addActionListener(new ActionListener() {
166 public void actionPerformed(ActionEvent e) {
167 tableModel.selectNone();
170 panel.add(button, "growx 1, sizegroup selectbutton, wrap");
173 selectedCountLabel = new JLabel();
174 updateSelectedCount();
175 panel.add(selectedCountLabel);
177 this.add(panel, "grow 100, wrap");
181 // Field separator panel
182 panel = new JPanel(new MigLayout("fill"));
183 panel.setBorder(BorderFactory.createTitledBorder("Field separator"));
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);
191 fieldSeparator = new JComboBox(new String[] { ",", ";", SPACE, TAB });
192 fieldSeparator.setEditable(true);
193 fieldSeparator.setSelectedItem(Prefs.getString(Prefs.EXPORT_FIELD_SEPARATOR,
195 fieldSeparator.setToolTipText(tip);
196 panel.add(fieldSeparator);
198 this.add(panel, "spany, split, growx 1");
203 // Comments separator panel
204 panel = new JPanel(new MigLayout("fill"));
205 panel.setBorder(BorderFactory.createTitledBorder("Comments"));
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,
212 panel.add(simulationComments, "wrap");
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");
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");
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");
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);
239 this.add(panel, "growx 1");
242 // Space-filling panel
243 panel = new JPanel();
244 this.add(panel, "width 1, height 1, grow 1");
248 button = new JButton("Export to file...");
249 button.addActionListener(new ActionListener() {
251 public void actionPerformed(ActionEvent e) {
255 this.add(button, "gapbottom para, gapright para, right");
260 private void doExport() {
261 JFileChooser chooser = new JFileChooser();
262 chooser.setFileFilter(CSV_FILE_FILTER);
263 chooser.setCurrentDirectory(Prefs.getDefaultDirectory());
265 if (chooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION)
268 File file = chooser.getSelectedFile();
272 if (file.getName().indexOf('.') < 0) {
273 String name = file.getAbsolutePath();
274 name = name + ".csv";
275 file = new File(name);
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)
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();
292 // Store preferences and export
294 Prefs.setDefaultDirectory(chooser.getCurrentDirectory());
295 for (int i=0; i < selected.length; i++) {
296 Prefs.setExportSelected(types[i], selected[i]);
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);
307 FlightDataBranch.Type[] fieldTypes = new FlightDataBranch.Type[n];
308 Unit[] fieldUnits = new Unit[n];
310 for (int i=0; i < selected.length; i++) {
312 fieldTypes[pos] = types[i];
313 fieldUnits[pos] = units[i];
318 if (fieldSep.equals(SPACE)) {
320 } else if (fieldSep.equals(TAB)) {
325 SaveCSVWorker.export(file, simulation, branch, fieldTypes, fieldUnits, fieldSep,
326 commentChar, simulationComment, fieldComment, eventComment,
327 SwingUtilities.getWindowAncestor(this));
331 private void updateSelectedCount() {
332 int total = selected.length;
336 for (int i=0; i < selected.length; i++) {
342 str = "Exporting 1 variable out of " + total + ".";
344 str = "Exporting "+n+" variables out of " + total + ".";
347 selectedCountLabel.setText(str);
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.
356 private class SelectionBackgroundCellRenderer implements TableCellRenderer {
358 private final TableCellRenderer renderer;
360 public SelectionBackgroundCellRenderer(TableCellRenderer renderer) {
361 this.renderer = renderer;
365 public Component getTableCellRendererComponent(JTable table, Object value,
366 boolean isSelected, boolean hasFocus, int row, int column) {
368 Component component = renderer.getTableCellRendererComponent(table,
369 value, isSelected, hasFocus, row, column);
372 component.setBackground(table.getSelectionBackground());
373 component.setForeground(table.getSelectionForeground());
375 component.setBackground(table.getBackground());
376 component.setForeground(table.getForeground());
386 * The table model for the variable selection.
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;
394 public int getColumnCount() {
399 public int getRowCount() {
404 public String getColumnName(int column) {
413 throw new IndexOutOfBoundsException("column=" + column);
419 public Class<?> getColumnClass(int column) {
422 return Boolean.class;
424 return FlightDataBranch.Type.class;
428 throw new IndexOutOfBoundsException("column=" + column);
433 public Object getValueAt(int row, int column) {
437 return selected[row];
446 throw new IndexOutOfBoundsException("column="+column);
452 public void setValueAt(Object value, int row, int column) {
456 selected[row] = (Boolean)value;
457 this.fireTableRowsUpdated(row, row);
458 updateSelectedCount();
465 units[row] = (Unit)value;
469 throw new IndexOutOfBoundsException("column="+column);
475 public boolean isCellEditable(int row, int column) {
484 return types[row].getUnitGroup().getUnitCount() > 1;
487 throw new IndexOutOfBoundsException("column="+column);
491 public void selectAll() {
492 Arrays.fill(selected, true);
493 updateSelectedCount();
494 this.fireTableDataChanged();
497 public void selectNone() {
498 Arrays.fill(selected, false);
499 updateSelectedCount();
500 this.fireTableDataChanged();