]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/save/FileSaver.java
Version 6, October 2008
[GpsPrune.git] / tim / prune / save / FileSaver.java
1 package tim.prune.save;
2
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;
11 import java.io.File;
12 import java.io.FileWriter;
13 import java.io.IOException;
14
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;
33
34 import tim.prune.App;
35 import tim.prune.Config;
36 import tim.prune.I18nManager;
37 import tim.prune.UpdateMessageBroker;
38 import tim.prune.data.Altitude;
39 import tim.prune.data.Coordinate;
40 import tim.prune.data.DataPoint;
41 import tim.prune.data.Field;
42 import tim.prune.data.FieldList;
43 import tim.prune.data.Timestamp;
44 import tim.prune.data.Track;
45 import tim.prune.load.GenericFileFilter;
46 import tim.prune.load.OneCharDocument;
47
48 /**
49  * Class to manage the saving of track data
50  * into a user-specified file
51  */
52 public class FileSaver
53 {
54         private App _app = null;
55         private JFrame _parentFrame = null;
56         private Track _track = null;
57         private JDialog _dialog = null;
58         private JFileChooser _fileChooser = null;
59         private JPanel _cards = null;
60         private JButton _nextButton = null, _backButton = null;
61         private JTable _table = null;
62         private FieldSelectionTableModel _model = null;
63         private JButton _moveUpButton = null, _moveDownButton = null;
64         private UpDownToggler _toggler = null;
65         private JRadioButton[] _delimiterRadios = null;
66         private JTextField _otherDelimiterText = null;
67         private JCheckBox _headerRowCheckbox = null;
68         private JRadioButton[] _coordUnitsRadios = null;
69         private JRadioButton[] _altitudeUnitsRadios = null;
70         private JRadioButton[] _timestampUnitsRadios = null;
71         private static final int[] FORMAT_COORDS = {Coordinate.FORMAT_NONE, Coordinate.FORMAT_DEG_MIN_SEC,
72                 Coordinate.FORMAT_DEG_MIN, Coordinate.FORMAT_DEG};
73         private static final int[] FORMAT_ALTS = {Altitude.FORMAT_NONE, Altitude.FORMAT_METRES, Altitude.FORMAT_FEET};
74         private static final int[] FORMAT_TIMES = {Timestamp.FORMAT_ORIGINAL, Timestamp.FORMAT_LOCALE, Timestamp.FORMAT_ISO_8601};
75
76
77         /**
78          * Constructor
79          * @param inApp application object to inform of success
80          * @param inParentFrame parent frame
81          * @param inTrack track object to save
82          */
83         public FileSaver(App inApp, JFrame inParentFrame, Track inTrack)
84         {
85                 _app = inApp;
86                 _parentFrame = inParentFrame;
87                 _track = inTrack;
88         }
89
90
91         /**
92          * Show the save file dialog
93          * @param inDefaultDelimiter default delimiter to use
94          */
95         public void showDialog(char inDefaultDelimiter)
96         {
97                 if (_dialog == null)
98                 {
99                         _dialog = new JDialog(_parentFrame, I18nManager.getText("dialog.saveoptions.title"), true);
100                         _dialog.setLocationRelativeTo(_parentFrame);
101                         _dialog.getContentPane().add(makeDialogComponents());
102                         _dialog.pack();
103                 }
104                 // Check field list
105                 FieldList fieldList = _track.getFieldList();
106                 int numFields = fieldList.getNumFields();
107                 _model = new FieldSelectionTableModel(numFields);
108                 for (int i=0; i<numFields; i++)
109                 {
110                         Field field = fieldList.getField(i);
111                         FieldInfo info = new FieldInfo(field, _track.hasData(field));
112                         _model.addFieldInfo(info, i);
113                 }
114                 // Initialise dialog and show it
115                 initDialog(_model, inDefaultDelimiter);
116                 _dialog.show();
117         }
118
119
120         /**
121          * Make the dialog components
122          * @return the GUI components for the save dialog
123          */
124         private Component makeDialogComponents()
125         {
126                 JPanel panel = new JPanel();
127                 panel.setLayout(new BorderLayout());
128                 _cards = new JPanel();
129                 _cards.setLayout(new CardLayout());
130                 panel.add(_cards, BorderLayout.CENTER);
131
132                 // Make first card for field selection and delimiter
133                 JPanel firstCard = new JPanel();
134                 firstCard.setLayout(new BoxLayout(firstCard, BoxLayout.Y_AXIS));
135                 JPanel tablePanel = new JPanel();
136                 tablePanel.setLayout(new BorderLayout());
137                 _table = new JTable();
138                 _table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
139                 // Enclose table in a scrollpane to prevent other components getting lost
140                 JScrollPane scrollPane = new JScrollPane(_table);
141                 _table.setPreferredScrollableViewportSize(new Dimension(300, 150));
142                 tablePanel.add(scrollPane, BorderLayout.CENTER);
143
144                 // Make a panel to hold the table and up/down buttons
145                 JPanel fieldsPanel = new JPanel();
146                 fieldsPanel.setLayout(new BorderLayout());
147                 fieldsPanel.add(tablePanel, BorderLayout.CENTER);
148                 JPanel updownPanel = new JPanel();
149                 updownPanel.setLayout(new BoxLayout(updownPanel, BoxLayout.Y_AXIS));
150                 _moveUpButton = new JButton(I18nManager.getText("button.moveup"));
151                 _moveUpButton.addActionListener(new ActionListener() {
152                         public void actionPerformed(ActionEvent e)
153                         {
154                                 int row = _table.getSelectedRow();
155                                 if (row > 0)
156                                 {
157                                         _model.swapItems(row, row - 1);
158                                         _table.setRowSelectionInterval(row - 1, row - 1);
159                                 }
160                         }
161                 });
162                 _moveUpButton.setEnabled(false);
163                 updownPanel.add(_moveUpButton);
164                 _moveDownButton = new JButton(I18nManager.getText("button.movedown"));
165                 _moveDownButton.addActionListener(new ActionListener() {
166                         public void actionPerformed(ActionEvent e)
167                         {
168                                 int row = _table.getSelectedRow();
169                                 if (row > -1 && row < (_model.getRowCount() - 1))
170                                 {
171                                         _model.swapItems(row, row + 1);
172                                         _table.setRowSelectionInterval(row + 1, row + 1);
173                                 }
174                         }
175                 });
176                 _moveDownButton.setEnabled(false);
177                 updownPanel.add(_moveDownButton);
178                 fieldsPanel.add(updownPanel, BorderLayout.EAST);
179                 // enable/disable buttons based on table row selection
180                 _toggler = new UpDownToggler(_moveUpButton, _moveDownButton);
181                 _table.getSelectionModel().addListSelectionListener(_toggler);
182
183                 // Add fields panel and the delimiter panel to first card in pack
184                 JLabel saveOptionsLabel = new JLabel(I18nManager.getText("dialog.save.fieldstosave"));
185                 saveOptionsLabel.setAlignmentX(JLabel.LEFT_ALIGNMENT);
186                 firstCard.add(saveOptionsLabel);
187                 fieldsPanel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
188                 firstCard.add(fieldsPanel);
189                 firstCard.add(Box.createRigidArea(new Dimension(0,10)));
190
191                 // delimiter panel
192                 JLabel delimLabel = new JLabel(I18nManager.getText("dialog.delimiter.label"));
193                 delimLabel.setAlignmentX(JLabel.LEFT_ALIGNMENT);
194                 firstCard.add(delimLabel);
195                 JPanel delimsPanel = new JPanel();
196                 delimsPanel.setLayout(new GridLayout(0, 2));
197                 delimsPanel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
198                 // radio buttons
199                 _delimiterRadios = new JRadioButton[5];
200                 _delimiterRadios[0] = new JRadioButton(I18nManager.getText("dialog.delimiter.comma"));
201                 delimsPanel.add(_delimiterRadios[0]);
202                 _delimiterRadios[1] = new JRadioButton(I18nManager.getText("dialog.delimiter.tab"));
203                 delimsPanel.add(_delimiterRadios[1]);
204                 _delimiterRadios[2] = new JRadioButton(I18nManager.getText("dialog.delimiter.semicolon"));
205                 delimsPanel.add(_delimiterRadios[2]);
206                 _delimiterRadios[3] = new JRadioButton(I18nManager.getText("dialog.delimiter.space"));
207                 delimsPanel.add(_delimiterRadios[3]);
208                 JPanel otherPanel = new JPanel();
209                 otherPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
210                 _delimiterRadios[4] = new JRadioButton(I18nManager.getText("dialog.delimiter.other"));
211                 otherPanel.add(_delimiterRadios[4]);
212                 _otherDelimiterText = new JTextField(new OneCharDocument(), null, 2);
213                 otherPanel.add(_otherDelimiterText);
214                 // Group radio buttons
215                 ButtonGroup delimGroup = new ButtonGroup();
216                 for (int i=0; i<_delimiterRadios.length; i++)
217                 {
218                         delimGroup.add(_delimiterRadios[i]);
219                 }
220                 delimsPanel.add(otherPanel);
221                 firstCard.add(delimsPanel);
222
223                 // header checkbox
224                 firstCard.add(Box.createRigidArea(new Dimension(0,10)));
225                 _headerRowCheckbox = new JCheckBox(I18nManager.getText("dialog.save.headerrow"), true);
226                 firstCard.add(_headerRowCheckbox);
227
228                 _cards.add(firstCard, "card1");
229
230                 JPanel secondCard = new JPanel();
231                 secondCard.setLayout(new BorderLayout());
232                 JPanel secondCardHolder = new JPanel();
233                 secondCardHolder.setLayout(new BoxLayout(secondCardHolder, BoxLayout.Y_AXIS));
234                 JLabel coordLabel = new JLabel(I18nManager.getText("dialog.save.coordinateunits"));
235                 coordLabel.setAlignmentX(JLabel.LEFT_ALIGNMENT);
236                 secondCardHolder.add(coordLabel);
237                 JPanel coordsUnitsPanel = new JPanel();
238                 coordsUnitsPanel.setBorder(BorderFactory.createEtchedBorder());
239                 coordsUnitsPanel.setLayout(new GridLayout(0, 2));
240                 _coordUnitsRadios = new JRadioButton[4];
241                 _coordUnitsRadios[0] = new JRadioButton(I18nManager.getText("units.original"));
242                 _coordUnitsRadios[1] = new JRadioButton(I18nManager.getText("units.degminsec"));
243                 _coordUnitsRadios[2] = new JRadioButton(I18nManager.getText("units.degmin"));
244                 _coordUnitsRadios[3] = new JRadioButton(I18nManager.getText("units.deg"));
245                 ButtonGroup coordGroup = new ButtonGroup();
246                 for (int i=0; i<4; i++)
247                 {
248                         coordGroup.add(_coordUnitsRadios[i]);
249                         coordsUnitsPanel.add(_coordUnitsRadios[i]);
250                         _coordUnitsRadios[i].setSelected(i==0);
251                 }
252                 coordsUnitsPanel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
253                 secondCardHolder.add(coordsUnitsPanel);
254                 secondCardHolder.add(Box.createRigidArea(new Dimension(0,10)));
255                 // altitude units
256                 JLabel altUnitsLabel = new JLabel(I18nManager.getText("dialog.save.altitudeunits"));
257                 altUnitsLabel.setAlignmentX(JLabel.LEFT_ALIGNMENT);
258                 secondCardHolder.add(altUnitsLabel);
259                 JPanel altUnitsPanel = new JPanel();
260                 altUnitsPanel.setBorder(BorderFactory.createEtchedBorder());
261                 altUnitsPanel.setLayout(new GridLayout(0, 2));
262                 _altitudeUnitsRadios = new JRadioButton[3];
263                 _altitudeUnitsRadios[0] = new JRadioButton(I18nManager.getText("units.original"));
264                 _altitudeUnitsRadios[1] = new JRadioButton(I18nManager.getText("units.metres"));
265                 _altitudeUnitsRadios[2] = new JRadioButton(I18nManager.getText("units.feet"));
266                 ButtonGroup altGroup = new ButtonGroup();
267                 for (int i=0; i<3; i++)
268                 {
269                         altGroup.add(_altitudeUnitsRadios[i]);
270                         altUnitsPanel.add(_altitudeUnitsRadios[i]);
271                         _altitudeUnitsRadios[i].setSelected(i==0);
272                 }
273                 altUnitsPanel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
274                 secondCardHolder.add(altUnitsPanel);
275                 secondCardHolder.add(Box.createRigidArea(new Dimension(0,10)));
276                 // Selection of format of timestamps
277                 JLabel timestampLabel = new JLabel(I18nManager.getText("dialog.save.timestampformat"));
278                 timestampLabel.setAlignmentX(JLabel.LEFT_ALIGNMENT);
279                 secondCardHolder.add(timestampLabel);
280                 JPanel timestampPanel = new JPanel();
281                 timestampPanel.setBorder(BorderFactory.createEtchedBorder());
282                 timestampPanel.setLayout(new GridLayout(0, 2));
283                 _timestampUnitsRadios = new JRadioButton[3];
284                 _timestampUnitsRadios[0] = new JRadioButton(I18nManager.getText("units.original"));
285                 _timestampUnitsRadios[1] = new JRadioButton(I18nManager.getText("units.default"));
286                 _timestampUnitsRadios[2] = new JRadioButton(I18nManager.getText("units.iso8601"));
287                 ButtonGroup timeGroup = new ButtonGroup();
288                 for (int i=0; i<3; i++)
289                 {
290                         timeGroup.add(_timestampUnitsRadios[i]);
291                         timestampPanel.add(_timestampUnitsRadios[i]);
292                         _timestampUnitsRadios[i].setSelected(i==0);
293                 }
294                 timestampPanel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
295                 secondCardHolder.add(timestampPanel);
296                 secondCard.add(secondCardHolder, BorderLayout.NORTH);
297                 _cards.add(secondCard, "card2");
298
299                 // Put together with ok/cancel buttons on the bottom
300                 JPanel buttonPanel = new JPanel();
301                 buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
302                 _backButton = new JButton(I18nManager.getText("button.back"));
303                 _backButton.addActionListener(new ActionListener() {
304                         public void actionPerformed(ActionEvent e)
305                         {
306                                 CardLayout cl = (CardLayout) _cards.getLayout();
307                                 cl.previous(_cards);
308                                 _backButton.setEnabled(false);
309                                 _nextButton.setEnabled(true);
310                         }
311                 });
312                 _backButton.setEnabled(false);
313                 buttonPanel.add(_backButton);
314                 _nextButton = new JButton(I18nManager.getText("button.next"));
315                 _nextButton.setEnabled(true);
316                 _nextButton.addActionListener(new ActionListener() {
317                         public void actionPerformed(ActionEvent e)
318                         {
319                                 CardLayout cl = (CardLayout) _cards.getLayout();
320                                 cl.next(_cards);
321                                 _backButton.setEnabled(true);
322                                 _nextButton.setEnabled(false);
323                         }
324                 });
325                 buttonPanel.add(_nextButton);
326                 JButton okButton = new JButton(I18nManager.getText("button.finish"));
327                 okButton.addActionListener(new ActionListener() {
328                         public void actionPerformed(ActionEvent e)
329                         {
330                                 if (saveToFile())
331                                 {
332                                         _dialog.dispose();
333                                 }
334                         }
335                 });
336                 buttonPanel.add(okButton);
337                 JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
338                 cancelButton.addActionListener(new ActionListener() {
339                         public void actionPerformed(ActionEvent e)
340                         {
341                                 _dialog.dispose();
342                         }
343                 });
344                 buttonPanel.add(cancelButton);
345                 panel.add(buttonPanel, BorderLayout.SOUTH);
346                 return panel;
347         }
348
349         /**
350          * Initialize the dialog with the given details
351          * @param inModel table model
352          * @param inDefaultDelimiter default delimiter character
353          */
354         private void initDialog(TableModel inModel, char inDefaultDelimiter)
355         {
356                 // set table model
357                 _table.setModel(inModel);
358                 // reset toggler
359                 _toggler.setListSize(inModel.getRowCount());
360                 // choose last-used delimiter as default
361                 switch (inDefaultDelimiter)
362                 {
363                         case ','  : _delimiterRadios[0].setSelected(true); break;
364                         case '\t' : _delimiterRadios[1].setSelected(true); break;
365                         case ';'  : _delimiterRadios[2].setSelected(true); break;
366                         case ' '  : _delimiterRadios[3].setSelected(true); break;
367                         default   : _delimiterRadios[4].setSelected(true);
368                                                 _otherDelimiterText.setText("" + inDefaultDelimiter);
369                 }
370                 // set card and enable buttons
371                 CardLayout cl = (CardLayout) _cards.getLayout();
372                 cl.first(_cards);
373                 _nextButton.setEnabled(true);
374                 _backButton.setEnabled(false);
375         }
376
377
378         /**
379          * Save the track to file with the chosen options
380          * @return true if successful or cancelled, false if failed
381          */
382         private boolean saveToFile()
383         {
384                 boolean saveOK = true;
385                 FileWriter writer = null;
386                 if (_fileChooser == null)
387                 {
388                         _fileChooser = new JFileChooser();
389                         _fileChooser.setDialogType(JFileChooser.SAVE_DIALOG);
390                         _fileChooser.addChoosableFileFilter(new GenericFileFilter("filetype.txt", new String[] {"txt", "text"}));
391                         _fileChooser.addChoosableFileFilter(new GenericFileFilter("filetype.gpx", new String[] {"gpx"}));
392                         _fileChooser.addChoosableFileFilter(new GenericFileFilter("filetype.kml", new String[] {"kml"}));
393                         _fileChooser.setAcceptAllFileFilterUsed(true);
394                         // start from directory in config which should be set
395                         File configDir = Config.getWorkingDirectory();
396                         if (configDir != null) {_fileChooser.setCurrentDirectory(configDir);}
397                 }
398                 if (_fileChooser.showSaveDialog(_parentFrame) == JFileChooser.APPROVE_OPTION)
399                 {
400                         File saveFile = _fileChooser.getSelectedFile();
401                         String lineSeparator = System.getProperty("line.separator");
402                         // Get coordinate format and altitude format
403                         int coordFormat = Coordinate.FORMAT_NONE;
404                         for (int i=0; i<_coordUnitsRadios.length; i++)
405                                 if (_coordUnitsRadios[i].isSelected())
406                                         coordFormat = FORMAT_COORDS[i];
407                         int altitudeFormat = Altitude.FORMAT_NONE;
408                         for (int i=0; i<_altitudeUnitsRadios.length; i++)
409                         {
410                                 if (_altitudeUnitsRadios[i].isSelected())
411                                 {
412                                         altitudeFormat = FORMAT_ALTS[i];
413                                 }
414                         }
415                         // Get timestamp formats
416                         int timestampFormat = Timestamp.FORMAT_ORIGINAL;
417                         for (int i=0; i<_timestampUnitsRadios.length; i++)
418                         {
419                                 if (_timestampUnitsRadios[i].isSelected())
420                                 {
421                                         timestampFormat = FORMAT_TIMES[i];
422                                 }
423                         }
424
425                         // Check if file exists, and confirm overwrite if necessary
426                         Object[] buttonTexts = {I18nManager.getText("button.overwrite"), I18nManager.getText("button.cancel")};
427                         if (!saveFile.exists() || JOptionPane.showOptionDialog(_parentFrame,
428                                         I18nManager.getText("dialog.save.overwrite.text"),
429                                         I18nManager.getText("dialog.save.overwrite.title"), JOptionPane.YES_NO_OPTION,
430                                         JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1])
431                                 == JOptionPane.YES_OPTION)
432                         {
433                                 try
434                                 {
435                                         // Create output file
436                                         writer = new FileWriter(saveFile);
437                                         // Determine delimiter character to use
438                                         char delimiter = getDelimiter();
439                                         FieldInfo info = null;
440                                         Field field = null;
441
442                                         StringBuffer buffer = null;
443                                         int numFields = _model.getRowCount();
444                                         boolean firstField = true;
445                                         // Write header row if required
446                                         if (_headerRowCheckbox.isSelected())
447                                         {
448                                                 buffer = new StringBuffer();
449                                                 for (int f=0; f<numFields; f++)
450                                                 {
451                                                         info = _model.getFieldInfo(f);
452                                                         if (info.isSelected())
453                                                         {
454                                                                 if (!firstField)
455                                                                 {
456                                                                         // output field separator
457                                                                         buffer.append(delimiter);
458                                                                 }
459                                                                 field = info.getField();
460                                                                 buffer.append(field.getName());
461                                                                 firstField = false;
462                                                         }
463                                                 }
464                                                 writer.write(buffer.toString());
465                                                 writer.write(lineSeparator);
466                                         }
467
468                                         // Loop over points outputting each in turn to buffer
469                                         int numPoints = _track.getNumPoints();
470                                         for (int p=0; p<numPoints; p++)
471                                         {
472                                                 DataPoint point = _track.getPoint(p);
473                                                 firstField = true;
474                                                 buffer = new StringBuffer();
475                                                 for (int f=0; f<numFields; f++)
476                                                 {
477                                                         info = _model.getFieldInfo(f);
478                                                         if (info.isSelected())
479                                                         {
480                                                                 if (!firstField)
481                                                                 {
482                                                                         // output field separator
483                                                                         buffer.append(delimiter);
484                                                                 }
485                                                                 field = info.getField();
486                                                                 // Output field according to type
487                                                                 if (field == Field.LATITUDE)
488                                                                 {
489                                                                         buffer.append(point.getLatitude().output(coordFormat));
490                                                                 }
491                                                                 else if (field == Field.LONGITUDE)
492                                                                 {
493                                                                         buffer.append(point.getLongitude().output(coordFormat));
494                                                                 }
495                                                                 else if (field == Field.ALTITUDE)
496                                                                 {
497                                                                         try
498                                                                         {
499                                                                                 buffer.append(point.getAltitude().getStringValue(altitudeFormat));
500                                                                         }
501                                                                         catch (NullPointerException npe) {}
502                                                                 }
503                                                                 else if (field == Field.TIMESTAMP)
504                                                                 {
505                                                                         if (point.hasTimestamp())
506                                                                         {
507                                                                                 if (timestampFormat == Timestamp.FORMAT_ORIGINAL) {
508                                                                                         // output original string
509                                                                                         buffer.append(point.getFieldValue(Field.TIMESTAMP));
510                                                                                 }
511                                                                                 else {
512                                                                                         // format value accordingly
513                                                                                         buffer.append(point.getTimestamp().getText(timestampFormat));
514                                                                                 }
515                                                                         }
516                                                                 }
517                                                                 else
518                                                                 {
519                                                                         String value = point.getFieldValue(field);
520                                                                         if (value != null)
521                                                                         {
522                                                                                 buffer.append(value);
523                                                                         }
524                                                                 }
525                                                                 firstField = false;
526                                                         }
527                                                 }
528                                                 // Output to file
529                                                 writer.write(buffer.toString());
530                                                 writer.write(lineSeparator);
531                                         }
532                                         // Store directory in config for later
533                                         Config.setWorkingDirectory(saveFile.getParentFile());
534                                         // Save successful
535                                         UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.save.ok1")
536                                                  + " " + numPoints + " " + I18nManager.getText("confirm.save.ok2")
537                                                  + " " + saveFile.getAbsolutePath());
538                                         _app.informDataSaved();
539                                 }
540                                 catch (IOException ioe)
541                                 {
542                                         saveOK = false;
543                                         JOptionPane.showMessageDialog(_parentFrame,
544                                                 I18nManager.getText("error.save.failed") + ioe.getMessage(),
545                                                 I18nManager.getText("error.save.dialogtitle"),
546                                                 JOptionPane.ERROR_MESSAGE);
547                                 }
548                                 finally
549                                 {
550                                         // try to close file if it's open
551                                         try
552                                         {
553                                                 if (writer != null)
554                                                 {
555                                                         writer.close();
556                                                 }
557                                         }
558                                         catch (Exception e) {}
559                                 }
560                         }
561                         else
562                         {
563                                 // Overwrite file confirm cancelled
564                                 saveOK = false;
565                         }
566                 }
567                 return saveOK;
568         }
569
570
571         /**
572          * @return the selected delimiter character
573          */
574         private char getDelimiter()
575         {
576                 // Check the preset 4 delimiters
577                 final char[] delimiters = {',', '\t', ';', ' '};
578                 for (int i=0; i<4; i++)
579                 {
580                         if (_delimiterRadios[i].isSelected())
581                         {
582                                 return delimiters[i];
583                         }
584                 }
585                 // Wasn't any of those so must be 'other'
586                 return _otherDelimiterText.getText().charAt(0);
587         }
588 }