altoslib,altosuilib: Bump library version numbers
[fw/altos] / altosui / AltosIdleMonitorUI.java
1 /*
2  * Copyright © 2010 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 package altosui;
20
21 import java.awt.*;
22 import java.awt.event.*;
23 import javax.swing.*;
24 import javax.swing.event.*;
25 import java.io.*;
26 import java.util.concurrent.*;
27 import java.util.Arrays;
28 import org.altusmetrum.altoslib_12.*;
29 import org.altusmetrum.altosuilib_12.*;
30
31 public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDisplay, AltosIdleMonitorListener, DocumentListener {
32         AltosDevice             device;
33         JTabbedPane             pane;
34         AltosPad                pad;
35         AltosInfoTable          flightInfo;
36         AltosFlightStatus       flightStatus;
37         AltosIgnitor            igniter;
38         AltosIdleMonitor        thread;
39         AltosUIMap              sitemap;
40         int                     serial;
41         boolean                 remote;
42         boolean                 has_igniter;
43         boolean                 has_map;
44
45         void stop_display() {
46                 if (thread != null) {
47                         try {
48                                 thread.abort();
49                         } catch (InterruptedException ie) {
50                         }
51                 }
52                 thread = null;
53         }
54
55         void disconnect() {
56                 stop_display();
57         }
58
59         public void reset() {
60                 pad.reset();
61                 flightInfo.clear();
62         }
63
64         public void font_size_changed(int font_size) {
65                 pad.font_size_changed(font_size);
66                 flightInfo.font_size_changed(font_size);
67         }
68
69         public void units_changed(boolean imperial_units) {
70                 pad.units_changed(imperial_units);
71                 flightInfo.units_changed(imperial_units);
72         }
73
74         AltosFlightStatusUpdate status_update;
75
76         public void show(AltosState state, AltosListenerState listener_state) {
77                 status_update.saved_state = state;
78                 if (igniter.should_show(state)) {
79                         if (!has_igniter) {
80                                 pane.add("Ignitor", igniter);
81                                 has_igniter = true;
82                         }
83                 } else {
84                         if (has_igniter) {
85                                 pane.remove(igniter);
86                                 has_igniter = false;
87                         }
88                 }
89                 if (state.gps != null && state.gps.connected) {
90                         if (!has_map) {
91                                 pane.add("Site Map", sitemap);
92                                 has_map = true;
93                         }
94                 } else {
95                         if (has_map) {
96                                 pane.remove(sitemap);
97                                 has_map = false;
98                         }
99                 }
100
101 //              try {
102                         pad.show(state, listener_state);
103                         flightStatus.show(state, listener_state);
104                         flightInfo.show(state, listener_state);
105                         if (has_igniter)
106                                 igniter.show(state, listener_state);
107                         if (has_map)
108                                 sitemap.show(state, listener_state);
109 //              } catch (Exception e) {
110 //                      System.out.print("Show exception " + e);
111 //              }
112         }
113
114         public void update(final AltosState state, final AltosListenerState listener_state) {
115                 Runnable r = new Runnable() {
116                                 public void run() {
117                                         show(state, listener_state);
118                                 }
119                         };
120                 SwingUtilities.invokeLater(r);
121         }
122
123         public void failed() {
124                 Runnable r = new Runnable() {
125                                 public void run() {
126                                         close();
127                                 }
128                         };
129                 SwingUtilities.invokeLater(r);
130         }
131
132         public void error(final String reason) {
133                 Runnable r = new Runnable() {
134                                 public void run() {
135                                         disconnect();
136                                         JOptionPane.showMessageDialog(AltosIdleMonitorUI.this,
137                                                                       reason,
138                                                                       "Error fetching data",
139                                                                       JOptionPane.ERROR_MESSAGE);
140                                 }
141                         };
142                 SwingUtilities.invokeLater(r);
143         }
144
145         Container       bag;
146         AltosUIFreqList frequencies;
147         JTextField      callsign_value;
148
149         /* DocumentListener interface methods */
150         public void changedUpdate(DocumentEvent e) {
151                 if (callsign_value != null) {
152                         String  callsign = callsign_value.getText();
153                         System.out.printf("callsign set to %s\n", callsign);
154                         thread.set_callsign(callsign);
155                         AltosUIPreferences.set_callsign(callsign);
156                 }
157         }
158
159         public void insertUpdate(DocumentEvent e) {
160                 changedUpdate(e);
161         }
162
163         public void removeUpdate(DocumentEvent e) {
164                 changedUpdate(e);
165         }
166
167         void idle_exception(JFrame owner, Exception e) {
168                 if (e instanceof FileNotFoundException) {
169                         JOptionPane.showMessageDialog(owner,
170                                                       ((FileNotFoundException) e).getMessage(),
171                                                       "Cannot open target device",
172                                                       JOptionPane.ERROR_MESSAGE);
173                 } else if (e instanceof AltosSerialInUseException) {
174                         JOptionPane.showMessageDialog(owner,
175                                                       String.format("Device \"%s\" already in use",
176                                                                     device.toShortString()),
177                                                       "Device in use",
178                                                       JOptionPane.ERROR_MESSAGE);
179                 } else if (e instanceof IOException) {
180                         IOException ee = (IOException) e;
181                         JOptionPane.showMessageDialog(owner,
182                                                       device.toShortString(),
183                                                       ee.getLocalizedMessage(),
184                                                       JOptionPane.ERROR_MESSAGE);
185                 } else {
186                         JOptionPane.showMessageDialog(owner,
187                                                       String.format("Connection to \"%s\" failed",
188                                                                     device.toShortString()),
189                                                       "Connection Failed",
190                                                       JOptionPane.ERROR_MESSAGE);
191                 }
192         }
193
194         private void close() {
195                 try {
196                         disconnect();
197                 } catch (Exception ex) {
198                         System.out.printf("Exception %s\n", ex.toString());
199                         for (StackTraceElement el : ex.getStackTrace())
200                                 System.out.printf("%s\n", el.toString());
201                 }
202                 setVisible(false);
203                 dispose();
204                 AltosUIPreferences.unregister_font_listener(AltosIdleMonitorUI.this);
205         }
206
207         public AltosIdleMonitorUI(JFrame in_owner)
208                 throws FileNotFoundException, TimeoutException, InterruptedException {
209
210                 device = AltosDeviceUIDialog.show(in_owner, Altos.product_any);
211                 remote = false;
212                 if (!device.matchProduct(Altos.product_altimeter))
213                         remote = true;
214
215                 serial = device.getSerial();
216
217                 AltosSerial link;
218                 try {
219                         link = new AltosSerial(device);
220                 } catch (Exception ex) {
221                         idle_exception(in_owner, ex);
222                         return;
223                 }
224                 link.set_frame(this);
225
226                 /* We let the user set the freq/callsign, so don't bother with the cancel dialog */
227                 link.set_cancel_enable(false);
228
229                 bag = getContentPane();
230                 bag.setLayout(new GridBagLayout());
231
232                 setTitle(String.format("AltOS %s", device.toShortString()));
233
234                 /* Stick frequency selector at top of table for telemetry monitoring */
235                 if (remote && serial >= 0) {
236                         set_inset(3);
237
238                         // Frequency menu
239                         frequencies = new AltosUIFreqList(AltosUIPreferences.frequency(serial));
240                         frequencies.addActionListener(new ActionListener() {
241                                         public void actionPerformed(ActionEvent e) {
242                                                 double frequency = frequencies.frequency();
243                                                 thread.set_frequency(frequency);
244                                                 AltosUIPreferences.set_frequency(device.getSerial(),
245                                                                                frequency);
246                                         }
247                         });
248                         bag.add (frequencies, constraints(0, 1));
249                         bag.add (new JLabel("Callsign:"), constraints(1, 1));
250                         /* Add callsign configuration */
251                         callsign_value = new JTextField(AltosUIPreferences.callsign());
252                         callsign_value.getDocument().addDocumentListener(this);
253                         callsign_value.setToolTipText("Callsign sent in packet mode");
254                         bag.add(callsign_value, constraints(2, 1, GridBagConstraints.HORIZONTAL));
255                         next_row();
256                 }
257
258                 set_inset(0);
259
260                 /* Flight status is always visible */
261                 flightStatus = new AltosFlightStatus();
262                 bag.add(flightStatus, constraints(0, 4, GridBagConstraints.HORIZONTAL));
263
264                 next_row();
265
266                 /* The rest of the window uses a tabbed pane to
267                  * show one of the alternate data views
268                  */
269                 pane = new JTabbedPane();
270
271                 pad = new AltosPad();
272                 pane.add("Launch Pad", pad);
273
274                 flightInfo = new AltosInfoTable();
275                 pane.add("Table", new JScrollPane(flightInfo));
276
277                 igniter = new AltosIgnitor();
278
279                 sitemap = new AltosUIMap();
280
281                 /* Make the tabbed pane use the rest of the window space */
282                 bag.add(pane, constraints(0, 4, GridBagConstraints.BOTH));
283
284                 setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
285
286                 AltosUIPreferences.register_font_listener(this);
287
288                 addWindowListener(new WindowAdapter() {
289                                 @Override
290                                 public void windowClosing(WindowEvent e) {
291                                         close();
292                                 }
293                         });
294
295                 pack();
296                 setVisible(true);
297
298                 thread = new AltosIdleMonitor((AltosIdleMonitorListener) this, link, (boolean) remote);
299
300                 status_update = new AltosFlightStatusUpdate(flightStatus);
301
302                 new javax.swing.Timer(100, status_update).start();
303
304                 thread.start();
305         }
306 }