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.JCheckBox;
21 import javax.swing.JDialog;
22 import javax.swing.JFileChooser;
23 import javax.swing.JFrame;
24 import javax.swing.JLabel;
25 import javax.swing.JOptionPane;
26 import javax.swing.JPanel;
27 import javax.swing.JRadioButton;
28 import javax.swing.JScrollPane;
29 import javax.swing.JTable;
30 import javax.swing.JTextField;
31 import javax.swing.ListSelectionModel;
32 import javax.swing.table.TableModel;
35 import tim.prune.I18nManager;
36 import tim.prune.UpdateMessageBroker;
37 import tim.prune.config.Config;
38 import tim.prune.data.Coordinate;
39 import tim.prune.data.DataPoint;
40 import tim.prune.data.Field;
41 import tim.prune.data.FieldList;
42 import tim.prune.data.RecentFile;
43 import tim.prune.data.Timestamp;
44 import tim.prune.data.Track;
45 import tim.prune.data.Unit;
46 import tim.prune.data.UnitSetLibrary;
47 import tim.prune.load.GenericFileFilter;
48 import tim.prune.load.OneCharDocument;
51 * Class to manage the saving of track data
52 * as text into a user-specified file
54 public class FileSaver
56 private App _app = null;
57 private JFrame _parentFrame = null;
58 private JDialog _dialog = null;
59 private JFileChooser _fileChooser = null;
60 private JPanel _cards = null;
61 private JButton _nextButton = null, _backButton = null;
62 private JTable _table = null;
63 private FieldSelectionTableModel _model = null;
64 private JButton _moveUpButton = null, _moveDownButton = null;
65 private UpDownToggler _toggler = null;
66 private JRadioButton[] _delimiterRadios = null;
67 private JTextField _otherDelimiterText = null;
68 private JCheckBox _headerRowCheckbox = null;
69 private PointTypeSelector _pointTypeSelector = null;
70 private JRadioButton[] _coordUnitsRadios = null;
71 private JRadioButton[] _altitudeUnitsRadios = null;
72 private JRadioButton[] _timestampUnitsRadios = null;
74 private static final int[] FORMAT_COORDS = {Coordinate.FORMAT_NONE, Coordinate.FORMAT_DEG_MIN_SEC,
75 Coordinate.FORMAT_DEG_MIN, Coordinate.FORMAT_DEG};
76 private static final Unit[] UNIT_ALTS = {null, UnitSetLibrary.UNITS_METRES, UnitSetLibrary.UNITS_FEET};
77 private static final Timestamp.Format[] FORMAT_TIMES = {Timestamp.Format.ORIGINAL, Timestamp.Format.LOCALE, Timestamp.Format.ISO8601};
82 * @param inApp application object to inform of success
83 * @param inParentFrame parent frame
85 public FileSaver(App inApp, JFrame inParentFrame)
88 _parentFrame = inParentFrame;
93 * Show the save file dialog
94 * @param inDefaultDelimiter default delimiter to use
96 public void showDialog(char inDefaultDelimiter)
100 _dialog = new JDialog(_parentFrame, I18nManager.getText("dialog.saveoptions.title"), true);
101 _dialog.setLocationRelativeTo(_parentFrame);
102 _dialog.getContentPane().add(makeDialogComponents());
105 // Has the track got media?
106 final boolean hasMedia = _app.getTrackInfo().getPhotoList().hasCorrelatedPhotos()
107 || _app.getTrackInfo().getAudioList().hasCorrelatedAudios();
109 Track track = _app.getTrackInfo().getTrack();
110 FieldList fieldList = track.getFieldList();
111 int numFields = fieldList.getNumFields();
112 _model = new FieldSelectionTableModel(numFields + (hasMedia ? 1 : 0));
113 for (int i=0; i<numFields; i++)
115 Field field = fieldList.getField(i);
116 FieldInfo info = new FieldInfo(field, track.hasData(field));
117 _model.addFieldInfo(info, i);
119 // Add a field for photos / audio if any present
122 _model.addFieldInfo(new FieldInfo(Field.MEDIA_FILENAME, true), numFields);
124 // Initialise dialog and show it
125 initDialog(_model, inDefaultDelimiter);
126 _dialog.setVisible(true);
131 * Make the dialog components
132 * @return the GUI components for the save dialog
134 private Component makeDialogComponents()
136 JPanel panel = new JPanel();
137 panel.setLayout(new BorderLayout());
138 _cards = new JPanel();
139 _cards.setLayout(new CardLayout());
140 panel.add(_cards, BorderLayout.CENTER);
142 // Make first card for field selection and delimiter
143 JPanel firstCard = new JPanel();
144 firstCard.setLayout(new BoxLayout(firstCard, BoxLayout.Y_AXIS));
145 JPanel tablePanel = new JPanel();
146 tablePanel.setLayout(new BorderLayout());
147 _table = new JTable();
148 _table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
149 // Enclose table in a scrollpane to prevent other components getting lost
150 JScrollPane scrollPane = new JScrollPane(_table);
151 _table.setPreferredScrollableViewportSize(new Dimension(300, 150));
152 tablePanel.add(scrollPane, BorderLayout.CENTER);
154 // Make a panel to hold the table and up/down buttons
155 JPanel fieldsPanel = new JPanel();
156 fieldsPanel.setLayout(new BorderLayout());
157 fieldsPanel.add(tablePanel, BorderLayout.CENTER);
158 JPanel updownPanel = new JPanel();
159 updownPanel.setLayout(new BoxLayout(updownPanel, BoxLayout.Y_AXIS));
160 _moveUpButton = new JButton(I18nManager.getText("button.moveup"));
161 _moveUpButton.addActionListener(new ActionListener() {
162 public void actionPerformed(ActionEvent e)
164 int row = _table.getSelectedRow();
167 _model.swapItems(row, row - 1);
168 _table.setRowSelectionInterval(row - 1, row - 1);
172 _moveUpButton.setEnabled(false);
173 updownPanel.add(_moveUpButton);
174 _moveDownButton = new JButton(I18nManager.getText("button.movedown"));
175 _moveDownButton.addActionListener(new ActionListener() {
176 public void actionPerformed(ActionEvent e)
178 int row = _table.getSelectedRow();
179 if (row > -1 && row < (_model.getRowCount() - 1))
181 _model.swapItems(row, row + 1);
182 _table.setRowSelectionInterval(row + 1, row + 1);
186 _moveDownButton.setEnabled(false);
187 updownPanel.add(_moveDownButton);
188 fieldsPanel.add(updownPanel, BorderLayout.EAST);
189 // enable/disable buttons based on table row selection
190 _toggler = new UpDownToggler(_moveUpButton, _moveDownButton);
191 _table.getSelectionModel().addListSelectionListener(_toggler);
193 // Add fields panel and the delimiter panel to first card in pack
194 JLabel saveOptionsLabel = new JLabel(I18nManager.getText("dialog.save.fieldstosave"));
195 saveOptionsLabel.setAlignmentX(JLabel.LEFT_ALIGNMENT);
196 firstCard.add(saveOptionsLabel);
197 fieldsPanel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
198 firstCard.add(fieldsPanel);
199 firstCard.add(Box.createRigidArea(new Dimension(0,10)));
202 JLabel delimLabel = new JLabel(I18nManager.getText("dialog.delimiter.label"));
203 delimLabel.setAlignmentX(JLabel.LEFT_ALIGNMENT);
204 firstCard.add(delimLabel);
205 JPanel delimsPanel = new JPanel();
206 delimsPanel.setLayout(new GridLayout(0, 2));
207 delimsPanel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
209 _delimiterRadios = new JRadioButton[5];
210 _delimiterRadios[0] = new JRadioButton(I18nManager.getText("dialog.delimiter.comma"));
211 delimsPanel.add(_delimiterRadios[0]);
212 _delimiterRadios[1] = new JRadioButton(I18nManager.getText("dialog.delimiter.tab"));
213 delimsPanel.add(_delimiterRadios[1]);
214 _delimiterRadios[2] = new JRadioButton(I18nManager.getText("dialog.delimiter.semicolon"));
215 delimsPanel.add(_delimiterRadios[2]);
216 _delimiterRadios[3] = new JRadioButton(I18nManager.getText("dialog.delimiter.space"));
217 delimsPanel.add(_delimiterRadios[3]);
218 JPanel otherPanel = new JPanel();
219 otherPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
220 _delimiterRadios[4] = new JRadioButton(I18nManager.getText("dialog.delimiter.other"));
221 otherPanel.add(_delimiterRadios[4]);
222 _otherDelimiterText = new JTextField(new OneCharDocument(), null, 2);
223 otherPanel.add(_otherDelimiterText);
224 // Group radio buttons
225 ButtonGroup delimGroup = new ButtonGroup();
226 for (int i=0; i<_delimiterRadios.length; i++)
228 delimGroup.add(_delimiterRadios[i]);
230 delimsPanel.add(otherPanel);
231 firstCard.add(delimsPanel);
234 firstCard.add(Box.createRigidArea(new Dimension(0,10)));
235 _headerRowCheckbox = new JCheckBox(I18nManager.getText("dialog.save.headerrow"), true);
236 firstCard.add(_headerRowCheckbox);
237 _cards.add(firstCard, "card1");
240 JPanel secondCard = new JPanel();
241 secondCard.setLayout(new BorderLayout());
242 JPanel secondCardHolder = new JPanel();
243 secondCardHolder.setLayout(new BoxLayout(secondCardHolder, BoxLayout.Y_AXIS));
244 // point type selector
245 secondCardHolder.add(Box.createRigidArea(new Dimension(0,10)));
246 _pointTypeSelector = new PointTypeSelector();
247 _pointTypeSelector.setAlignmentX(JLabel.LEFT_ALIGNMENT);
248 secondCardHolder.add(_pointTypeSelector);
249 JLabel coordLabel = new JLabel(I18nManager.getText("dialog.save.coordinateunits"));
250 coordLabel.setAlignmentX(JLabel.LEFT_ALIGNMENT);
251 secondCardHolder.add(coordLabel);
252 JPanel coordsUnitsPanel = new JPanel();
253 coordsUnitsPanel.setBorder(BorderFactory.createEtchedBorder());
254 coordsUnitsPanel.setLayout(new GridLayout(0, 2));
255 _coordUnitsRadios = new JRadioButton[4];
256 _coordUnitsRadios[0] = new JRadioButton(I18nManager.getText("units.original"));
257 _coordUnitsRadios[1] = new JRadioButton(I18nManager.getText("units.degminsec"));
258 _coordUnitsRadios[2] = new JRadioButton(I18nManager.getText("units.degmin"));
259 _coordUnitsRadios[3] = new JRadioButton(I18nManager.getText("units.deg"));
260 ButtonGroup coordGroup = new ButtonGroup();
261 for (int i=0; i<4; i++)
263 coordGroup.add(_coordUnitsRadios[i]);
264 coordsUnitsPanel.add(_coordUnitsRadios[i]);
265 _coordUnitsRadios[i].setSelected(i==0);
267 coordsUnitsPanel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
268 secondCardHolder.add(coordsUnitsPanel);
269 secondCardHolder.add(Box.createRigidArea(new Dimension(0,7)));
271 JLabel altUnitsLabel = new JLabel(I18nManager.getText("dialog.save.altitudeunits"));
272 altUnitsLabel.setAlignmentX(JLabel.LEFT_ALIGNMENT);
273 secondCardHolder.add(altUnitsLabel);
274 JPanel altUnitsPanel = new JPanel();
275 altUnitsPanel.setBorder(BorderFactory.createEtchedBorder());
276 altUnitsPanel.setLayout(new GridLayout(0, 2));
277 _altitudeUnitsRadios = new JRadioButton[3];
278 _altitudeUnitsRadios[0] = new JRadioButton(I18nManager.getText("units.original"));
279 _altitudeUnitsRadios[1] = new JRadioButton(I18nManager.getText("units.metres"));
280 _altitudeUnitsRadios[2] = new JRadioButton(I18nManager.getText("units.feet"));
281 ButtonGroup altGroup = new ButtonGroup();
282 for (int i=0; i<3; i++)
284 altGroup.add(_altitudeUnitsRadios[i]);
285 altUnitsPanel.add(_altitudeUnitsRadios[i]);
286 _altitudeUnitsRadios[i].setSelected(i==0);
288 altUnitsPanel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
289 secondCardHolder.add(altUnitsPanel);
290 secondCardHolder.add(Box.createRigidArea(new Dimension(0,7)));
291 // Selection of format of timestamps
292 JLabel timestampLabel = new JLabel(I18nManager.getText("dialog.save.timestampformat"));
293 timestampLabel.setAlignmentX(JLabel.LEFT_ALIGNMENT);
294 secondCardHolder.add(timestampLabel);
295 JPanel timestampPanel = new JPanel();
296 timestampPanel.setBorder(BorderFactory.createEtchedBorder());
297 timestampPanel.setLayout(new GridLayout(0, 2));
298 _timestampUnitsRadios = new JRadioButton[3];
299 _timestampUnitsRadios[0] = new JRadioButton(I18nManager.getText("units.original"));
300 _timestampUnitsRadios[1] = new JRadioButton(I18nManager.getText("units.default"));
301 _timestampUnitsRadios[2] = new JRadioButton(I18nManager.getText("units.iso8601"));
302 ButtonGroup timeGroup = new ButtonGroup();
303 for (int i=0; i<3; i++)
305 timeGroup.add(_timestampUnitsRadios[i]);
306 timestampPanel.add(_timestampUnitsRadios[i]);
307 _timestampUnitsRadios[i].setSelected(i==0);
309 timestampPanel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
310 secondCardHolder.add(timestampPanel);
311 secondCard.add(secondCardHolder, BorderLayout.NORTH);
312 _cards.add(secondCard, "card2");
314 // Put together with ok/cancel buttons on the bottom
315 JPanel buttonPanel = new JPanel();
316 buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
317 _backButton = new JButton(I18nManager.getText("button.back"));
318 _backButton.addActionListener(new ActionListener() {
319 public void actionPerformed(ActionEvent e)
321 CardLayout cl = (CardLayout) _cards.getLayout();
323 _backButton.setEnabled(false);
324 _nextButton.setEnabled(true);
327 _backButton.setEnabled(false);
328 buttonPanel.add(_backButton);
329 _nextButton = new JButton(I18nManager.getText("button.next"));
330 _nextButton.setEnabled(true);
331 _nextButton.addActionListener(new ActionListener() {
332 public void actionPerformed(ActionEvent e)
334 CardLayout cl = (CardLayout) _cards.getLayout();
336 _backButton.setEnabled(true);
337 _nextButton.setEnabled(false);
340 buttonPanel.add(_nextButton);
341 JButton okButton = new JButton(I18nManager.getText("button.finish"));
342 okButton.addActionListener(new ActionListener() {
343 public void actionPerformed(ActionEvent e)
351 buttonPanel.add(okButton);
352 JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
353 cancelButton.addActionListener(new ActionListener() {
354 public void actionPerformed(ActionEvent e)
359 buttonPanel.add(cancelButton);
360 panel.add(buttonPanel, BorderLayout.SOUTH);
365 * Initialize the dialog with the given details
366 * @param inModel table model
367 * @param inDefaultDelimiter default delimiter character
369 private void initDialog(TableModel inModel, char inDefaultDelimiter)
372 _table.setModel(inModel);
374 _toggler.setListSize(inModel.getRowCount());
375 // choose last-used delimiter as default
376 switch (inDefaultDelimiter)
378 case ',' : _delimiterRadios[0].setSelected(true); break;
379 case '\t' : _delimiterRadios[1].setSelected(true); break;
380 case ';' : _delimiterRadios[2].setSelected(true); break;
381 case ' ' : _delimiterRadios[3].setSelected(true); break;
382 default : _delimiterRadios[4].setSelected(true);
383 _otherDelimiterText.setText("" + inDefaultDelimiter);
385 _pointTypeSelector.init(_app.getTrackInfo());
386 // set card and enable buttons
387 CardLayout cl = (CardLayout) _cards.getLayout();
389 _nextButton.setEnabled(true);
390 _backButton.setEnabled(false);
395 * Start the save process by choosing the file to save to
396 * @return true if successful or cancelled, false if failed
398 private boolean saveToFile()
400 if (!_pointTypeSelector.getAnythingSelected()) {
401 JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.save.notypesselected"),
402 I18nManager.getText("dialog.saveoptions.title"), JOptionPane.WARNING_MESSAGE);
405 if (_fileChooser == null)
407 _fileChooser = new JFileChooser();
408 _fileChooser.setDialogType(JFileChooser.SAVE_DIALOG);
409 _fileChooser.addChoosableFileFilter(new GenericFileFilter("filetype.txt", new String[] {"txt", "text"}));
410 _fileChooser.setAcceptAllFileFilterUsed(true);
411 // start from directory in config which should be set
412 String configDir = Config.getConfigString(Config.KEY_TRACK_DIR);
413 if (configDir == null) {configDir = Config.getConfigString(Config.KEY_PHOTO_DIR);}
414 if (configDir != null) {_fileChooser.setCurrentDirectory(new File(configDir));}
416 if (_fileChooser.showSaveDialog(_parentFrame) == JFileChooser.APPROVE_OPTION)
418 return saveToFile(_fileChooser.getSelectedFile());
420 return true; // cancelled
425 * Save the track to the specified file using the chosen options
426 * @param inSaveFile file to save to
427 * @return true if save successful, false if failed
429 private boolean saveToFile(File inSaveFile)
431 // TODO: Shorten method
432 FileWriter writer = null;
433 final String lineSeparator = System.getProperty("line.separator");
434 boolean saveOK = true;
435 // Get coordinate format and altitude format
436 int coordFormat = Coordinate.FORMAT_NONE;
437 for (int i=0; i<_coordUnitsRadios.length; i++)
438 if (_coordUnitsRadios[i].isSelected())
439 coordFormat = FORMAT_COORDS[i];
440 Unit altitudeUnit = null;
441 for (int i=0; i<_altitudeUnitsRadios.length; i++)
443 if (_altitudeUnitsRadios[i].isSelected()) {
444 altitudeUnit = UNIT_ALTS[i];
447 // Get timestamp format
448 Timestamp.Format timestampFormat = Timestamp.Format.ORIGINAL;
449 for (int i=0; i<_timestampUnitsRadios.length; i++)
451 if (_timestampUnitsRadios[i].isSelected()) {
452 timestampFormat = FORMAT_TIMES[i];
456 // Correct chosen filename if necessary
457 final File saveFile = (isFilenameOk(inSaveFile)?inSaveFile:new File(inSaveFile.getAbsolutePath() + ".txt"));
459 // Check if file exists, and confirm overwrite if necessary
460 Object[] buttonTexts = {I18nManager.getText("button.overwrite"), I18nManager.getText("button.cancel")};
461 if (!saveFile.exists() || JOptionPane.showOptionDialog(_parentFrame,
462 I18nManager.getText("dialog.save.overwrite.text"),
463 I18nManager.getText("dialog.save.overwrite.title"), JOptionPane.YES_NO_OPTION,
464 JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1])
465 == JOptionPane.YES_OPTION)
469 // Create output file
470 writer = new FileWriter(saveFile);
471 // Determine delimiter character to use
472 final char delimiter = getDelimiter();
473 FieldInfo info = null;
475 StringBuffer buffer = null;
476 int numFields = _model.getRowCount();
477 boolean firstField = true;
478 // Write header row if required
479 if (_headerRowCheckbox.isSelected())
481 buffer = new StringBuffer();
482 for (int f=0; f<numFields; f++)
484 info = _model.getFieldInfo(f);
485 if (info.isSelected())
487 // output field separator
489 buffer.append(delimiter);
491 buffer.append(info.getField().getName());
495 writer.write(buffer.toString());
496 writer.write(lineSeparator);
500 int selStart = -1, selEnd = -1;
501 if (_pointTypeSelector.getJustSelection()) {
502 selStart = _app.getTrackInfo().getSelection().getStart();
503 selEnd = _app.getTrackInfo().getSelection().getEnd();
505 // Loop over points outputting each in turn to buffer
506 Track track = _app.getTrackInfo().getTrack();
507 final int numPoints = track.getNumPoints();
509 for (int p=0; p<numPoints; p++)
511 DataPoint point = track.getPoint(p);
512 boolean savePoint = ((point.isWaypoint() && _pointTypeSelector.getWaypointsSelected())
513 || (!point.isWaypoint() && !point.hasMedia() && _pointTypeSelector.getTrackpointsSelected())
514 || (!point.isWaypoint() && point.getPhoto()!=null && _pointTypeSelector.getPhotopointsSelected())
515 || (!point.isWaypoint() && point.getAudio()!=null && _pointTypeSelector.getAudiopointsSelected()))
516 && (!_pointTypeSelector.getJustSelection() || (p>=selStart && p<=selEnd));
517 if (!savePoint) {continue;}
520 buffer = new StringBuffer();
521 for (int f=0; f<numFields; f++)
523 info = _model.getFieldInfo(f);
524 if (info.isSelected())
526 // output field separator
528 buffer.append(delimiter);
530 saveField(buffer, point, info.getField(), coordFormat, altitudeUnit, timestampFormat);
535 writer.write(buffer.toString());
536 writer.write(lineSeparator);
538 // Store directory in config for later
539 Config.setConfigString(Config.KEY_TRACK_DIR, saveFile.getParentFile().getAbsolutePath());
540 // Add to recent file list
541 Config.getRecentFileList().addFile(new RecentFile(inSaveFile, true));
543 UpdateMessageBroker.informSubscribers();
544 UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.save.ok1")
545 + " " + numSaved + " " + I18nManager.getText("confirm.save.ok2")
546 + " " + saveFile.getAbsolutePath());
547 _app.informDataSaved();
549 catch (IOException ioe)
552 _app.showErrorMessageNoLookup("error.save.dialogtitle",
553 I18nManager.getText("error.save.failed") + " : " + ioe.getMessage());
557 // try to close file if it's open
561 catch (Exception e) {}
566 // Overwrite file confirm cancelled
574 * Format the given field and append to the given buffer for saving
575 * @param inBuffer buffer to append to
576 * @param inPoint point object
577 * @param inField field object
578 * @param inCoordFormat coordinate format
579 * @param inAltitudeUnit altitude unit
580 * @param inTimestampFormat timestamp format
582 private void saveField(StringBuffer inBuffer, DataPoint inPoint, Field inField,
583 int inCoordFormat, Unit inAltitudeUnit, Timestamp.Format inTimestampFormat)
585 // Output field according to type
586 if (inField == Field.LATITUDE)
588 inBuffer.append(inPoint.getLatitude().output(inCoordFormat));
590 else if (inField == Field.LONGITUDE)
592 inBuffer.append(inPoint.getLongitude().output(inCoordFormat));
594 else if (inField == Field.ALTITUDE)
598 inBuffer.append(inPoint.getAltitude().getStringValue(inAltitudeUnit));
600 catch (NullPointerException npe) {}
602 else if (inField == Field.TIMESTAMP)
604 if (inPoint.hasTimestamp())
606 // format value accordingly
607 inBuffer.append(inPoint.getTimestamp().getText(inTimestampFormat, null));
610 else if (inField == Field.MEDIA_FILENAME)
612 if (inPoint.hasMedia())
614 inBuffer.append(inPoint.getMediaName());
619 String value = inPoint.getFieldValue(inField);
622 inBuffer.append(value);
629 * @return the selected delimiter character
631 private char getDelimiter()
633 // Check the preset 4 delimiters
634 final char[] delimiters = {',', '\t', ';', ' '};
635 for (int i=0; i<4; i++)
637 if (_delimiterRadios[i].isSelected())
639 return delimiters[i];
642 // Wasn't any of those so must be 'other'
643 return _otherDelimiterText.getText().charAt(0);
648 * Check the selected filename to see if it is acceptable
649 * @param inFile chosen file to save
650 * @return true if filename is ok
652 private static boolean isFilenameOk(File inFile)
654 String filename = inFile.getName().toLowerCase();
655 return (filename.length() <4 || (!filename.endsWith(".gpx")
656 && !filename.endsWith(".kml") && !filename.endsWith(".kmz") && !filename.endsWith(".zip")));