]> gitweb.fperrin.net Git - GpsPrune.git/blobdiff - tim/prune/save/FileSaver.java
Version 14, October 2012
[GpsPrune.git] / tim / prune / save / FileSaver.java
index b1ea368c03678f1b535a49ad146acabee97e9be3..6be44eb80bc3eb6beb828f0270db15c75255954e 100644 (file)
@@ -17,6 +17,7 @@ import javax.swing.Box;
 import javax.swing.BoxLayout;
 import javax.swing.ButtonGroup;
 import javax.swing.JButton;
+import javax.swing.JCheckBox;
 import javax.swing.JDialog;
 import javax.swing.JFileChooser;
 import javax.swing.JFrame;
@@ -24,29 +25,35 @@ import javax.swing.JLabel;
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JRadioButton;
+import javax.swing.JScrollPane;
 import javax.swing.JTable;
 import javax.swing.JTextField;
 import javax.swing.ListSelectionModel;
+import javax.swing.table.TableModel;
 
 import tim.prune.App;
 import tim.prune.I18nManager;
+import tim.prune.UpdateMessageBroker;
+import tim.prune.config.Config;
 import tim.prune.data.Altitude;
 import tim.prune.data.Coordinate;
 import tim.prune.data.DataPoint;
 import tim.prune.data.Field;
 import tim.prune.data.FieldList;
+import tim.prune.data.RecentFile;
+import tim.prune.data.Timestamp;
 import tim.prune.data.Track;
