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