1 package tim.prune.save;
3 import java.awt.BorderLayout;
4 import java.awt.CardLayout;
5 import java.awt.Component;
6 import java.awt.Dimension;
7 import java.awt.FlowLayout;
8 import java.awt.GridLayout;
9 import java.awt.event.ActionEvent;
10 import java.awt.event.ActionListener;
12 import java.io.FileWriter;
13 import java.io.IOException;
15 import javax.swing.BorderFactory;
16 import javax.swing.Box;
17 import javax.swing.BoxLayout;
18 import javax.swing.ButtonGroup;
19 import javax.swing.JButton;
20 import javax.swing.JDialog;
21 import javax.swing.JFileChooser;
22 import javax.swing.JFrame;
23 import javax.swing.JLabel;
24 import javax.swing.JOptionPane;
25 import javax.swing.JPanel;
26 import javax.swing.JRadioButton;
27 import javax.swing.JTable;
28 import javax.swing.JTextField;
29 import javax.swing.ListSelectionModel;
32 import tim.prune.I18nManager;
33 import tim.prune.data.Altitude;
34 import tim.prune.data.Coordinate;
35 import tim.prune.data.DataPoint;
36 import tim.prune.data.Field;
37 import tim.prune.data.FieldList;
38 import tim.prune.data.Track;
39 import tim.prune.load.OneCharDocument;
42 * Class to manage the saving of track data
43 * into a user-specified file
45 public class FileSaver
47 private App _app = null;
48 private JFrame _parentFrame = null;
49 private Track _track = null;
50 private JDialog _dialog = null;
51 private JFileChooser _fileChooser = null;
52 private JPanel _cards = null;
53 private JButton _nextButton = null, _backButton = null;
54 private JTable _table = null;
55 private FieldSelectionTableModel _model = null;
56 private JButton _moveUpButton = null, _moveDownButton = null;
57 private JRadioButton[] _delimiterRadios = null;
58 private JTextField _otherDelimiterText = null;
59 private JRadioButton[] _coordUnitsRadios = null;
60 private JRadioButton[] _altitudeUnitsRadios = null;
61 private static final int[] FORMAT_COORDS = {Coordinate.FORMAT_NONE, Coordinate.FORMAT_DEG_MIN_SEC,
62 Coordinate.FORMAT_DEG_MIN, Coordinate.FORMAT_DEG};
63 private static final int[] FORMAT_ALTS = {Altitude.FORMAT_NONE, Altitude.FORMAT_METRES, Altitude.FORMAT_FEET};
68 * @param inApp application object to inform of success
69 * @param inParentFrame parent frame
70 * @param inTrack track object to save
72 public FileSaver(App inApp, JFrame inParentFrame, Track inTrack)
75 _parentFrame = inParentFrame;
81 * Show the save file dialog
82 * @param inDefaultDelimiter default delimiter to use
84 public void showDialog(char inDefaultDelimiter)
86 _dialog = new JDialog(_parentFrame, I18nManager.getText("dialog.saveoptions.title"), true);
87 _dialog.setLocationRelativeTo(_parentFrame);
89 FieldList fieldList = _track.getFieldList();
90 int numFields = fieldList.getNumFields();
91 _model = new FieldSelectionTableModel(numFields);
92 for (int i=0; i<numFields; i++)
94 Field field = fieldList.getField(i);
95 FieldInfo info = new FieldInfo(field, _track.hasData(field));
96 _model.addFieldInfo(info, i);
98 _dialog.getContentPane().add(makeDialogComponents(_model, inDefaultDelimiter));
105 * Make the dialog components
106 * @param inTableModel table model for fields
107 * @param inDelimiter default delimiter character
108 * @return the GUI components for the save dialog
110 private Component makeDialogComponents(FieldSelectionTableModel inTableModel, char inDelimiter)
112 JPanel panel = new JPanel();
113 panel.setLayout(new BorderLayout());
114 _cards = new JPanel();
115 _cards.setLayout(new CardLayout());
116 panel.add(_cards, BorderLayout.CENTER);
118 // Make first card for field selection and delimiter
119 JPanel firstCard = new JPanel();
120 firstCard.setLayout(new BoxLayout(firstCard, BoxLayout.Y_AXIS));
121 JPanel tablePanel = new JPanel();
122 tablePanel.setLayout(new BorderLayout());
123 _table = new JTable(inTableModel);
124 _table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
125 tablePanel.add(_table.getTableHeader(), BorderLayout.NORTH);
126 tablePanel.add(_table, BorderLayout.CENTER);
128 // Make a panel to hold the table and up/down buttons
129 JPanel fieldsPanel = new JPanel();
130 fieldsPanel.setLayout(new BorderLayout());
131 fieldsPanel.add(tablePanel, BorderLayout.CENTER);
132 JPanel updownPanel = new JPanel();
133 updownPanel.setLayout(new BoxLayout(updownPanel, BoxLayout.Y_AXIS));
134 _moveUpButton = new JButton(I18nManager.getText("button.moveup"));
135 _moveUpButton.addActionListener(new ActionListener() {
136 public void actionPerformed(ActionEvent e)
138 int row = _table.getSelectedRow();
141 _model.swapItems(row, row - 1);
142 _table.setRowSelectionInterval(row - 1, row - 1);
146 _moveUpButton.setEnabled(false);
147 updownPanel.add(_moveUpButton);
148 _moveDownButton = new JButton(I18nManager.getText("button.movedown"));
149 _moveDownButton.addActionListener(new ActionListener() {
150 public void actionPerformed(ActionEvent e)
152 int row = _table.getSelectedRow();
153 if (row > -1 && row < (_model.getRowCount() - 1))
155 _model.swapItems(row, row + 1);
156 _table.setRowSelectionInterval(row + 1, row + 1);
160 _moveDownButton.setEnabled(false);
161 updownPanel.add(_moveDownButton);
162 fieldsPanel.add(updownPanel, BorderLayout.EAST);
163 // enable/disable buttons based on table row selection
164 _table.getSelectionModel().addListSelectionListener(
165 new UpDownToggler(_moveUpButton, _moveDownButton, inTableModel.getRowCount())
168 // Add fields panel and the delimiter panel to first card in pack
169 JLabel saveOptionsLabel = new JLabel(I18nManager.getText("dialog.save.fieldstosave"));
170 saveOptionsLabel.setAlignmentX(JLabel.LEFT_ALIGNMENT);
171 firstCard.add(saveOptionsLabel);
172 fieldsPanel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
173 firstCard.add(fieldsPanel);
174 firstCard.add(Box.createRigidArea(new Dimension(0,10)));
177 JLabel delimLabel = new JLabel(I18nManager.getText("dialog.delimiter.label"));
178 delimLabel.setAlignmentX(JLabel.LEFT_ALIGNMENT);
179 firstCard.add(delimLabel);
180 JPanel delimsPanel = new JPanel();
181 delimsPanel.setLayout(new GridLayout(0, 2));
182 delimsPanel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
184 _delimiterRadios = new JRadioButton[5];
185 _delimiterRadios[0] = new JRadioButton(I18nManager.getText("dialog.delimiter.comma"));
186 delimsPanel.add(_delimiterRadios[0]);
187 _delimiterRadios[1] = new JRadioButton(I18nManager.getText("dialog.delimiter.tab"));
188 delimsPanel.add(_delimiterRadios[1]);
189 _delimiterRadios[2] = new JRadioButton(I18nManager.getText("dialog.delimiter.semicolon"));
190 delimsPanel.add(_delimiterRadios[2]);
191 _delimiterRadios[3] = new JRadioButton(I18nManager.getText("dialog.delimiter.space"));
192 delimsPanel.add(_delimiterRadios[3]);
193 JPanel otherPanel = new JPanel();
194 otherPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
195 _delimiterRadios[4] = new JRadioButton(I18nManager.getText("dialog.delimiter.other"));
196 otherPanel.add(_delimiterRadios[4]);
197 _otherDelimiterText = new JTextField(new OneCharDocument(), null, 2);
198 otherPanel.add(_otherDelimiterText);
199 // Group radio buttons
200 ButtonGroup delimGroup = new ButtonGroup();
201 for (int i=0; i<_delimiterRadios.length; i++)
203 delimGroup.add(_delimiterRadios[i]);
205 // choose last-used delimiter as default
208 case ',' : _delimiterRadios[0].setSelected(true); break;
209 case '\t' : _delimiterRadios[1].setSelected(true); break;
210 case ';' : _delimiterRadios[2].setSelected(true); break;
211 case ' ' : _delimiterRadios[3].setSelected(true); break;
212 default : _delimiterRadios[4].setSelected(true);
213 _otherDelimiterText.setText("" + inDelimiter);
215 delimsPanel.add(otherPanel);
216 firstCard.add(delimsPanel);
217 _cards.add(firstCard, "card1");
219 JPanel secondCard = new JPanel();
220 secondCard.setLayout(new BorderLayout());
221 JPanel secondCardHolder = new JPanel();
222 secondCardHolder.setLayout(new BoxLayout(secondCardHolder, BoxLayout.Y_AXIS));
223 JLabel coordLabel = new JLabel(I18nManager.getText("dialog.save.coordinateunits"));
224 coordLabel.setAlignmentX(JLabel.LEFT_ALIGNMENT);
225 secondCardHolder.add(coordLabel);
226 JPanel coordsUnitsPanel = new JPanel();
227 coordsUnitsPanel.setBorder(BorderFactory.createEtchedBorder());
228 coordsUnitsPanel.setLayout(new GridLayout(0, 2));
229 _coordUnitsRadios = new JRadioButton[4];
230 _coordUnitsRadios[0] = new JRadioButton(I18nManager.getText("dialog.save.units.original"));
231 _coordUnitsRadios[1] = new JRadioButton(I18nManager.getText("units.degminsec"));
232 _coordUnitsRadios[2] = new JRadioButton(I18nManager.getText("units.degmin"));
233 _coordUnitsRadios[3] = new JRadioButton(I18nManager.getText("units.deg"));
234 ButtonGroup coordGroup = new ButtonGroup();
235 for (int i=0; i<4; i++)
237 coordGroup.add(_coordUnitsRadios[i]);
238 coordsUnitsPanel.add(_coordUnitsRadios[i]);
239 _coordUnitsRadios[i].setSelected(i==0);
241 coordsUnitsPanel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
242 secondCardHolder.add(coordsUnitsPanel);
243 secondCardHolder.add(Box.createRigidArea(new Dimension(0,10)));
244 JLabel altUnitsLabel = new JLabel(I18nManager.getText("dialog.save.altitudeunits"));
245 altUnitsLabel.setAlignmentX(JLabel.LEFT_ALIGNMENT);
246 secondCardHolder.add(altUnitsLabel);
247 JPanel altUnitsPanel = new JPanel();
248 altUnitsPanel.setBorder(BorderFactory.createEtchedBorder());
249 altUnitsPanel.setLayout(new GridLayout(0, 2));
250 _altitudeUnitsRadios = new JRadioButton[3];
251 _altitudeUnitsRadios[0] = new JRadioButton(I18nManager.getText("dialog.save.units.original"));
252 _altitudeUnitsRadios[1] = new JRadioButton(I18nManager.getText("units.metres"));
253 _altitudeUnitsRadios[2] = new JRadioButton(I18nManager.getText("units.feet"));
254 ButtonGroup altGroup = new ButtonGroup();
255 for (int i=0; i<3; i++)
257 altGroup.add(_altitudeUnitsRadios[i]);
258 altUnitsPanel.add(_altitudeUnitsRadios[i]);
259 _altitudeUnitsRadios[i].setSelected(i==0);
261 altUnitsPanel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
262 secondCardHolder.add(altUnitsPanel);
263 secondCard.add(secondCardHolder, BorderLayout.NORTH);
264 _cards.add(secondCard, "card2");
266 // Put together with ok/cancel buttons on the bottom
267 JPanel buttonPanel = new JPanel();
268 buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
269 _backButton = new JButton(I18nManager.getText("button.back"));
270 _backButton.addActionListener(new ActionListener() {
271 public void actionPerformed(ActionEvent e)
273 CardLayout cl = (CardLayout)(_cards.getLayout());
275 _backButton.setEnabled(false);
276 _nextButton.setEnabled(true);
279 _backButton.setEnabled(false);
280 buttonPanel.add(_backButton);
281 _nextButton = new JButton(I18nManager.getText("button.next"));
282 _nextButton.setEnabled(true);
283 _nextButton.addActionListener(new ActionListener() {
284 public void actionPerformed(ActionEvent e)
286 CardLayout cl = (CardLayout)(_cards.getLayout());
288 _backButton.setEnabled(true);
289 _nextButton.setEnabled(false);
292 buttonPanel.add(_nextButton);
293 JButton okButton = new JButton(I18nManager.getText("button.finish"));
294 okButton.addActionListener(new ActionListener() {
295 public void actionPerformed(ActionEvent e)
303 buttonPanel.add(okButton);
304 JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
305 cancelButton.addActionListener(new ActionListener() {
306 public void actionPerformed(ActionEvent e)
311 buttonPanel.add(cancelButton);
312 panel.add(buttonPanel, BorderLayout.SOUTH);
318 * Save the track to file with the chosen options
319 * @return true if successful or cancelled, false if failed
321 private boolean saveToFile()
323 boolean saveOK = true;
324 FileWriter writer = null;
325 if (_fileChooser == null)
326 _fileChooser = new JFileChooser();
327 _fileChooser.setDialogType(JFileChooser.SAVE_DIALOG);
328 if (_fileChooser.showSaveDialog(_parentFrame) == JFileChooser.APPROVE_OPTION)
330 File saveFile = _fileChooser.getSelectedFile();
331 String lineSeparator = System.getProperty("line.separator");
332 // Get coordinate format and altitude format
333 int coordFormat = Coordinate.FORMAT_NONE;
334 for (int i=0; i<_coordUnitsRadios.length; i++)
335 if (_coordUnitsRadios[i].isSelected())
336 coordFormat = FORMAT_COORDS[i];
337 int altitudeFormat = Altitude.FORMAT_NONE;
338 for (int i=0; i<_altitudeUnitsRadios.length; i++)
339 if (_altitudeUnitsRadios[i].isSelected())
340 altitudeFormat = FORMAT_ALTS[i];
342 // Check if file exists, don't overwrite any files for v1!
343 if (!saveFile.exists())
347 // Create output file
348 writer = new FileWriter(saveFile);
349 // Determine delimiter character to use
350 char delimiter = getDelimiter();
351 FieldInfo info = null;
353 StringBuffer buffer = null;
354 // For now, just spit out to console
355 int numPoints = _track.getNumPoints();
356 int numFields = _model.getRowCount();
357 for (int p=0; p<numPoints; p++)
359 DataPoint point = _track.getPoint(p);
360 boolean firstField = true;
361 buffer = new StringBuffer();
362 for (int f=0; f<numFields; f++)
364 info = _model.getFieldInfo(f);
365 if (info.isSelected())
369 // output field separator
370 buffer.append(delimiter);
372 field = info.getField();
373 // Output field according to type
374 if (field == Field.LATITUDE)
376 buffer.append(point.getLatitude().output(coordFormat));
378 else if (field == Field.LONGITUDE)
380 buffer.append(point.getLongitude().output(coordFormat));
382 else if (field == Field.ALTITUDE)
384 buffer.append(point.getAltitude().getValue(altitudeFormat));
386 else if (field == Field.TIMESTAMP)
388 buffer.append(point.getTimestamp().getText());
392 String value = point.getFieldValue(field);
395 buffer.append(value);
402 writer.write(buffer.toString());
403 writer.write(lineSeparator);
406 JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.save.ok1")
407 + " " + numPoints + " " + I18nManager.getText("dialog.save.ok2")
408 + saveFile.getAbsolutePath(),
409 I18nManager.getText("dialog.save.oktitle"), JOptionPane.INFORMATION_MESSAGE);
410 _app.informDataSaved();
412 catch (IOException ioe)
415 JOptionPane.showMessageDialog(_parentFrame,
416 I18nManager.getText("error.save.failed") + ioe.getMessage(),
417 I18nManager.getText("error.save.dialogtitle"),
418 JOptionPane.ERROR_MESSAGE);
422 // try to close file if it's open
430 catch (Exception e) {}
436 JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("error.save.fileexists"),
437 I18nManager.getText("error.save.dialogtitle"), JOptionPane.ERROR_MESSAGE);
445 * @return the selected delimiter character
447 private char getDelimiter()
449 // Check the preset 4 delimiters
450 final char[] delimiters = {',', '\t', ';', ' '};
451 for (int i=0; i<4; i++)
453 if (_delimiterRadios[i].isSelected())
455 return delimiters[i];
458 // Wasn't any of those so must be 'other'
459 return _otherDelimiterText.getText().charAt(0);