+import tim.prune.load.GenericFileFilter;
 import tim.prune.load.OneCharDocument;
 
 /**
  * Class to manage the saving of track data
- * into a user-specified file
+ * as text into a user-specified file
  */
 public class FileSaver
 {
        private App _app = null;
        private JFrame _parentFrame = null;
-       private Track _track = null;
        private JDialog _dialog = null;
        private JFileChooser _fileChooser = null;
        private JPanel _cards = null;
@@ -54,26 +61,30 @@ public class FileSaver
        private JTable _table = null;
        private FieldSelectionTableModel _model = null;
        private JButton _moveUpButton = null, _moveDownButton = null;
+       private UpDownToggler _toggler = null;
        private JRadioButton[] _delimiterRadios = null;
        private JTextField _otherDelimiterText = null;
+       private JCheckBox _headerRowCheckbox = null;
+       private PointTypeSelector _pointTypeSelector = null;
        private JRadioButton[] _coordUnitsRadios = null;
        private JRadioButton[] _altitudeUnitsRadios = null;
+       private JRadioButton[] _timestampUnitsRadios = null;
+
        private static final int[] FORMAT_COORDS = {Coordinate.FORMAT_NONE, Coordinate.FORMAT_DEG_MIN_SEC,
                Coordinate.FORMAT_DEG_MIN, Coordinate.FORMAT_DEG};
-       private static final int[] FORMAT_ALTS = {Altitude.FORMAT_NONE, Altitude.FORMAT_METRES, Altitude.FORMAT_FEET};
+       private static final Altitude.Format[] FORMAT_ALTS = {Altitude.Format.NO_FORMAT, Altitude.Format.METRES, Altitude.Format.FEET};
+       private static final int[] FORMAT_TIMES = {Timestamp.FORMAT_ORIGINAL, Timestamp.FORMAT_LOCALE, Timestamp.FORMAT_ISO_8601};
 
 
        /**
         * Constructor
         * @param inApp application object to inform of success
         * @param inParentFrame parent frame
-        * @param inTrack track object to save
         */
-       public FileSaver(App inApp, JFrame inParentFrame, Track inTrack)
+       public FileSaver(App inApp, JFrame inParentFrame)
        {
                _app = inApp;
                _parentFrame = inParentFrame;
-               _track = inTrack;
        }
 
 
@@ -83,31 +94,35 @@ public class FileSaver
         */
        public void showDialog(char inDefaultDelimiter)
        {
-               _dialog = new JDialog(_parentFrame, I18nManager.getText("dialog.saveoptions.title"), true);
-               _dialog.setLocationRelativeTo(_parentFrame);
+               if (_dialog == null)
+               {
+                       _dialog = new JDialog(_parentFrame, I18nManager.getText("dialog.saveoptions.title"), true);
+                       _dialog.setLocationRelativeTo(_parentFrame);
+                       _dialog.getContentPane().add(makeDialogComponents());
+                       _dialog.pack();
+               }
                // Check field list
-               FieldList fieldList = _track.getFieldList();
+               Track track = _app.getTrackInfo().getTrack();
+               FieldList fieldList = track.getFieldList();
                int numFields = fieldList.getNumFields();
                _model = new FieldSelectionTableModel(numFields);
                for (int i=0; i<numFields; i++)
                {
                        Field field = fieldList.getField(i);
-                       FieldInfo info = new FieldInfo(field, _track.hasData(field));
+                       FieldInfo info = new FieldInfo(field, track.hasData(field));
                        _model.addFieldInfo(info, i);
                }
-               _dialog.getContentPane().add(makeDialogComponents(_model, inDefaultDelimiter));
-               _dialog.pack();
-               _dialog.show();
+               // Initialise dialog and show it
+               initDialog(_model, inDefaultDelimiter);
+               _dialog.setVisible(true);
        }
 
 
        /**
         * Make the dialog components
-        * @param inTableModel table model for fields
-        * @param inDelimiter default delimiter character
         * @return the GUI components for the save dialog
         */
-       private Component makeDialogComponents(FieldSelectionTableModel inTableModel, char inDelimiter)
+       private Component makeDialogComponents()
        {
                JPanel panel = new JPanel();
                panel.setLayout(new BorderLayout());
@@ -120,10 +135,12 @@ public class FileSaver
                firstCard.setLayout(new BoxLayout(firstCard, BoxLayout.Y_AXIS));
                JPanel tablePanel = new JPanel();
                tablePanel.setLayout(new BorderLayout());
-               _table = new JTable(inTableModel);
+               _table = new JTable();
                _table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
-               tablePanel.add(_table.getTableHeader(), BorderLayout.NORTH);
-               tablePanel.add(_table, BorderLayout.CENTER);
+               // Enclose table in a scrollpane to prevent other components getting lost
+               JScrollPane scrollPane = new JScrollPane(_table);
+               _table.setPreferredScrollableViewportSize(new Dimension(300, 150));
+               tablePanel.add(scrollPane, BorderLayout.CENTER);
 
                // Make a panel to hold the table and up/down buttons
                JPanel fieldsPanel = new JPanel();
@@ -161,9 +178,8 @@ public class FileSaver
                updownPanel.add(_moveDownButton);
                fieldsPanel.add(updownPanel, BorderLayout.EAST);
                // enable/disable buttons based on table row selection
-               _table.getSelectionModel().addListSelectionListener(
-                       new UpDownToggler(_moveUpButton, _moveDownButton, inTableModel.getRowCount())
-               );
+               _toggler = new UpDownToggler(_moveUpButton, _moveDownButton);
+               _table.getSelectionModel().addListSelectionListener(_toggler);
 
                // Add fields panel and the delimiter panel to first card in pack
                JLabel saveOptionsLabel = new JLabel(I18nManager.getText("dialog.save.fieldstosave"));
@@ -202,24 +218,25 @@ public class FileSaver
                {
                        delimGroup.add(_delimiterRadios[i]);
                }
-               // choose last-used delimiter as default
-               switch (inDelimiter)
-               {
-                       case ','  : _delimiterRadios[0].setSelected(true); break;
-                       case '\t' : _delimiterRadios[1].setSelected(true); break;
-                       case ';'  : _delimiterRadios[2].setSelected(true); break;
-                       case ' '  : _delimiterRadios[3].setSelected(true); break;
-                       default   : _delimiterRadios[4].setSelected(true);
-                                               _otherDelimiterText.setText("" + inDelimiter);
-               }
                delimsPanel.add(otherPanel);
                firstCard.add(delimsPanel);
+
+               // header checkbox
+               firstCard.add(Box.createRigidArea(new Dimension(0,10)));
+               _headerRowCheckbox = new JCheckBox(I18nManager.getText("dialog.save.headerrow"), true);
+               firstCard.add(_headerRowCheckbox);
                _cards.add(firstCard, "card1");
 
+               // Second card
                JPanel secondCard = new JPanel();
                secondCard.setLayout(new BorderLayout());
                JPanel secondCardHolder = new JPanel();
                secondCardHolder.setLayout(new BoxLayout(secondCardHolder, BoxLayout.Y_AXIS));
+               // point type selector
+               secondCardHolder.add(Box.createRigidArea(new Dimension(0,10)));
+               _pointTypeSelector = new PointTypeSelector();
+               _pointTypeSelector.setAlignmentX(JLabel.LEFT_ALIGNMENT);
+               secondCardHolder.add(_pointTypeSelector);
                JLabel coordLabel = new JLabel(I18nManager.getText("dialog.save.coordinateunits"));
                coordLabel.setAlignmentX(JLabel.LEFT_ALIGNMENT);
                secondCardHolder.add(coordLabel);
@@ -227,7 +244,7 @@ public class FileSaver
                coordsUnitsPanel.setBorder(BorderFactory.createEtchedBorder());
                coordsUnitsPanel.setLayout(new GridLayout(0, 2));
                _coordUnitsRadios = new JRadioButton[4];
-               _coordUnitsRadios[0] = new JRadioButton(I18nManager.getText("dialog.save.units.original"));
+               _coordUnitsRadios[0] = new JRadioButton(I18nManager.getText("units.original"));
                _coordUnitsRadios[1] = new JRadioButton(I18nManager.getText("units.degminsec"));
                _coordUnitsRadios[2] = new JRadioButton(I18nManager.getText("units.degmin"));
                _coordUnitsRadios[3] = new JRadioButton(I18nManager.getText("units.deg"));
@@ -240,7 +257,8 @@ public class FileSaver
                }
                coordsUnitsPanel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
                secondCardHolder.add(coordsUnitsPanel);
-               secondCardHolder.add(Box.createRigidArea(new Dimension(0,10)));
+               secondCardHolder.add(Box.createRigidArea(new Dimension(0,7)));
+               // altitude units
                JLabel altUnitsLabel = new JLabel(I18nManager.getText("dialog.save.altitudeunits"));
                altUnitsLabel.setAlignmentX(JLabel.LEFT_ALIGNMENT);
                secondCardHolder.add(altUnitsLabel);
@@ -248,7 +266,7 @@ public class FileSaver
                altUnitsPanel.setBorder(BorderFactory.createEtchedBorder());
                altUnitsPanel.setLayout(new GridLayout(0, 2));
                _altitudeUnitsRadios = new JRadioButton[3];
-               _altitudeUnitsRadios[0] = new JRadioButton(I18nManager.getText("dialog.save.units.original"));
+               _altitudeUnitsRadios[0] = new JRadioButton(I18nManager.getText("units.original"));
                _altitudeUnitsRadios[1] = new JRadioButton(I18nManager.getText("units.metres"));
                _altitudeUnitsRadios[2] = new JRadioButton(I18nManager.getText("units.feet"));
                ButtonGroup altGroup = new ButtonGroup();
@@ -260,6 +278,27 @@ public class FileSaver
                }
                altUnitsPanel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
                secondCardHolder.add(altUnitsPanel);
