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.simulation.FlightDataType;
31 import net.sf.openrocket.unit.Unit;
32 import net.sf.openrocket.unit.UnitGroup;
33 import net.sf.openrocket.util.GUIUtil;
34 import net.sf.openrocket.util.Prefs;
35 import net.sf.openrocket.util.SaveCSVWorker;
37 public class SimulationExportPanel extends JPanel {
39 private static final String SPACE = "SPACE";
40 private static final String TAB = "TAB";
42 private static final FileFilter CSV_FILE_FILTER = new FileFilter() {
44 public String getDescription() {
45 return "Comma Separated Files (*.csv)";
48 public boolean accept(File f) {
51 String name = f.getName().toLowerCase();
52 return name.endsWith(".csv");
58 private final JTable table;
59 private final SelectionTableModel tableModel;
60 private final JLabel selectedCountLabel;
62 private final Simulation simulation;
63 private final FlightDataBranch branch;
65 private final boolean[] selected;
66 private final FlightDataType[] types;
67 private final Unit[] units;
69 private final JComboBox fieldSeparator;
70 private final JCheckBox simulationComments;
71 private final JCheckBox fieldNameComments;
72 private final JCheckBox eventComments;
73 private final JComboBox commentCharacter;
76 public SimulationExportPanel(Simulation sim) {
77 super(new MigLayout("fill, flowy"));
85 this.simulation = sim;
87 // TODO: MEDIUM: Only exports primary branch
89 final FlightData data = simulation.getSimulatedData();
91 // Check that data exists
92 if (data == null || data.getBranchCount() == 0 ||
93 data.getBranch(0).getTypes().length == 0) {
94 throw new IllegalArgumentException("No data for panel");
98 // Create the data model
99 branch = data.getBranch(0);
101 types = branch.getTypes();
104 selected = new boolean[types.length];
105 units = new Unit[types.length];
106 for (int i = 0; i < types.length; i++) {
107 selected[i] = Prefs.isExportSelected(types[i]);
108 units[i] = types[i].getUnitGroup().getDefaultUnit();
112 //// Create the panel
115 // Set up the variable selection table
116 tableModel = new SelectionTableModel();
117 table = new JTable(tableModel);
118 table.setDefaultRenderer(Object.class,
119 new SelectionBackgroundCellRenderer(table.getDefaultRenderer(Object.class)));
120 table.setDefaultRenderer(Boolean.class,
121 new SelectionBackgroundCellRenderer(table.getDefaultRenderer(Boolean.class)));
122 table.setRowSelectionAllowed(false);
123 table.setColumnSelectionAllowed(false);
125 table.setDefaultEditor(Unit.class, new UnitCellEditor() {
127 protected UnitGroup getUnitGroup(Unit value, int row, int column) {
128 return types[row].getUnitGroup();
133 TableColumnModel columnModel = table.getColumnModel();
134 TableColumn col = columnModel.getColumn(0);
135 int w = table.getRowHeight();
137 col.setPreferredWidth(w);
140 col = columnModel.getColumn(1);
141 col.setPreferredWidth(200);
143 col = columnModel.getColumn(2);
144 col.setPreferredWidth(100);
146 table.addMouseListener(new GUIUtil.BooleanTableClickListener(table));
149 panel = new JPanel(new MigLayout("fill"));
150 panel.setBorder(BorderFactory.createTitledBorder("Variables to export"));
152 panel.add(new JScrollPane(table), "wmin 300lp, width 300lp, height 1, grow 100, wrap");
154 // Select all/none buttons
155 button = new JButton("Select all");
156 button.addActionListener(new ActionListener() {
158 public void actionPerformed(ActionEvent e) {
159 tableModel.selectAll();
162 panel.add(button, "split 2, growx 1, sizegroup selectbutton");
164 button = new JButton("Select none");
165 button.addActionListener(new ActionListener() {
167 public void actionPerformed(ActionEvent e) {
168 tableModel.selectNone();
171 panel.add(button, "growx 1, sizegroup selectbutton, wrap");
174 selectedCountLabel = new JLabel();
175 updateSelectedCount();
176 panel.add(selectedCountLabel);
178 this.add(panel, "grow 100, wrap");
182 // Field separator panel
183 panel = new JPanel(new MigLayout("fill"));
184 panel.setBorder(BorderFactory.createTitledBorder("Field separator"));
186 label = new JLabel("Field separator string:");
187 tip = "<html>The string used to separate the fields in the exported file.<br>" +
188 "Use ',' for a Comma Separated Values (CSV) file.";
189 label.setToolTipText(tip);
192 fieldSeparator = new JComboBox(new String[] { ",", ";", SPACE, TAB });
193 fieldSeparator.setEditable(true);
194 fieldSeparator.setSelectedItem(Prefs.getString(Prefs.EXPORT_FIELD_SEPARATOR,
196 fieldSeparator.setToolTipText(tip);
197 panel.add(fieldSeparator);
199 this.add(panel, "spany, split, growx 1");
204 // Comments separator panel
205 panel = new JPanel(new MigLayout("fill"));
206 panel.setBorder(BorderFactory.createTitledBorder("Comments"));
208 simulationComments = new JCheckBox("Include simulation description");
209 simulationComments.setToolTipText("Include a comment at the beginning of the file " +
210 "describing the simulation.");
211 simulationComments.setSelected(Prefs.getBoolean(Prefs.EXPORT_SIMULATION_COMMENT,
213 panel.add(simulationComments, "wrap");
216 fieldNameComments = new JCheckBox("Include field descriptions");
217 fieldNameComments.setToolTipText("Include a comment line with the descriptions of " +
218 "the exported variables.");
219 fieldNameComments.setSelected(Prefs.getBoolean(Prefs.EXPORT_FIELD_NAME_COMMENT, true));
220 panel.add(fieldNameComments, "wrap");
223 eventComments = new JCheckBox("Include flight events");
224 eventComments.setToolTipText("Include a comment line for every flight event.");
225 eventComments.setSelected(Prefs.getBoolean(Prefs.EXPORT_EVENT_COMMENTS, true));
226 panel.add(eventComments, "wrap");
229 label = new JLabel("Comment character:");
230 tip = "The character(s) that mark a comment line.";
231 label.setToolTipText(tip);
232 panel.add(label, "split 2");
234 commentCharacter = new JComboBox(new String[] { "#", "%", ";" });
235 commentCharacter.setEditable(true);
236 commentCharacter.setSelectedItem(Prefs.getString(Prefs.EXPORT_COMMENT_CHARACTER, "#"));
237 commentCharacter.setToolTipText(tip);
238 panel.add(commentCharacter);
240 this.add(panel, "growx 1");
243 // Space-filling panel
244 panel = new JPanel();
245 this.add(panel, "width 1, height 1, grow 1");
249 button = new JButton("Export to file...");
250 button.addActionListener(new ActionListener() {
252 public void actionPerformed(ActionEvent e) {
256 this.add(button, "gapbottom para, gapright para, right");
261 private void doExport() {
262 JFileChooser chooser = new JFileChooser();
263 chooser.setFileFilter(CSV_FILE_FILTER);
264 chooser.setCurrentDirectory(Prefs.getDefaultDirectory());
266 if (chooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION)
269 File file = chooser.getSelectedFile();
273 if (file.getName().indexOf('.') < 0) {
274 String name = file.getAbsolutePath();
275 name = name + ".csv";
276 file = new File(name);
280 int ret = JOptionPane.showConfirmDialog(this,
281 "File \"" + file.getName() + "\" exists. Overwrite?",
282 "File exists", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
283 if (ret != JOptionPane.YES_OPTION)
287 String commentChar = commentCharacter.getSelectedItem().toString();
288 String fieldSep = fieldSeparator.getSelectedItem().toString();
289 boolean simulationComment = simulationComments.isSelected();
290 boolean fieldComment = fieldNameComments.isSelected();
291 boolean eventComment = eventComments.isSelected();
293 // Store preferences and export
295 Prefs.setDefaultDirectory(chooser.getCurrentDirectory());
296 for (int i=0; i < selected.length; i++) {
297 Prefs.setExportSelected(types[i], selected[i]);
301 Prefs.putString(Prefs.EXPORT_FIELD_SEPARATOR, fieldSep);
302 Prefs.putString(Prefs.EXPORT_COMMENT_CHARACTER, commentChar);
303 Prefs.putBoolean(Prefs.EXPORT_EVENT_COMMENTS, eventComment);
304 Prefs.putBoolean(Prefs.EXPORT_FIELD_NAME_COMMENT, fieldComment);
305 Prefs.putBoolean(Prefs.EXPORT_SIMULATION_COMMENT, simulationComment);
308 FlightDataType[] fieldTypes = new FlightDataType[n];
309 Unit[] fieldUnits = new Unit[n];
311 for (int i=0; i < selected.length; i++) {
313 fieldTypes[pos] = types[i];
314 fieldUnits[pos] = units[i];
319 if (fieldSep.equals(SPACE)) {
321 } else if (fieldSep.equals(TAB)) {
326 SaveCSVWorker.export(file, simulation, branch, fieldTypes, fieldUnits, fieldSep,
327 commentChar, simulationComment, fieldComment, eventComment,
328 SwingUtilities.getWindowAncestor(this));
332 private void updateSelectedCount() {
333 int total = selected.length;
337 for (int i=0; i < selected.length; i++) {
343 str = "Exporting 1 variable out of " + total + ".";
345 str = "Exporting "+n+" variables out of " + total + ".";
348 selectedCountLabel.setText(str);
354 * A table cell renderer that uses another renderer and sets the background and
355 * foreground of the returned component based on the selection of the variable.
357 private class SelectionBackgroundCellRenderer implements TableCellRenderer {
359 private final TableCellRenderer renderer;
361 public SelectionBackgroundCellRenderer(TableCellRenderer renderer) {
362 this.renderer = renderer;
366 public Component getTableCellRendererComponent(JTable table, Object value,
367 boolean isSelected, boolean hasFocus, int row, int column) {
369 Component component = renderer.getTableCellRendererComponent(table,
370 value, isSelected, hasFocus, row, column);
373 component.setBackground(table.getSelectionBackground());
374 component.setForeground(table.getSelectionForeground());
376 component.setBackground(table.getBackground());
377 component.setForeground(table.getForeground());
387 * The table model for the variable selection.
389 private class SelectionTableModel extends AbstractTableModel {
390 private static final int SELECTED = 0;
391 private static final int NAME = 1;
392 private static final int UNIT = 2;
395 public int getColumnCount() {
400 public int getRowCount() {
405 public String getColumnName(int column) {
414 throw new IndexOutOfBoundsException("column=" + column);
420 public Class<?> getColumnClass(int column) {
423 return Boolean.class;
425 return FlightDataType.class;
429 throw new IndexOutOfBoundsException("column=" + column);
434 public Object getValueAt(int row, int column) {
438 return selected[row];
447 throw new IndexOutOfBoundsException("column="+column);
453 public void setValueAt(Object value, int row, int column) {
457 selected[row] = (Boolean)value;
458 this.fireTableRowsUpdated(row, row);
459 updateSelectedCount();
466 units[row] = (Unit)value;
470 throw new IndexOutOfBoundsException("column="+column);
476 public boolean isCellEditable(int row, int column) {
485 return types[row].getUnitGroup().getUnitCount() > 1;
488 throw new IndexOutOfBoundsException("column="+column);
492 public void selectAll() {
493 Arrays.fill(selected, true);
494 updateSelectedCount();
495 this.fireTableDataChanged();
498 public void selectNone() {
499 Arrays.fill(selected, false);
500 updateSelectedCount();
501 this.fireTableDataChanged();