1 package tim.prune.load;
3 import java.awt.BorderLayout;
4 import java.awt.Component;
5 import java.awt.FlowLayout;
6 import java.awt.GridLayout;
7 import java.awt.event.ActionEvent;
8 import java.awt.event.ActionListener;
9 import java.awt.event.KeyAdapter;
10 import java.awt.event.KeyEvent;
11 import java.io.BufferedReader;
13 import java.io.InputStreamReader;
14 import java.util.ArrayList;
16 import javax.swing.BorderFactory;
17 import javax.swing.BoxLayout;
18 import javax.swing.JButton;
19 import javax.swing.JCheckBox;
20 import javax.swing.JDialog;
21 import javax.swing.JLabel;
22 import javax.swing.JOptionPane;
23 import javax.swing.JPanel;
24 import javax.swing.JProgressBar;
25 import javax.swing.JTextField;
26 import javax.swing.SwingConstants;
27 import javax.swing.SwingUtilities;
28 import javax.swing.event.ChangeEvent;
29 import javax.swing.event.ChangeListener;
30 import javax.xml.parsers.SAXParser;
31 import javax.xml.parsers.SAXParserFactory;
34 import tim.prune.ExternalTools;
35 import tim.prune.GenericFunction;
36 import tim.prune.I18nManager;
37 import tim.prune.config.Config;
38 import tim.prune.data.Altitude;
39 import tim.prune.data.SourceInfo;
40 import tim.prune.load.xml.XmlFileLoader;
41 import tim.prune.load.xml.XmlHandler;
42 import tim.prune.save.GpxExporter;
45 * Class to manage the loading of GPS data using GpsBabel
47 public class GpsLoader extends GenericFunction implements Runnable
49 private boolean _gpsBabelChecked = false;
50 private JDialog _dialog = null;
51 private JTextField _deviceField = null, _formatField = null;
52 private JCheckBox _waypointCheckbox = null, _trackCheckbox = null;
53 private JCheckBox _saveCheckbox = null;
54 private JButton _okButton = null;
55 private JProgressBar _progressBar = null;
56 private File _saveFile = null;
57 private boolean _cancelled = false;
62 * @param inApp Application object to inform of data load
64 public GpsLoader(App inApp)
69 /** Get the name key */
70 public String getNameKey() {
71 return "function.loadfromgps";
75 * Open the GUI to select options and start the load
79 // Check if gpsbabel looks like it's installed
80 if (_gpsBabelChecked || ExternalTools.isToolInstalled(ExternalTools.TOOL_GPSBABEL)
81 || JOptionPane.showConfirmDialog(_dialog,
82 I18nManager.getText("dialog.gpsload.nogpsbabel"),
83 I18nManager.getText(getNameKey()),
84 JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION)
86 _gpsBabelChecked = true;
90 _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
91 _dialog.setLocationRelativeTo(_parentFrame);
92 _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
93 _dialog.getContentPane().add(makeDialogComponents());
96 // Initialise progress bars, buttons
98 setupProgressBar(true);
99 _dialog.setVisible(true);
105 * @return a panel containing the main dialog components
107 private JPanel makeDialogComponents()
109 JPanel outerPanel = new JPanel();
110 outerPanel.setLayout(new BorderLayout());
111 // Main panel with options etc
112 JPanel mainPanel = new JPanel();
113 mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
115 // text fields for options
116 JPanel gridPanel = new JPanel();
117 gridPanel.setLayout(new GridLayout(0, 2, 10, 3));
118 JLabel deviceLabel = new JLabel(I18nManager.getText("dialog.gpsload.device"));
119 deviceLabel.setHorizontalAlignment(SwingConstants.RIGHT);
120 gridPanel.add(deviceLabel);
121 _deviceField = new JTextField(Config.getConfigString(Config.KEY_GPS_DEVICE), 12);
122 _deviceField.addKeyListener(new KeyAdapter() {
123 public void keyReleased(KeyEvent e)
125 // close dialog if escape pressed
126 if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
131 gridPanel.add(_deviceField);
132 JLabel formatLabel = new JLabel(I18nManager.getText("dialog.gpsload.format"));
133 formatLabel.setHorizontalAlignment(SwingConstants.RIGHT);
134 gridPanel.add(formatLabel);
135 _formatField = new JTextField(Config.getConfigString(Config.KEY_GPS_FORMAT), 12);
136 gridPanel.add(_formatField);
137 gridPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
138 gridPanel.setBorder(BorderFactory.createEmptyBorder(10, 5, 5, 20));
139 mainPanel.add(gridPanel);
142 ChangeListener checkboxListener = new ChangeListener() {
143 public void stateChanged(ChangeEvent e)
148 _waypointCheckbox = new JCheckBox(I18nManager.getText("dialog.gpsload.getwaypoints"), true);
149 _waypointCheckbox.addChangeListener(checkboxListener);
150 _waypointCheckbox.setAlignmentX(Component.CENTER_ALIGNMENT);
151 mainPanel.add(_waypointCheckbox);
152 _trackCheckbox = new JCheckBox(I18nManager.getText("dialog.gpsload.gettracks"), true);
153 _trackCheckbox.addChangeListener(checkboxListener);
154 _trackCheckbox.setAlignmentX(Component.CENTER_ALIGNMENT);
155 mainPanel.add(_trackCheckbox);
156 // Checkbox for immediately saving to file
157 _saveCheckbox = new JCheckBox(I18nManager.getText("dialog.gpsload.save"));
158 _saveCheckbox.setAlignmentX(Component.CENTER_ALIGNMENT);
159 mainPanel.add(_saveCheckbox);
161 // progress bar (initially invisible)
162 _progressBar = new JProgressBar(0, 10);
163 mainPanel.add(_progressBar);
164 outerPanel.add(mainPanel, BorderLayout.NORTH);
166 // Lower panel with ok and cancel buttons
167 JPanel buttonPanel = new JPanel();
168 buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
169 _okButton = new JButton(I18nManager.getText("button.ok"));
170 _okButton.addActionListener(new ActionListener() {
171 public void actionPerformed(ActionEvent e)
173 // start thread to call gpsbabel
175 new Thread(GpsLoader.this).start();
178 buttonPanel.add(_okButton);
179 JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
180 cancelButton.addActionListener(new ActionListener() {
181 public void actionPerformed(ActionEvent e)
187 buttonPanel.add(cancelButton);
188 outerPanel.add(buttonPanel, BorderLayout.SOUTH);
194 * @param inStart true if the dialog is restarting
196 private void setupProgressBar(boolean inStart)
199 _progressBar.setVisible(!inStart);
200 // set indeterminate flags, initial value
201 _progressBar.setIndeterminate(false);
202 _progressBar.setValue(0);
207 * Enable or disable the ok button
209 private void enableOkButton()
211 _okButton.setEnabled(_waypointCheckbox.isSelected() || _trackCheckbox.isSelected());
216 * Run method for performing tasks in separate thread
220 _okButton.setEnabled(false);
221 setupProgressBar(false);
222 if (_waypointCheckbox.isSelected() || _trackCheckbox.isSelected())
224 _progressBar.setIndeterminate(true);
232 _app.showErrorMessageNoLookup(getNameKey(), e.getMessage());
236 setupProgressBar(true);
247 * Execute the call to gpsbabel and pass the results back to the app
249 private void callGpsBabel() throws Exception
251 // Set up command to call gpsbabel
252 final String device = _deviceField.getText().trim();
253 final String format = _formatField.getText().trim();
254 String[] commands = getCommandArray(device, format);
255 // Save GPS settings in config
256 Config.setConfigString(Config.KEY_GPS_DEVICE, device);
257 Config.setConfigString(Config.KEY_GPS_FORMAT, format);
259 String errorMessage = "", errorMessage2 = "";
260 XmlHandler handler = null;
261 Process process = Runtime.getRuntime().exec(commands);
264 if (_saveFile != null)
266 // data is being saved to file, so need to wait for it to finish
268 // try to read error message, if any
270 BufferedReader r = new BufferedReader(new InputStreamReader(process.getErrorStream()));
271 while ((line = r.readLine()) != null) {
272 errorMessage += line + "\n";
274 // Close error stream
277 } catch (Exception e) {}
279 catch (Exception e) {} // couldn't get error message
281 // Trigger it to be loaded by app
282 if (process.exitValue() == 0)
284 SwingUtilities.invokeLater(new Runnable() {
286 ArrayList<File> fileList = new ArrayList<File>();
287 fileList.add(_saveFile);
288 _app.loadDataFiles(fileList);
292 else if (errorMessage.length() > 0) {
293 throw new Exception(errorMessage);
295 else throw new Exception(I18nManager.getText("error.gpsload.unknown"));
299 // Pass input stream to try to parse the xml
302 XmlFileLoader xmlLoader = new XmlFileLoader(_app);
303 SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
304 saxParser.parse(process.getInputStream(), xmlLoader);
305 handler = xmlLoader.getHandler();
306 if (handler == null) {
307 errorMessage = "Null handler";
310 catch (Exception e) {
311 errorMessage = e.getMessage();
314 // Read the error stream to see if there's a better error message there
315 BufferedReader r = new BufferedReader(new InputStreamReader(process.getErrorStream()));
316 while ((line = r.readLine()) != null) {
317 errorMessage2 += line + "\n";
319 // Close error stream
322 } catch (Exception e) {}
324 if (errorMessage2.length() > 0) {errorMessage = errorMessage2;}
325 if (errorMessage.length() > 0) {throw new Exception(errorMessage);}
327 // Send data back to app
328 _app.informDataLoaded(handler.getFieldArray(), handler.getDataArray(), Altitude.Format.METRES,
329 new SourceInfo(_deviceField.getText(), SourceInfo.FILE_TYPE.GPSBABEL));
335 * Get the commands to call
336 * @param inDevice device name to use
337 * @param inFormat format to use
338 * @return String array containing commands
340 private String[] getCommandArray(String inDevice, String inFormat)
342 String[] commands = null;
343 final String command = Config.getConfigString(Config.KEY_GPSBABEL_PATH);
344 final boolean loadWaypoints = _waypointCheckbox.isSelected();
345 final boolean loadTrack = _trackCheckbox.isSelected();
346 if (loadWaypoints && loadTrack) {
347 // Both waypoints and track points selected
348 commands = new String[] {command, "-w", "-t", "-i", inFormat,
349 "-f", inDevice, "-o", "gpx", "-F", "-"};
353 // Only waypoints OR track points selected
354 commands = new String[] {command, "-w", "-i", inFormat,
355 "-f", inDevice, "-o", "gpx", "-F", "-"};
360 // Do we want to save the gpx straight to file?
361 if (_saveCheckbox.isSelected()) {
362 // Select file to save to
363 _saveFile = GpxExporter.chooseGpxFile(_parentFrame);
364 if (_saveFile != null) {
365 commands[commands.length-1] = _saveFile.getAbsolutePath();