+               secondCardHolder.add(Box.createRigidArea(new Dimension(0,7)));
+               // Selection of format of timestamps
+               JLabel timestampLabel = new JLabel(I18nManager.getText("dialog.save.timestampformat"));
+               timestampLabel.setAlignmentX(JLabel.LEFT_ALIGNMENT);
+               secondCardHolder.add(timestampLabel);
+               JPanel timestampPanel = new JPanel();
+               timestampPanel.setBorder(BorderFactory.createEtchedBorder());
+               timestampPanel.setLayout(new GridLayout(0, 2));
+               _timestampUnitsRadios = new JRadioButton[3];
+               _timestampUnitsRadios[0] = new JRadioButton(I18nManager.getText("units.original"));
+               _timestampUnitsRadios[1] = new JRadioButton(I18nManager.getText("units.default"));
+               _timestampUnitsRadios[2] = new JRadioButton(I18nManager.getText("units.iso8601"));
+               ButtonGroup timeGroup = new ButtonGroup();
+               for (int i=0; i<3; i++)
+               {
+                       timeGroup.add(_timestampUnitsRadios[i]);
+                       timestampPanel.add(_timestampUnitsRadios[i]);
+                       _timestampUnitsRadios[i].setSelected(i==0);
+               }
+               timestampPanel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
+               secondCardHolder.add(timestampPanel);
                secondCard.add(secondCardHolder, BorderLayout.NORTH);
                _cards.add(secondCard, "card2");
 
