]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/load/GpsLoader.java
c0ca7549807b9f354176d9bb5793610a5275753c
[GpsPrune.git] / tim / prune / load / GpsLoader.java
1 package tim.prune.load;
2
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;
12 import java.io.File;
13 import java.io.InputStreamReader;
14 import java.util.ArrayList;
15
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;
32
33 import tim.prune.App;
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;
43
44 /**
45  * Class to manage the loading of GPS data using GpsBabel
46  */
47 public class GpsLoader extends GenericFunction implements Runnable
48 {
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;
58
59
60         /**
61          * Constructor
62          * @param inApp Application object to inform of data load
63          */
64         public GpsLoader(App inApp)
65         {
66                 super(inApp);
67         }
68
69         /** Get the name key */
70         public String getNameKey() {
71                 return "function.loadfromgps";
72         }
73
74         /**
75          * Open the GUI to select options and start the load
76          */
77         public void begin()
78         {
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)
85                 {
86                         _gpsBabelChecked = true;
87                         // Make dialog window
88                         if (_dialog == null)
89                         {
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());
94                                 _dialog.pack();
95                         }
96                         // Initialise progress bars, buttons
97                         enableOkButton();
98                         setupProgressBar(true);
99                         _dialog.setVisible(true);
100                 }
101         }
102
103
104         /**
105          * @return a panel containing the main dialog components
106          */
107         private JPanel makeDialogComponents()
108         {
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));
114
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)
124                         {
125                                 // close dialog if escape pressed
126                                 if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
127                                         _dialog.dispose();
128                                 }
129                         }
130                 });
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);
140
141                 // checkboxes
142                 ChangeListener checkboxListener = new ChangeListener() {
143                         public void stateChanged(ChangeEvent e)
144                         {
145                                 enableOkButton();
146                         }
147                 };
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);
160
161                 // progress bar (initially invisible)
162                 _progressBar = new JProgressBar(0, 10);
163                 mainPanel.add(_progressBar);
164                 outerPanel.add(mainPanel, BorderLayout.NORTH);
165
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)
172                         {
173                                 // start thread to call gpsbabel
174                                 _cancelled = false;
175                                 new Thread(GpsLoader.this).start();
176                         }
177                 });
178                 buttonPanel.add(_okButton);
179                 JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
180                 cancelButton.addActionListener(new ActionListener() {
181                         public void actionPerformed(ActionEvent e)
182                         {
183                                 _cancelled = true;
184                                 _dialog.dispose();
185                         }
186                 });
187                 buttonPanel.add(cancelButton);
188                 outerPanel.add(buttonPanel, BorderLayout.SOUTH);
189                 return outerPanel;
190         }
191
192
193         /**
194          * @param inStart true if the dialog is restarting
195          */
196         private void setupProgressBar(boolean inStart)
197         {
198                 // set visibility
199                 _progressBar.setVisible(!inStart);
200                 // set indeterminate flags, initial value
201                 _progressBar.setIndeterminate(false);
202                 _progressBar.setValue(0);
203         }
204
205
206         /**
207          * Enable or disable the ok button
208          */
209         private void enableOkButton()
210         {
211                 _okButton.setEnabled(_waypointCheckbox.isSelected() || _trackCheckbox.isSelected());
212         }
213
214
215         /**
216          * Run method for performing tasks in separate thread
217          */
218         public void run()
219         {
220                 _okButton.setEnabled(false);
221                 setupProgressBar(false);
222                 if (_waypointCheckbox.isSelected() || _trackCheckbox.isSelected())
223                 {
224                         _progressBar.setIndeterminate(true);
225                         _saveFile = null;
226                         try
227                         {
228                                 callGpsBabel();
229                         }
230                         catch (Exception e)
231                         {
232                                 _app.showErrorMessageNoLookup(getNameKey(), e.getMessage());
233                                 _cancelled = true;
234                         }
235                 }
236                 setupProgressBar(true);
237                 enableOkButton();
238
239                 // Close dialog
240                 if (!_cancelled) {
241                         _dialog.dispose();
242                 }
243         }
244
245
246         /**
247          * Execute the call to gpsbabel and pass the results back to the app
248          */
249         private void callGpsBabel() throws Exception
250         {
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);
258
259                 String errorMessage = "", errorMessage2 = "";
260                 XmlHandler handler = null;
261                 Process process = Runtime.getRuntime().exec(commands);
262                 String line = null;
263
264                 if (_saveFile != null)
265                 {
266                         // data is being saved to file, so need to wait for it to finish
267                         process.waitFor();
268                         // try to read error message, if any
269                         try {
270                                 BufferedReader r = new BufferedReader(new InputStreamReader(process.getErrorStream()));
271                                 while ((line = r.readLine()) != null) {
272                                         errorMessage += line + "\n";
273                                 }
274                                 // Close error stream
275                                 try {
276                                         r.close();
277                                 } catch (Exception e) {}
278                         }
279                         catch (Exception e) {} // couldn't get error message
280
281                         // Trigger it to be loaded by app
282                         if (process.exitValue() == 0)
283                         {
284                                 SwingUtilities.invokeLater(new Runnable() {
285                                         public void run() {
286                                                 ArrayList<File> fileList = new ArrayList<File>();
287                                                 fileList.add(_saveFile);
288                                                 _app.loadDataFiles(fileList);
289                                         }
290                                 });
291                         }
292                         else if (errorMessage.length() > 0) {
293                                 throw new Exception(errorMessage);
294                         }
295                         else throw new Exception(I18nManager.getText("error.gpsload.unknown"));
296                 }
297                 else
298                 {
299                         // Pass input stream to try to parse the xml
300                         try
301                         {
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";
308                                 }
309                         }
310                         catch (Exception e) {
311                                 errorMessage = e.getMessage();
312                         }
313
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";
318                         }
319                         // Close error stream
320                         try {
321                                 r.close();
322                         } catch (Exception e) {}
323
324                         if (errorMessage2.length() > 0) {errorMessage = errorMessage2;}
325                         if (errorMessage.length() > 0) {throw new Exception(errorMessage);}
326
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));
330                 }
331         }
332
333
334         /**
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
339          */
340         private String[] getCommandArray(String inDevice, String inFormat)
341         {
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", "-"};
350                 }
351                 else
352                 {
353                         // Only waypoints OR track points selected
354                         commands = new String[] {command, "-w", "-i", inFormat,
355                                 "-f", inDevice, "-o", "gpx", "-F", "-"};
356                         if (loadTrack) {
357                                 commands[1] = "-t";
358                         }
359                 }
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();
366                         }
367                 }
368                 return commands;
369         }
370 }