X-Git-Url: http://gitweb.fperrin.net/?p=GpsPrune.git;a=blobdiff_plain;f=src%2Ftim%2Fprune%2Fsave%2FFileSaver.java;fp=src%2Ftim%2Fprune%2Fsave%2FFileSaver.java;h=45af83e0a7cac09286848f55be8508b3d2e87c9e;hp=0000000000000000000000000000000000000000;hb=ce6f2161b8596f7018d6a76bff79bc9e571f35fd;hpb=2d8cb72e84d5cc1089ce77baf1e34ea3ea2f8465 diff --git a/src/tim/prune/save/FileSaver.java b/src/tim/prune/save/FileSaver.java new file mode 100644 index 0000000..45af83e --- /dev/null +++ b/src/tim/prune/save/FileSaver.java @@ -0,0 +1,658 @@ +package tim.prune.save; + +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +import javax.swing.BorderFactory; +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; +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.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.data.Unit; +import tim.prune.data.UnitSetLibrary; +import tim.prune.load.GenericFileFilter; +import tim.prune.load.OneCharDocument; + +/** + * Class to manage the saving of track data + * as text into a user-specified file + */ +public class FileSaver +{ + private App _app = null; + private JFrame _parentFrame = null; + private JDialog _dialog = null; + private JFileChooser _fileChooser = null; + private JPanel _cards = null; + private JButton _nextButton = null, _backButton = null; + 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 Unit[] UNIT_ALTS = {null, UnitSetLibrary.UNITS_METRES, UnitSetLibrary.UNITS_FEET}; + private static final Timestamp.Format[] FORMAT_TIMES = {Timestamp.Format.ORIGINAL, Timestamp.Format.LOCALE, Timestamp.Format.ISO8601}; + + + /** + * Constructor + * @param inApp application object to inform of success + * @param inParentFrame parent frame + */ + public FileSaver(App inApp, JFrame inParentFrame) + { + _app = inApp; + _parentFrame = inParentFrame; + } + + + /** + * Show the save file dialog + * @param inDefaultDelimiter default delimiter to use + */ + public void showDialog(char inDefaultDelimiter) + { + if (_dialog == null) + { + _dialog = new JDialog(_parentFrame, I18nManager.getText("dialog.saveoptions.title"), true); + _dialog.setLocationRelativeTo(_parentFrame); + _dialog.getContentPane().add(makeDialogComponents()); + _dialog.pack(); + } + // Has the track got media? + final boolean hasMedia = _app.getTrackInfo().getPhotoList().hasCorrelatedPhotos() + || _app.getTrackInfo().getAudioList().hasCorrelatedAudios(); + // Check field list + Track track = _app.getTrackInfo().getTrack(); + FieldList fieldList = track.getFieldList(); + int numFields = fieldList.getNumFields(); + _model = new FieldSelectionTableModel(numFields + (hasMedia ? 1 : 0)); + for (int i=0; i 0) + { + _model.swapItems(row, row - 1); + _table.setRowSelectionInterval(row - 1, row - 1); + } + } + }); + _moveUpButton.setEnabled(false); + updownPanel.add(_moveUpButton); + _moveDownButton = new JButton(I18nManager.getText("button.movedown")); + _moveDownButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) + { + int row = _table.getSelectedRow(); + if (row > -1 && row < (_model.getRowCount() - 1)) + { + _model.swapItems(row, row + 1); + _table.setRowSelectionInterval(row + 1, row + 1); + } + } + }); + _moveDownButton.setEnabled(false); + updownPanel.add(_moveDownButton); + fieldsPanel.add(updownPanel, BorderLayout.EAST); + // enable/disable buttons based on table row selection + _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")); + saveOptionsLabel.setAlignmentX(JLabel.LEFT_ALIGNMENT); + firstCard.add(saveOptionsLabel); + fieldsPanel.setAlignmentX(JPanel.LEFT_ALIGNMENT); + firstCard.add(fieldsPanel); + firstCard.add(Box.createRigidArea(new Dimension(0,10))); + + // delimiter panel + JLabel delimLabel = new JLabel(I18nManager.getText("dialog.delimiter.label")); + delimLabel.setAlignmentX(JLabel.LEFT_ALIGNMENT); + firstCard.add(delimLabel); + JPanel delimsPanel = new JPanel(); + delimsPanel.setLayout(new GridLayout(0, 2)); + delimsPanel.setAlignmentX(JPanel.LEFT_ALIGNMENT); + // radio buttons + _delimiterRadios = new JRadioButton[5]; + _delimiterRadios[0] = new JRadioButton(I18nManager.getText("dialog.delimiter.comma")); + delimsPanel.add(_delimiterRadios[0]); + _delimiterRadios[1] = new JRadioButton(I18nManager.getText("dialog.delimiter.tab")); + delimsPanel.add(_delimiterRadios[1]); + _delimiterRadios[2] = new JRadioButton(I18nManager.getText("dialog.delimiter.semicolon")); + delimsPanel.add(_delimiterRadios[2]); + _delimiterRadios[3] = new JRadioButton(I18nManager.getText("dialog.delimiter.space")); + delimsPanel.add(_delimiterRadios[3]); + JPanel otherPanel = new JPanel(); + otherPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); + _delimiterRadios[4] = new JRadioButton(I18nManager.getText("dialog.delimiter.other")); + otherPanel.add(_delimiterRadios[4]); + _otherDelimiterText = new JTextField(new OneCharDocument(), null, 2); + otherPanel.add(_otherDelimiterText); + // Group radio buttons + ButtonGroup delimGroup = new ButtonGroup(); + for (int i=0; i<_delimiterRadios.length; i++) + { + delimGroup.add(_delimiterRadios[i]); + } + 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); + JPanel coordsUnitsPanel = new JPanel(); + coordsUnitsPanel.setBorder(BorderFactory.createEtchedBorder()); + coordsUnitsPanel.setLayout(new GridLayout(0, 2)); + _coordUnitsRadios = new JRadioButton[4]; + _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")); + ButtonGroup coordGroup = new ButtonGroup(); + for (int i=0; i<4; i++) + { + coordGroup.add(_coordUnitsRadios[i]); + coordsUnitsPanel.add(_coordUnitsRadios[i]); + _coordUnitsRadios[i].setSelected(i==0); + } + coordsUnitsPanel.setAlignmentX(JPanel.LEFT_ALIGNMENT); + secondCardHolder.add(coordsUnitsPanel); + 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); + JPanel altUnitsPanel = new JPanel(); + altUnitsPanel.setBorder(BorderFactory.createEtchedBorder()); + altUnitsPanel.setLayout(new GridLayout(0, 2)); + _altitudeUnitsRadios = new JRadioButton[3]; + _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(); + for (int i=0; i<3; i++) + { + altGroup.add(_altitudeUnitsRadios[i]); + altUnitsPanel.add(_altitudeUnitsRadios[i]); + _altitudeUnitsRadios[i].setSelected(i==0); + } + 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"); + + // Put together with ok/cancel buttons on the bottom + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT)); + _backButton = new JButton(I18nManager.getText("button.back")); + _backButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) + { + CardLayout cl = (CardLayout) _cards.getLayout(); + cl.previous(_cards); + _backButton.setEnabled(false); + _nextButton.setEnabled(true); + } + }); + _backButton.setEnabled(false); + buttonPanel.add(_backButton); + _nextButton = new JButton(I18nManager.getText("button.next")); + _nextButton.setEnabled(true); + _nextButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) + { + CardLayout cl = (CardLayout) _cards.getLayout(); + cl.next(_cards); + _backButton.setEnabled(true); + _nextButton.setEnabled(false); + } + }); + buttonPanel.add(_nextButton); + JButton okButton = new JButton(I18nManager.getText("button.finish")); + okButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) + { + if (saveToFile()) + { + _dialog.dispose(); + } + } + }); + buttonPanel.add(okButton); + JButton cancelButton = new JButton(I18nManager.getText("button.cancel")); + cancelButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) + { + _dialog.dispose(); + } + }); + buttonPanel.add(cancelButton); + panel.add(buttonPanel, BorderLayout.SOUTH); + 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); + } + + + /** + * Start the save process by choosing the file to save to + * @return true if successful or cancelled, false if failed + */ + private boolean saveToFile() + { + 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.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) + { + 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]; + Unit altitudeUnit = null; + for (int i=0; i<_altitudeUnitsRadios.length; i++) + { + if (_altitudeUnitsRadios[i].isSelected()) { + altitudeUnit = UNIT_ALTS[i]; + } + } + // Get timestamp format + Timestamp.Format 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 + { + // 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()) + { + buffer = new StringBuffer(); + for (int f=0; f=selStart && p<=selEnd)); + if (!savePoint) {continue;} + numSaved++; + firstField = true; + buffer = new StringBuffer(); + for (int f=0; f