@@ -270,10 +309,10 @@ public class FileSaver
                _backButton.addActionListener(new ActionListener() {
                        public void actionPerformed(ActionEvent e)
                        {
-                           CardLayout cl = (CardLayout)(_cards.getLayout());
-                           cl.previous(_cards);
-                           _backButton.setEnabled(false);
-                           _nextButton.setEnabled(true);
+                               CardLayout cl = (CardLayout) _cards.getLayout();
+                               cl.previous(_cards);
+                               _backButton.setEnabled(false);
+                               _nextButton.setEnabled(true);
                        }
                });
                _backButton.setEnabled(false);
@@ -283,10 +322,10 @@ public class FileSaver
                _nextButton.addActionListener(new ActionListener() {
                        public void actionPerformed(ActionEvent e)
                        {
-                           CardLayout cl = (CardLayout)(_cards.getLayout());
-                           cl.next(_cards);
-                           _backButton.setEnabled(true);
-                           _nextButton.setEnabled(false);
+                               CardLayout cl = (CardLayout) _cards.getLayout();
+                               cl.next(_cards);
+                               _backButton.setEnabled(true);
+                               _nextButton.setEnabled(false);
                        }
                });
                buttonPanel.add(_nextButton);
@@ -313,134 +352,269 @@ public class FileSaver
                return panel;
        }
 
+       /**
+        * Initialize the dialog with the given details
+        * @param inModel table model
+        * @param inDefaultDelimiter default delimiter character
+        */
+       private void initDialog(TableModel inModel, char inDefaultDelimiter)
+       {
+               // set table model
+               _table.setModel(inModel);
+               // reset toggler
+               _toggler.setListSize(inModel.getRowCount());
+               // choose last-used delimiter as default
+               switch (inDefaultDelimiter)
+               {
+                       case ','  : _delimiterRadios[0].setSelected(true); break;
+                       case '\t' : _delimiterRadios[1].setSelected(true); break;
+                       case ';'  : _delimiterRadios[2].setSelected(true); break;
+                       case ' '  : _delimiterRadios[3].setSelected(true); break;
+                       default   : _delimiterRadios[4].setSelected(true);
+                                               _otherDelimiterText.setText("" + inDefaultDelimiter);
+               }
+               _pointTypeSelector.init(_app.getTrackInfo());
+               // set card and enable buttons
+               CardLayout cl = (CardLayout) _cards.getLayout();
+               cl.first(_cards);
+               _nextButton.setEnabled(true);
+               _backButton.setEnabled(false);
+       }
+
 
        /**
-        * Save the track to file with the chosen options
+        * Start the save process by choosing the file to save to
         * @return true if successful or cancelled, false if failed
         */
        private boolean saveToFile()
        {
-               boolean saveOK = true;
-               FileWriter writer = null;
+               if (!_pointTypeSelector.getAnythingSelected()) {
+                       JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.save.notypesselected"),
+                               I18nManager.getText("dialog.saveoptions.title"), JOptionPane.WARNING_MESSAGE);
+                       return false;
+               }
                if (_fileChooser == null)
