Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
[fw/altos] / telegps / TeleGPSConfigUI.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; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 package org.altusmetrum.telegps;
19
20 import java.text.*;
21 import java.awt.*;
22 import java.awt.event.*;
23 import javax.swing.*;
24 import javax.swing.event.*;
25 import org.altusmetrum.altoslib_7.*;
26 import org.altusmetrum.altosuilib_7.*;
27
28 public class TeleGPSConfigUI
29         extends AltosUIDialog
30         implements ActionListener, ItemListener, DocumentListener, AltosConfigValues, AltosUnitsListener
31 {
32
33         Container               pane;
34         JLabel                  product_label;
35         JLabel                  version_label;
36         JLabel                  serial_label;
37         JLabel                  frequency_label;
38         JLabel                  radio_calibration_label;
39         JLabel                  radio_frequency_label;
40         JLabel                  radio_enable_label;
41         JLabel                  rate_label;
42         JLabel                  aprs_interval_label;
43         JLabel                  aprs_ssid_label;
44         JLabel                  aprs_format_label;
45         JLabel                  flight_log_max_label;
46         JLabel                  callsign_label;
47         JLabel                  tracker_motion_label;
48         JLabel                  tracker_interval_label;
49
50         public boolean          dirty;
51
52         JFrame                  owner;
53         JLabel                  product_value;
54         JLabel                  version_value;
55         JLabel                  serial_value;
56         AltosUIFreqList         radio_frequency_value;
57         JTextField              radio_calibration_value;
58         JRadioButton            radio_enable_value;
59         AltosUIRateList         rate_value;
60         JComboBox<String>       aprs_interval_value;
61         JComboBox<Integer>      aprs_ssid_value;
62         JComboBox<String>       aprs_format_value;
63         JComboBox<String>       flight_log_max_value;
64         JTextField              callsign_value;
65         JComboBox<String>       tracker_motion_value;
66         JComboBox<String>       tracker_interval_value;
67
68         JButton                 save;
69         JButton                 reset;
70         JButton                 reboot;
71         JButton                 close;
72
73         ActionListener          listener;
74
75         static String[]         aprs_interval_values = {
76                 "Disabled",
77                 "2",
78                 "5",
79                 "10"
80         };
81
82         static Integer[]        aprs_ssid_values = {
83                 0, 1, 2 ,3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
84         };
85
86         static String[]         tracker_motion_values_m = {
87                 "2",
88                 "5",
89                 "10",
90                 "25",
91         };
92
93         static String[]         tracker_motion_values_ft = {
94                 "5",
95                 "20",
96                 "50",
97                 "100"
98         };
99
100         static String[]         tracker_interval_values = {
101                 "1",
102                 "2",
103                 "5",
104                 "10"
105         };
106
107         /* A window listener to catch closing events and tell the config code */
108         class ConfigListener extends WindowAdapter {
109                 TeleGPSConfigUI ui;
110
111                 public ConfigListener(TeleGPSConfigUI this_ui) {
112                         ui = this_ui;
113                 }
114
115                 public void windowClosing(WindowEvent e) {
116                         ui.actionPerformed(new ActionEvent(e.getSource(),
117                                                            ActionEvent.ACTION_PERFORMED,
118                                                            "Close"));
119                 }
120         }
121
122         public void set_pyros(AltosPyro[] new_pyros) {
123         }
124
125         public AltosPyro[] pyros() {
126                 return null;
127         }
128
129         public void set_pyro_firing_time(double new_pyro_firing_time) {
130         }
131
132         public double pyro_firing_time() {
133                 return -1;
134         }
135
136         boolean is_telemetrum() {
137                 String  product = product_value.getText();
138                 return product != null && product.startsWith("TeleGPS");
139         }
140
141         void set_radio_calibration_tool_tip() {
142                 if (radio_calibration_value.isEnabled())
143                         radio_calibration_value.setToolTipText("Tune radio output to match desired frequency");
144                 else
145                         radio_calibration_value.setToolTipText("Cannot tune radio while connected over packet mode");
146         }
147
148         void set_radio_enable_tool_tip() {
149                 if (radio_enable_value.isEnabled())
150                         radio_enable_value.setToolTipText("Enable/Disable telemetry and RDF transmissions");
151                 else
152                         radio_enable_value.setToolTipText("Firmware version does not support disabling radio");
153         }
154
155         void set_rate_tool_tip() {
156                 if (rate_value.isEnabled())
157                         rate_value.setToolTipText("Select telemetry baud rate");
158                 else
159                         rate_value.setToolTipText("Firmware version does not support variable telemetry rates");
160         }
161
162         void set_aprs_interval_tool_tip() {
163                 if (aprs_interval_value.isEnabled())
164                         aprs_interval_value.setToolTipText("Enable APRS and set the interval between APRS reports");
165                 else
166                         aprs_interval_value.setToolTipText("Hardware doesn't support APRS");
167         }
168
169         void set_aprs_ssid_tool_tip() {
170                 if (aprs_ssid_value.isEnabled())
171                         aprs_ssid_value.setToolTipText("Set the APRS SSID (secondary station identifier)");
172                 else if (aprs_ssid_value.isEnabled())
173                         aprs_ssid_value.setToolTipText("Software version doesn't support setting the APRS SSID");
174                 else
175                         aprs_ssid_value.setToolTipText("Hardware doesn't support APRS");
176         }
177
178         void set_aprs_format_tool_tip() {
179                 if (aprs_format_value.isEnabled())
180                         aprs_format_value.setToolTipText("Set the APRS format (compressed/uncompressed)");
181                 else if (aprs_format_value.isEnabled())
182                         aprs_format_value.setToolTipText("Software version doesn't support setting the APRS format");
183                 else
184                         aprs_format_value.setToolTipText("Hardware doesn't support APRS");
185         }
186
187         void set_flight_log_max_tool_tip() {
188                 if (flight_log_max_value.isEnabled())
189                         flight_log_max_value.setToolTipText("Size reserved for each flight log (in kB)");
190                 else
191                         flight_log_max_value.setToolTipText("Cannot set max value with flight logs in memory");
192         }
193
194         /* Build the UI using a grid bag */
195         public TeleGPSConfigUI(JFrame in_owner) {
196                 super (in_owner, "Configure Device", false);
197
198                 owner = in_owner;
199                 GridBagConstraints c;
200                 int row = 0;
201
202                 Insets il = new Insets(4,4,4,4);
203                 Insets ir = new Insets(4,4,4,4);
204
205                 pane = getContentPane();
206                 pane.setLayout(new GridBagLayout());
207
208                 /* Product */
209                 c = new GridBagConstraints();
210                 c.gridx = 0; c.gridy = row;
211                 c.gridwidth = 4;
212                 c.fill = GridBagConstraints.NONE;
213                 c.anchor = GridBagConstraints.LINE_START;
214                 c.insets = il;
215                 product_label = new JLabel("Product:");
216                 pane.add(product_label, c);
217
218                 c = new GridBagConstraints();
219                 c.gridx = 4; c.gridy = row;
220                 c.gridwidth = 4;
221                 c.fill = GridBagConstraints.HORIZONTAL;
222                 c.weightx = 1;
223                 c.anchor = GridBagConstraints.LINE_START;
224                 c.insets = ir;
225                 product_value = new JLabel("");
226                 pane.add(product_value, c);
227                 row++;
228
229                 /* Version */
230                 c = new GridBagConstraints();
231                 c.gridx = 0; c.gridy = row;
232                 c.gridwidth = 4;
233                 c.fill = GridBagConstraints.NONE;
234                 c.anchor = GridBagConstraints.LINE_START;
235                 c.insets = il;
236                 c.ipady = 5;
237                 version_label = new JLabel("Software version:");
238                 pane.add(version_label, c);
239
240                 c = new GridBagConstraints();
241                 c.gridx = 4; c.gridy = row;
242                 c.gridwidth = 4;
243                 c.fill = GridBagConstraints.HORIZONTAL;
244                 c.weightx = 1;
245                 c.anchor = GridBagConstraints.LINE_START;
246                 c.insets = ir;
247                 c.ipady = 5;
248                 version_value = new JLabel("");
249                 pane.add(version_value, c);
250                 row++;
251
252                 /* Serial */
253                 c = new GridBagConstraints();
254                 c.gridx = 0; c.gridy = row;
255                 c.gridwidth = 4;
256                 c.fill = GridBagConstraints.NONE;
257                 c.anchor = GridBagConstraints.LINE_START;
258                 c.insets = il;
259                 c.ipady = 5;
260                 serial_label = new JLabel("Serial:");
261                 pane.add(serial_label, c);
262
263                 c = new GridBagConstraints();
264                 c.gridx = 4; c.gridy = row;
265                 c.gridwidth = 4;
266                 c.fill = GridBagConstraints.HORIZONTAL;
267                 c.weightx = 1;
268                 c.anchor = GridBagConstraints.LINE_START;
269                 c.insets = ir;
270                 c.ipady = 5;
271                 serial_value = new JLabel("");
272                 pane.add(serial_value, c);
273                 row++;
274
275                 /* Frequency */
276                 c = new GridBagConstraints();
277                 c.gridx = 0; c.gridy = row;
278                 c.gridwidth = 4;
279                 c.fill = GridBagConstraints.NONE;
280                 c.anchor = GridBagConstraints.LINE_START;
281                 c.insets = il;
282                 c.ipady = 5;
283                 radio_frequency_label = new JLabel("Frequency:");
284                 pane.add(radio_frequency_label, c);
285
286                 c = new GridBagConstraints();
287                 c.gridx = 4; c.gridy = row;
288                 c.gridwidth = 4;
289                 c.fill = GridBagConstraints.HORIZONTAL;
290                 c.weightx = 1;
291                 c.anchor = GridBagConstraints.LINE_START;
292                 c.insets = ir;
293                 c.ipady = 5;
294                 radio_frequency_value = new AltosUIFreqList();
295                 radio_frequency_value.addItemListener(this);
296                 pane.add(radio_frequency_value, c);
297                 radio_frequency_value.setToolTipText("Telemetry, RDF and packet frequency");
298                 row++;
299
300                 /* Radio Calibration */
301                 c = new GridBagConstraints();
302                 c.gridx = 0; c.gridy = row;
303                 c.gridwidth = 4;
304                 c.fill = GridBagConstraints.NONE;
305                 c.anchor = GridBagConstraints.LINE_START;
306                 c.insets = il;
307                 c.ipady = 5;
308                 radio_calibration_label = new JLabel("RF Calibration:");
309                 pane.add(radio_calibration_label, c);
310
311                 c = new GridBagConstraints();
312                 c.gridx = 4; c.gridy = row;
313                 c.gridwidth = 4;
314                 c.fill = GridBagConstraints.HORIZONTAL;
315                 c.weightx = 1;
316                 c.anchor = GridBagConstraints.LINE_START;
317                 c.insets = ir;
318                 c.ipady = 5;
319                 radio_calibration_value = new JTextField(String.format("%d", 1186611));
320                 radio_calibration_value.getDocument().addDocumentListener(this);
321                 pane.add(radio_calibration_value, c);
322                 set_radio_calibration_tool_tip();
323                 row++;
324
325                 /* Radio Enable */
326                 c = new GridBagConstraints();
327                 c.gridx = 0; c.gridy = row;
328                 c.gridwidth = 4;
329                 c.fill = GridBagConstraints.NONE;
330                 c.anchor = GridBagConstraints.LINE_START;
331                 c.insets = il;
332                 c.ipady = 5;
333                 radio_enable_label = new JLabel("Telemetry/RDF/APRS Enable:");
334                 pane.add(radio_enable_label, c);
335
336                 c = new GridBagConstraints();
337                 c.gridx = 4; c.gridy = row;
338                 c.gridwidth = 4;
339                 c.fill = GridBagConstraints.HORIZONTAL;
340                 c.weightx = 1;
341                 c.anchor = GridBagConstraints.LINE_START;
342                 c.insets = ir;
343                 c.ipady = 5;
344                 radio_enable_value = new JRadioButton("Enabled");
345                 radio_enable_value.addItemListener(this);
346                 pane.add(radio_enable_value, c);
347                 set_radio_enable_tool_tip();
348                 row++;
349
350                 /* Telemetry Rate */
351                 c = new GridBagConstraints();
352                 c.gridx = 0; c.gridy = row;
353                 c.gridwidth = 4;
354                 c.fill = GridBagConstraints.NONE;
355                 c.anchor = GridBagConstraints.LINE_START;
356                 c.insets = il;
357                 c.ipady = 5;
358                 rate_label = new JLabel("Telemetry baud rate:");
359                 pane.add(rate_label, c);
360
361                 c = new GridBagConstraints();
362                 c.gridx = 4; c.gridy = row;
363                 c.gridwidth = 4;
364                 c.fill = GridBagConstraints.HORIZONTAL;
365                 c.weightx = 1;
366                 c.anchor = GridBagConstraints.LINE_START;
367                 c.insets = ir;
368                 c.ipady = 5;
369                 rate_value = new AltosUIRateList();
370                 rate_value.addItemListener(this);
371                 pane.add(rate_value, c);
372                 set_rate_tool_tip();
373                 row++;
374
375                 /* APRS interval */
376                 c = new GridBagConstraints();
377                 c.gridx = 0; c.gridy = row;
378                 c.gridwidth = 4;
379                 c.fill = GridBagConstraints.NONE;
380                 c.anchor = GridBagConstraints.LINE_START;
381                 c.insets = il;
382                 c.ipady = 5;
383                 aprs_interval_label = new JLabel("APRS Interval(s):");
384                 pane.add(aprs_interval_label, c);
385
386                 c = new GridBagConstraints();
387                 c.gridx = 4; c.gridy = row;
388                 c.gridwidth = 4;
389                 c.fill = GridBagConstraints.HORIZONTAL;
390                 c.weightx = 1;
391                 c.anchor = GridBagConstraints.LINE_START;
392                 c.insets = ir;
393                 c.ipady = 5;
394                 aprs_interval_value = new JComboBox<String>(aprs_interval_values);
395                 aprs_interval_value.setEditable(true);
396                 aprs_interval_value.addItemListener(this);
397                 pane.add(aprs_interval_value, c);
398                 set_aprs_interval_tool_tip();
399                 row++;
400
401                 /* APRS SSID */
402                 c = new GridBagConstraints();
403                 c.gridx = 0; c.gridy = row;
404                 c.gridwidth = 4;
405                 c.fill = GridBagConstraints.NONE;
406                 c.anchor = GridBagConstraints.LINE_START;
407                 c.insets = il;
408                 c.ipady = 5;
409                 aprs_ssid_label = new JLabel("APRS SSID:");
410                 pane.add(aprs_ssid_label, c);
411
412                 c = new GridBagConstraints();
413                 c.gridx = 4; c.gridy = row;
414                 c.gridwidth = 4;
415                 c.fill = GridBagConstraints.HORIZONTAL;
416                 c.weightx = 1;
417                 c.anchor = GridBagConstraints.LINE_START;
418                 c.insets = ir;
419                 c.ipady = 5;
420                 aprs_ssid_value = new JComboBox<Integer>(aprs_ssid_values);
421                 aprs_ssid_value.setEditable(false);
422                 aprs_ssid_value.addItemListener(this);
423                 aprs_ssid_value.setMaximumRowCount(aprs_ssid_values.length);
424                 pane.add(aprs_ssid_value, c);
425                 set_aprs_ssid_tool_tip();
426                 row++;
427
428                 /* APRS format */
429                 c = new GridBagConstraints();
430                 c.gridx = 0; c.gridy = row;
431                 c.gridwidth = 4;
432                 c.fill = GridBagConstraints.NONE;
433                 c.anchor = GridBagConstraints.LINE_START;
434                 c.insets = il;
435                 c.ipady = 5;
436                 aprs_format_label = new JLabel("APRS format:");
437                 pane.add(aprs_format_label, c);
438
439                 c = new GridBagConstraints();
440                 c.gridx = 4; c.gridy = row;
441                 c.gridwidth = 4;
442                 c.fill = GridBagConstraints.HORIZONTAL;
443                 c.weightx = 1;
444                 c.anchor = GridBagConstraints.LINE_START;
445                 c.insets = ir;
446                 c.ipady = 5;
447                 aprs_format_value = new JComboBox<String>(AltosLib.ao_aprs_format_name);
448                 aprs_format_value.setEditable(false);
449                 aprs_format_value.addItemListener(this);
450                 aprs_format_value.setMaximumRowCount(AltosLib.ao_aprs_format_name.length);
451                 pane.add(aprs_format_value, c);
452                 set_aprs_format_tool_tip();
453                 row++;
454
455                 /* Callsign */
456                 c = new GridBagConstraints();
457                 c.gridx = 0; c.gridy = row;
458                 c.gridwidth = 4;
459                 c.fill = GridBagConstraints.NONE;
460                 c.anchor = GridBagConstraints.LINE_START;
461                 c.insets = il;
462                 c.ipady = 5;
463                 callsign_label = new JLabel("Callsign:");
464                 pane.add(callsign_label, c);
465
466                 c = new GridBagConstraints();
467                 c.gridx = 4; c.gridy = row;
468                 c.gridwidth = 4;
469                 c.fill = GridBagConstraints.HORIZONTAL;
470                 c.weightx = 1;
471                 c.anchor = GridBagConstraints.LINE_START;
472                 c.insets = ir;
473                 c.ipady = 5;
474                 callsign_value = new JTextField(AltosUIPreferences.callsign());
475                 callsign_value.getDocument().addDocumentListener(this);
476                 pane.add(callsign_value, c);
477                 callsign_value.setToolTipText("Callsign reported in telemetry data");
478                 row++;
479
480                 /* Flight log max */
481                 c = new GridBagConstraints();
482                 c.gridx = 0; c.gridy = row;
483                 c.gridwidth = 4;
484                 c.fill = GridBagConstraints.NONE;
485                 c.anchor = GridBagConstraints.LINE_START;
486                 c.insets = il;
487                 c.ipady = 5;
488                 flight_log_max_label = new JLabel("Maximum Log Size (kB):");
489                 pane.add(flight_log_max_label, c);
490
491                 c = new GridBagConstraints();
492                 c.gridx = 4; c.gridy = row;
493                 c.gridwidth = 4;
494                 c.fill = GridBagConstraints.HORIZONTAL;
495                 c.weightx = 1;
496                 c.anchor = GridBagConstraints.LINE_START;
497                 c.insets = ir;
498                 c.ipady = 5;
499                 flight_log_max_value = new JComboBox<String>();
500                 flight_log_max_value.setEditable(true);
501                 flight_log_max_value.addItemListener(this);
502                 pane.add(flight_log_max_value, c);
503                 set_flight_log_max_tool_tip();
504                 row++;
505
506                 /* Tracker triger horiz distances */
507                 c = new GridBagConstraints();
508                 c.gridx = 0; c.gridy = row;
509                 c.gridwidth = 4;
510                 c.fill = GridBagConstraints.NONE;
511                 c.anchor = GridBagConstraints.LINE_START;
512                 c.insets = il;
513                 c.ipady = 5;
514                 tracker_motion_label = new JLabel(get_tracker_motion_label());
515                 pane.add(tracker_motion_label, c);
516
517                 c = new GridBagConstraints();
518                 c.gridx = 4; c.gridy = row;
519                 c.gridwidth = 4;
520                 c.fill = GridBagConstraints.HORIZONTAL;
521                 c.weightx = 1;
522                 c.anchor = GridBagConstraints.LINE_START;
523                 c.insets = ir;
524                 c.ipady = 5;
525                 tracker_motion_value = new JComboBox<String>(tracker_motion_values());
526                 tracker_motion_value.setEditable(true);
527                 tracker_motion_value.addItemListener(this);
528                 pane.add(tracker_motion_value, c);
529                 row++;
530
531                 /* Tracker triger vert distances */
532                 c = new GridBagConstraints();
533                 c.gridx = 0; c.gridy = row;
534                 c.gridwidth = 4;
535                 c.fill = GridBagConstraints.NONE;
536                 c.anchor = GridBagConstraints.LINE_START;
537                 c.insets = il;
538                 c.ipady = 5;
539                 tracker_interval_label = new JLabel("Position Reporting Interval (s):");
540                 pane.add(tracker_interval_label, c);
541
542                 c = new GridBagConstraints();
543                 c.gridx = 4; c.gridy = row;
544                 c.gridwidth = 4;
545                 c.fill = GridBagConstraints.HORIZONTAL;
546                 c.weightx = 1;
547                 c.anchor = GridBagConstraints.LINE_START;
548                 c.insets = ir;
549                 c.ipady = 5;
550                 tracker_interval_value = new JComboBox<String>(tracker_interval_values);
551                 tracker_interval_value.setEditable(true);
552                 tracker_interval_value.addItemListener(this);
553                 pane.add(tracker_interval_value, c);
554                 set_tracker_tool_tip();
555                 row++;
556
557                 /* Buttons */
558                 c = new GridBagConstraints();
559                 c.gridx = 0; c.gridy = row;
560                 c.gridwidth = 2;
561                 c.fill = GridBagConstraints.NONE;
562                 c.anchor = GridBagConstraints.LINE_START;
563                 c.insets = il;
564                 save = new JButton("Save");
565                 pane.add(save, c);
566                 save.addActionListener(this);
567                 save.setActionCommand("Save");
568
569                 c = new GridBagConstraints();
570                 c.gridx = 2; c.gridy = row;
571                 c.gridwidth = 2;
572                 c.fill = GridBagConstraints.NONE;
573                 c.anchor = GridBagConstraints.CENTER;
574                 c.insets = il;
575                 reset = new JButton("Reset");
576                 pane.add(reset, c);
577                 reset.addActionListener(this);
578                 reset.setActionCommand("Reset");
579
580                 c = new GridBagConstraints();
581                 c.gridx = 4; c.gridy = row;
582                 c.gridwidth = 2;
583                 c.fill = GridBagConstraints.NONE;
584                 c.anchor = GridBagConstraints.CENTER;
585                 c.insets = il;
586                 reboot = new JButton("Reboot");
587                 pane.add(reboot, c);
588                 reboot.addActionListener(this);
589                 reboot.setActionCommand("Reboot");
590
591                 c = new GridBagConstraints();
592                 c.gridx = 6; c.gridy = row;
593                 c.gridwidth = 2;
594                 c.fill = GridBagConstraints.NONE;
595                 c.anchor = GridBagConstraints.LINE_END;
596                 c.insets = il;
597                 close = new JButton("Close");
598                 pane.add(close, c);
599                 close.addActionListener(this);
600                 close.setActionCommand("Close");
601
602                 addWindowListener(new ConfigListener(this));
603                 AltosPreferences.register_units_listener(this);
604         }
605
606         /* Once the initial values are set, the config code will show the dialog */
607         public void make_visible() {
608                 pack();
609                 setLocationRelativeTo(owner);
610                 setVisible(true);
611         }
612
613         /* If any values have been changed, confirm before closing */
614         public boolean check_dirty(String operation) {
615                 if (dirty) {
616                         Object[] options = { String.format("%s anyway", operation), "Keep editing" };
617                         int i;
618                         i = JOptionPane.showOptionDialog(this,
619                                                          String.format("Configuration modified. %s anyway?", operation),
620                                                          "Configuration Modified",
621                                                          JOptionPane.DEFAULT_OPTION,
622                                                          JOptionPane.WARNING_MESSAGE,
623                                                          null, options, options[1]);
624                         if (i != 0)
625                                 return false;
626                 }
627                 return true;
628         }
629
630         void set_dirty() {
631                 dirty = true;
632                 save.setEnabled(true);
633         }
634
635         public void set_clean() {
636                 dirty = false;
637                 save.setEnabled(false);
638         }
639
640         public void dispose() {
641                 AltosPreferences.unregister_units_listener(this);
642                 super.dispose();
643         }
644
645         /* Listen for events from our buttons */
646         public void actionPerformed(ActionEvent e) {
647                 String  cmd = e.getActionCommand();
648
649                 if (cmd.equals("Close") || cmd.equals("Reboot"))
650                         if (!check_dirty(cmd))
651                                 return;
652                 listener.actionPerformed(e);
653                 if (cmd.equals("Close") || cmd.equals("Reboot")) {
654                         setVisible(false);
655                         dispose();
656                 }
657                 set_clean();
658         }
659
660         /* ItemListener interface method */
661         public void itemStateChanged(ItemEvent e) {
662                 set_dirty();
663         }
664
665         /* DocumentListener interface methods */
666         public void changedUpdate(DocumentEvent e) {
667                 set_dirty();
668         }
669
670         public void insertUpdate(DocumentEvent e) {
671                 set_dirty();
672         }
673
674         public void removeUpdate(DocumentEvent e) {
675                 set_dirty();
676         }
677
678         /* Let the config code hook on a listener */
679         public void addActionListener(ActionListener l) {
680                 listener = l;
681         }
682
683         public void units_changed(boolean imperial_units) {
684                 boolean was_dirty = dirty;
685
686                 if (tracker_motion_value.isEnabled()) {
687                         String motion = tracker_motion_value.getSelectedItem().toString();
688                         tracker_motion_label.setText(get_tracker_motion_label());
689                         set_tracker_motion_values();
690                         try {
691                                 int m = (int) (AltosConvert.height.parse_locale(motion, !imperial_units) + 0.5);
692                                 set_tracker_motion(m);
693                         } catch (ParseException pe) {
694                         }
695                 }
696                 if (!was_dirty)
697                         set_clean();
698         }
699
700         /* set and get all of the dialog values */
701         public void set_product(String product) {
702                 radio_frequency_value.set_product(product);
703                 product_value.setText(product);
704                 set_flight_log_max_tool_tip();
705         }
706
707         public void set_version(String version) {
708                 version_value.setText(version);
709         }
710
711         public void set_serial(int serial) {
712                 radio_frequency_value.set_serial(serial);
713                 serial_value.setText(String.format("%d", serial));
714         }
715
716         public void set_altitude_32(int altitude_32) {
717         }
718
719         public void set_main_deploy(int new_main_deploy) {
720         }
721
722         public int main_deploy() {
723                 return -1;
724         }
725
726         public void set_apogee_delay(int new_apogee_delay) { }
727
728         public int apogee_delay() {
729                 return -1;
730         }
731
732         public void set_apogee_lockout(int new_apogee_lockout) { }
733
734         public int apogee_lockout() { return -1; }
735
736         public void set_radio_frequency(double new_radio_frequency) {
737                 radio_frequency_value.set_frequency(new_radio_frequency);
738         }
739
740         public double radio_frequency() {
741                 return radio_frequency_value.frequency();
742         }
743
744         public void set_radio_calibration(int new_radio_calibration) {
745                 radio_calibration_value.setVisible(new_radio_calibration >= 0);
746                 if (new_radio_calibration < 0)
747                         radio_calibration_value.setText("Disabled");
748                 else
749                         radio_calibration_value.setText(String.format("%d", new_radio_calibration));
750         }
751
752         private int parse_int(String name, String s, boolean split) throws AltosConfigDataException {
753                 String v = s;
754                 if (split)
755                         v = s.split("\\s+")[0];
756                 try {
757                         return Integer.parseInt(v);
758                 } catch (NumberFormatException ne) {
759                         throw new AltosConfigDataException("Invalid %s \"%s\"", name, s);
760                 }
761         }
762
763         public int radio_calibration() throws AltosConfigDataException {
764                 return parse_int("radio calibration", radio_calibration_value.getText(), false);
765         }
766
767         public void set_radio_enable(int new_radio_enable) {
768                 if (new_radio_enable >= 0) {
769                         radio_enable_value.setSelected(new_radio_enable > 0);
770                         radio_enable_value.setEnabled(true);
771                 } else {
772                         radio_enable_value.setSelected(true);
773                         radio_enable_value.setVisible(radio_frequency() > 0);
774                         radio_enable_value.setEnabled(false);
775                 }
776                 set_radio_enable_tool_tip();
777         }
778
779         public int radio_enable() {
780                 if (radio_enable_value.isEnabled())
781                         return radio_enable_value.isSelected() ? 1 : 0;
782                 else
783                         return -1;
784         }
785
786         public void set_telemetry_rate(int new_rate) {
787                 rate_value.set_rate(new_rate);
788         }
789
790         public int telemetry_rate() {
791                 return rate_value.rate();
792         }
793
794         public void set_callsign(String new_callsign) {
795                 callsign_value.setVisible(new_callsign != null);
796                 callsign_value.setText(new_callsign);
797         }
798
799         public String callsign() {
800                 return callsign_value.getText();
801         }
802
803         int     flight_log_max_limit;
804         int     flight_log_max;
805
806         public String flight_log_max_label(int flight_log_max) {
807                 if (flight_log_max_limit != 0) {
808                         int     nflight = flight_log_max_limit / flight_log_max;
809                         String  plural = nflight > 1 ? "s" : "";
810
811                         return String.format("%d (%d flight%s)", flight_log_max, nflight, plural);
812                 }
813                 return String.format("%d", flight_log_max);
814         }
815
816         public void set_flight_log_max(int new_flight_log_max) {
817                 flight_log_max_value.setSelectedItem(flight_log_max_label(new_flight_log_max));
818                 flight_log_max = new_flight_log_max;
819                 set_flight_log_max_tool_tip();
820         }
821
822         public void set_flight_log_max_enabled(boolean enable) {
823                 flight_log_max_value.setEnabled(enable);
824                 set_flight_log_max_tool_tip();
825         }
826
827         public int flight_log_max() throws AltosConfigDataException {
828                 return parse_int("flight log max", flight_log_max_value.getSelectedItem().toString(), true);
829         }
830
831         public void set_flight_log_max_limit(int new_flight_log_max_limit) {
832                 flight_log_max_limit = new_flight_log_max_limit;
833                 flight_log_max_value.removeAllItems();
834                 for (int i = 8; i >= 1; i--) {
835                         int     size = flight_log_max_limit / i;
836                         flight_log_max_value.addItem(String.format("%d (%d flights)", size, i));
837                 }
838                 if (flight_log_max != 0)
839                         set_flight_log_max(flight_log_max);
840         }
841
842         public void set_ignite_mode(int new_ignite_mode) { }
843         public int ignite_mode() { return -1; }
844
845
846         public void set_pad_orientation(int new_pad_orientation) { }
847         public int pad_orientation() { return -1; }
848
849         public void set_beep(int new_beep) { }
850
851         public int beep() { return -1; }
852
853         String[] tracker_motion_values() {
854                 if (AltosConvert.imperial_units)
855                         return tracker_motion_values_ft;
856                 else
857                         return tracker_motion_values_m;
858         }
859
860         void set_tracker_motion_values() {
861                 String[]        v = tracker_motion_values();
862                 while (tracker_motion_value.getItemCount() > 0)
863                         tracker_motion_value.removeItemAt(0);
864                 for (int i = 0; i < v.length; i++)
865                         tracker_motion_value.addItem(v[i]);
866                 tracker_motion_value.setMaximumRowCount(v.length);
867         }
868
869         String get_tracker_motion_label() {
870                 return String.format("Logging Trigger Motion (%s):", AltosConvert.height.show_units());
871         }
872
873         void set_tracker_tool_tip() {
874                 if (tracker_motion_value.isEnabled())
875                         tracker_motion_value.setToolTipText("How far the device must move before logging");
876                 else
877                         tracker_motion_value.setToolTipText("This device doesn't disable logging when stationary");
878                 if (tracker_interval_value.isEnabled())
879                         tracker_interval_value.setToolTipText("How often to report GPS position");
880                 else
881                         tracker_interval_value.setToolTipText("This device can't configure interval");
882         }
883
884         public void set_tracker_motion(int tracker_motion) {
885                 if (tracker_motion < 0) {
886                         tracker_motion_value.setEnabled(false);
887                 } else {
888                         tracker_motion_value.setEnabled(true);
889                         tracker_motion_value.setSelectedItem(AltosConvert.height.say(tracker_motion));
890                 }
891         }
892
893         public int tracker_motion() throws AltosConfigDataException {
894                 String str = tracker_motion_value.getSelectedItem().toString();
895                 try {
896                         return (int) (AltosConvert.height.parse_locale(str) + 0.5);
897                 } catch (ParseException pe) {
898                         throw new AltosConfigDataException("invalid tracker motion %s", str);
899                 }
900         }
901
902         public void set_tracker_interval(int tracker_interval) {
903                 if (tracker_interval< 0) {
904                         tracker_interval_value.setEnabled(false);
905                 } else {
906                         tracker_interval_value.setEnabled(true);
907                         tracker_interval_value.setSelectedItem(String.format("%d", tracker_interval));
908                 }
909         }
910
911         public int tracker_interval() throws AltosConfigDataException {
912                 return parse_int ("tracker interval", tracker_interval_value.getSelectedItem().toString(), false);
913         }
914
915         public void set_aprs_interval(int new_aprs_interval) {
916                 String  s;
917
918                 if (new_aprs_interval <= 0)
919                         s = "Disabled";
920                 else
921                         s = Integer.toString(new_aprs_interval);
922                 aprs_interval_value.setSelectedItem(s);
923                 aprs_interval_value.setVisible(new_aprs_interval >= 0);
924                 set_aprs_interval_tool_tip();
925         }
926
927         public int aprs_interval() throws AltosConfigDataException {
928                 String  s = aprs_interval_value.getSelectedItem().toString();
929
930                 if (s.equals("Disabled"))
931                         return 0;
932                 return parse_int("aprs interval", s, false);
933         }
934
935         public void set_aprs_ssid(int new_aprs_ssid) {
936                 aprs_ssid_value.setSelectedItem(Math.max(0,new_aprs_ssid));
937                 aprs_ssid_value.setVisible(new_aprs_ssid >= 0);
938                 set_aprs_ssid_tool_tip();
939         }
940
941         public int aprs_ssid() throws AltosConfigDataException {
942                 Integer i = (Integer) aprs_ssid_value.getSelectedItem();
943                 return i;
944         }
945
946         public void set_aprs_format(int new_aprs_format) {
947                 aprs_format_value.setVisible(new_aprs_format >= 0);
948                 aprs_format_label.setVisible(new_aprs_format >= 0);
949
950                 aprs_format_value.setSelectedIndex(Math.max(0,new_aprs_format));
951                 set_aprs_format_tool_tip();
952         }
953
954         public int aprs_format() throws AltosConfigDataException {
955                 return aprs_format_value.getSelectedIndex();
956         }
957 }