+               {
                        _fileChooser = new JFileChooser();
-               _fileChooser.setDialogType(JFileChooser.SAVE_DIALOG);
+                       _fileChooser.setDialogType(JFileChooser.SAVE_DIALOG);
+                       _fileChooser.addChoosableFileFilter(new GenericFileFilter("filetype.txt", new String[] {"txt", "text"}));
+                       _fileChooser.setAcceptAllFileFilterUsed(true);
+                       // start from directory in config which should be set
+                       String configDir = Config.getConfigString(Config.KEY_TRACK_DIR);
+                       if (configDir == null) {configDir = Config.getConfigString(Config.KEY_PHOTO_DIR);}
+                       if (configDir != null) {_fileChooser.setCurrentDirectory(new File(configDir));}
+               }
                if (_fileChooser.showSaveDialog(_parentFrame) == JFileChooser.APPROVE_OPTION)
                {
-                       File saveFile = _fileChooser.getSelectedFile();
-                       String lineSeparator = System.getProperty("line.separator");
-                       // Get coordinate format and altitude format
-                       int coordFormat = Coordinate.FORMAT_NONE;
-                       for (int i=0; i<_coordUnitsRadios.length; i++)
-                               if (_coordUnitsRadios[i].isSelected())
-                                       coordFormat = FORMAT_COORDS[i];
-                       int altitudeFormat = Altitude.FORMAT_NONE;
-                       for (int i=0; i<_altitudeUnitsRadios.length; i++)
-                               if (_altitudeUnitsRadios[i].isSelected())
-                                       altitudeFormat = FORMAT_ALTS[i];
-                       
-                       // Check if file exists, don't overwrite any files for v1!
-                       if (!saveFile.exists())
+                       return saveToFile(_fileChooser.getSelectedFile());
+               }
+               return true; // cancelled
+       }
+
+
+       /**
+        * Save the track to the specified file using the chosen options
+        * @param inSaveFile file to save to
+        * @return true if save successful, false if failed
+        */
+       private boolean saveToFile(File inSaveFile)
+       {
+               // TODO: Shorten method
+               FileWriter writer = null;
+               final String lineSeparator = System.getProperty("line.separator");
+               boolean saveOK = true;
+               // Get coordinate format and altitude format
+               int coordFormat = Coordinate.FORMAT_NONE;
+               for (int i=0; i<_coordUnitsRadios.length; i++)
+                       if (_coordUnitsRadios[i].isSelected())
+                               coordFormat = FORMAT_COORDS[i];
+               Altitude.Format altitudeFormat = Altitude.Format.NO_FORMAT;
+               for (int i=0; i<_altitudeUnitsRadios.length; i++)
+               {
+                       if (_altitudeUnitsRadios[i].isSelected()) {
+                               altitudeFormat = FORMAT_ALTS[i];
+                       }
+               }
+               // Get timestamp format
+               int timestampFormat = Timestamp.FORMAT_ORIGINAL;
+               for (int i=0; i<_timestampUnitsRadios.length; i++)
+               {
+                       if (_timestampUnitsRadios[i].isSelected()) {
+                               timestampFormat = FORMAT_TIMES[i];
+                       }
+               }
+
+               // Correct chosen filename if necessary
+               final File saveFile = (isFilenameOk(inSaveFile)?inSaveFile:new File(inSaveFile.getAbsolutePath() + ".txt"));
+
+               // Check if file exists, and confirm overwrite if necessary
+               Object[] buttonTexts = {I18nManager.getText("button.overwrite"), I18nManager.getText("button.cancel")};
+               if (!saveFile.exists() || JOptionPane.showOptionDialog(_parentFrame,
+                               I18nManager.getText("dialog.save.overwrite.text"),
+                               I18nManager.getText("dialog.save.overwrite.title"), JOptionPane.YES_NO_OPTION,
+                               JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1])
+                       == JOptionPane.YES_OPTION)
+               {
+                       try
                        {
-                               try
+                               // Create output file
+                               writer = new FileWriter(saveFile);
+                               // Determine delimiter character to use
+                               final char delimiter = getDelimiter();
+                               FieldInfo info = null;
+
+                               StringBuffer buffer = null;
+                               int numFields = _model.getRowCount();
+                               boolean firstField = true;
+                               // Write header row if required
+                               if (_headerRowCheckbox.isSelected())
                                {
-                                       // Create output file
-                                       writer = new FileWriter(saveFile);
-                                       // Determine delimiter character to use
-                                       char delimiter = getDelimiter();
-                                       FieldInfo info = null;
-                                       Field field = null;
-                                       StringBuffer buffer = null;
-                                       // For now, just spit out to console
-                                       int numPoints = _track.getNumPoints();
-                                       int numFields = _model.getRowCount();
-                                       for (int p=0; p<numPoints; p++)
+                                       buffer = new StringBuffer();
+                                       for (int f=0; f<numFields; f++)
                                        {
-                                               DataPoint point = _track.getPoint(p);
-                                               boolean firstField = true;
-                                               buffer = new StringBuffer();
-                                               for (int f=0; f<numFields; f++)
+                                               info = _model.getFieldInfo(f);
+                                               if (info.isSelected())
                                                {
-                                                       info = _model.getFieldInfo(f);
-                                                       if (info.isSelected())
-                                                       {
-                                                               if (!firstField)
-                                                               {
-                                                                       // output field separator
-                                                                       buffer.append(delimiter);
-                                                               }
-                                                               field = info.getField();
-                                                               // Output field according to type
-                                                               if (field == Field.LATITUDE)
-                                                               {
-                                                                       buffer.append(point.getLatitude().output(coordFormat));
-                                                               }
-                                                               else if (field == Field.LONGITUDE)
-                                                               {
-                                                                       buffer.append(point.getLongitude().output(coordFormat));
-                                                               }
-                                                               else if (field == Field.ALTITUDE)
-                                                               {
-                                                                       buffer.append(point.getAltitude().getValue(altitudeFormat));
-                                                               }
-                                                               else if (field == Field.TIMESTAMP)
-                                                               {
-                                                                       buffer.append(point.getTimestamp().getText());
-                                                               }
-                                                               else
-                                                               {
-                                                                       String value = point.getFieldValue(field);
-                                                                       if (value != null)
-                                                                       {
-                                                                               buffer.append(value);
-                                                                       }
-                                                               }
-                                                               firstField = false;
+                                                       // output field separator
+                                                       if (!firstField) {
+                                                               buffer.append(delimiter);
                                                        }
+                                                       buffer.append(info.getField().getName());
+                                                       firstField = false;
                                                }
-                                               // Output to file
-                                               writer.write(buffer.toString());
-                                               writer.write(lineSeparator);
                                        }
-                                       // Save successful
-                                       JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.save.ok1")
-                                                + " " + numPoints + " " + I18nManager.getText("dialog.save.ok2")
-                                                + saveFile.getAbsolutePath(),
-                                               I18nManager.getText("dialog.save.oktitle"), JOptionPane.INFORMATION_MESSAGE);
-                                       _app.informDataSaved();
+                                       writer.write(buffer.toString());
+                                       writer.write(lineSeparator);
                                }
-                               catch (IOException ioe)
-                               {
-                                       saveOK = false;
-                                       JOptionPane.showMessageDialog(_parentFrame,
-                                               I18nManager.getText("error.save.failed") + ioe.getMessage(),
-                                               I18nManager.getText("error.save.dialogtitle"),
-                                               JOptionPane.ERROR_MESSAGE);
+
+                               // Examine selection
+                               int selStart = -1, selEnd = -1;
+                               if (_pointTypeSelector.getJustSelection()) {
+                                       selStart = _app.getTrackInfo().getSelection().getStart();
+                                       selEnd = _app.getTrackInfo().getSelection().getEnd();
                                }
-                               finally
+                               // Loop over points outputting each in turn to buffer
+                               Track track = _app.getTrackInfo().getTrack();
+                               final int numPoints = track.getNumPoints();
+                               int numSaved = 0;
+                               for (int p=0; p<numPoints; p++)
                                {
-                                       // try to close file if it's open
-                                       try
+                                       DataPoint point = track.getPoint(p);
+                                       boolean savePoint = ((point.isWaypoint() && _pointTypeSelector.getWaypointsSelected())
+                                               || (!point.isWaypoint() && !point.hasMedia() && _pointTypeSelector.getTrackpointsSelected())
+                                               || (!point.isWaypoint() && point.getPhoto()!=null && _pointTypeSelector.getPhotopointsSelected())
+                                               || (!point.isWaypoint() && point.getAudio()!=null && _pointTypeSelector.getAudiopointsSelected()))
+                                               && (!_pointTypeSelector.getJustSelection() || (p>=selStart && p<=selEnd));
+                                       if (!savePoint) {continue;}
+                                       numSaved++;
+                                       firstField = true;
+                                       buffer = new StringBuffer();
+                                       for (int f=0; f<numFields; f++)
                                        {
-                                               if (writer != null)
+                                               info = _model.getFieldInfo(f);
+                                               if (info.isSelected())
                                                {
-                                                       writer.close();
+                                                       // output field separator
+                                                       if (!firstField) {
+                                                               buffer.append(delimiter);
+                                                       }
+                                                       saveField(buffer, point, info.getField(), coordFormat, altitudeFormat, timestampFormat);
+                                                       firstField = false;
                                                }
                                        }
-                                       catch (Exception e) {}
+                                       // Output to file
+                                       writer.write(buffer.toString());
+                                       writer.write(lineSeparator);
                                }
+                               // Store directory in config for later
+                               Config.setConfigString(Config.KEY_TRACK_DIR, saveFile.getParentFile().getAbsolutePath());
+                               // Add to recent file list
+                               Config.getRecentFileList().addFile(new RecentFile(inSaveFile, true));
+                               // Save successful
+                               UpdateMessageBroker.informSubscribers();
+                               UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.save.ok1")
+                                        + " " + numSaved + " " + I18nManager.getText("confirm.save.ok2")
+                                        + " " + saveFile.getAbsolutePath());
+                               _app.informDataSaved();
                        }
-                       else
+                       catch (IOException ioe)
                        {
                                saveOK = false;
-                               JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("error.save.fileexists"),
-                                       I18nManager.getText("error.save.dialogtitle"), JOptionPane.ERROR_MESSAGE);
+                               _app.showErrorMessageNoLookup("error.save.dialogtitle",
+                                       I18nManager.getText("error.save.failed") + " : " + ioe.getMessage());
+                       }
+                       finally
+                       {
+                               // try to close file if it's open
+                               try {
+                                       writer.close();
+                               }
+                               catch (Exception e) {}
                        }
                }
+               else
+               {
+                       // Overwrite file confirm cancelled
+                       saveOK = false;
+               }
                return saveOK;
        }
 
 
+       /**
+        * Format the given field and append to the given buffer for saving
+        * @param inBuffer buffer to append to
+        * @param inPoint point object
+        * @param inField field object
+        * @param inCoordFormat coordinate format
+        * @param inAltitudeFormat altitude format
+        * @param inTimestampFormat timestamp format
+        */
+       private void saveField(StringBuffer inBuffer, DataPoint inPoint, Field inField,
+               int inCoordFormat, Altitude.Format inAltitudeFormat, int inTimestampFormat)
+       {
+               // Output field according to type
+               if (inField == Field.LATITUDE)
+               {
+                       inBuffer.append(inPoint.getLatitude().output(inCoordFormat));
+               }
+               else if (inField == Field.LONGITUDE)
+               {
+                       inBuffer.append(inPoint.getLongitude().output(inCoordFormat));
+               }
+               else if (inField == Field.ALTITUDE)
+               {
+                       try
+                       {
+                               inBuffer.append(inPoint.getAltitude().getStringValue(inAltitudeFormat));
+                       }
+                       catch (NullPointerException npe) {}
+               }
+               else if (inField == Field.TIMESTAMP)
+               {
+                       if (inPoint.hasTimestamp())
+                       {
+                               if (inTimestampFormat == Timestamp.FORMAT_ORIGINAL) {
+                                       // output original string
+                                       inBuffer.append(inPoint.getTimestamp().getText(Timestamp.FORMAT_ORIGINAL));
+                               }
+                               else {
+                                       // format value accordingly
+                                       inBuffer.append(inPoint.getTimestamp().getText(inTimestampFormat));
+                               }
+                       }
+               }
+               else
+               {
+                       String value = inPoint.getFieldValue(inField);
+                       if (value != null)
+                       {
+                               inBuffer.append(value);
+                       }
+               }
+       }
+
+
        /**
         * @return the selected delimiter character
         */
@@ -458,4 +632,17 @@ public class FileSaver
                // Wasn't any of those so must be 'other'
                return _otherDelimiterText.getText().charAt(0);
        }
+
+
+       /**
+        * Check the selected filename to see if it is acceptable
+        * @param inFile chosen file to save
+        * @return true if filename is ok
+        */
+       private static boolean isFilenameOk(File inFile)
+       {
+               String filename = inFile.getName().toLowerCase();
+               return (filename.length() <4 || (!filename.endsWith(".gpx")
+                       && !filename.endsWith(".kml") && !filename.endsWith(".kmz") && !filename.endsWith(".zip")));
+       }
 }