altosui: Sanity check values from device configuration
[fw/altos] / altosui / AltosConfigUI.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 altosui;
19
20 import java.awt.*;
21 import java.awt.event.*;
22 import javax.swing.*;
23 import javax.swing.filechooser.FileNameExtensionFilter;
24 import javax.swing.table.*;
25 import javax.swing.event.*;
26 import java.io.*;
27 import java.util.*;
28 import java.text.*;
29 import java.util.prefs.*;
30 import java.util.concurrent.LinkedBlockingQueue;
31
32 import libaltosJNI.*;
33
34 public class AltosConfigUI
35         extends AltosDialog
36         implements ActionListener, ItemListener, DocumentListener
37 {
38
39         Container       pane;
40         Box             box;
41         JLabel          product_label;
42         JLabel          version_label;
43         JLabel          serial_label;
44         JLabel          main_deploy_label;
45         JLabel          apogee_delay_label;
46         JLabel          frequency_label;
47         JLabel          radio_calibration_label;
48         JLabel          radio_frequency_label;
49         JLabel          radio_enable_label;
50         JLabel          flight_log_max_label;
51         JLabel          ignite_mode_label;
52         JLabel          pad_orientation_label;
53         JLabel          callsign_label;
54
55         public boolean          dirty;
56
57         JFrame          owner;
58         JLabel          product_value;
59         JLabel          version_value;
60         JLabel          serial_value;
61         JComboBox       main_deploy_value;
62         JComboBox       apogee_delay_value;
63         AltosFreqList   radio_frequency_value;
64         JTextField      radio_calibration_value;
65         JRadioButton    radio_enable_value;
66         JComboBox       flight_log_max_value;
67         JComboBox       ignite_mode_value;
68         JComboBox       pad_orientation_value;
69         JTextField      callsign_value;
70
71         JButton         save;
72         JButton         reset;
73         JButton         reboot;
74         JButton         close;
75
76         ActionListener  listener;
77
78         static String[] main_deploy_values = {
79                 "100", "150", "200", "250", "300", "350",
80                 "400", "450", "500"
81         };
82
83         static String[] apogee_delay_values = {
84                 "0", "1", "2", "3", "4", "5"
85         };
86
87         static String[] flight_log_max_values = {
88                 "64", "128", "192", "256", "320",
89                 "384", "448", "512", "576", "640",
90                 "704", "768", "832", "896", "960",
91         };
92
93         static String[] ignite_mode_values = {
94                 "Dual Deploy",
95                 "Redundant Apogee",
96                 "Redundant Main",
97         };
98
99         static String[] pad_orientation_values = {
100                 "Antenna Up",
101                 "Antenna Down",
102         };
103
104         /* A window listener to catch closing events and tell the config code */
105         class ConfigListener extends WindowAdapter {
106                 AltosConfigUI   ui;
107
108                 public ConfigListener(AltosConfigUI this_ui) {
109                         ui = this_ui;
110                 }
111
112                 public void windowClosing(WindowEvent e) {
113                         ui.actionPerformed(new ActionEvent(e.getSource(),
114                                                            ActionEvent.ACTION_PERFORMED,
115                                                            "Close"));
116                 }
117         }
118
119         boolean is_telemini() {
120                 String  product = product_value.getText();
121                 return product != null && product.startsWith("TeleMini");
122         }
123
124         boolean is_telemetrum() {
125                 String  product = product_value.getText();
126                 return product != null && product.startsWith("TeleMetrum");
127         }
128
129         void set_radio_calibration_tool_tip() {
130                 if (radio_calibration_value.isEnabled())
131                         radio_calibration_value.setToolTipText("Tune radio output to match desired frequency");
132                 else
133                         radio_calibration_value.setToolTipText("Cannot tune radio while connected over packet mode");
134         }
135
136         void set_radio_enable_tool_tip() {
137                 if (radio_enable_value.isEnabled())
138                         radio_enable_value.setToolTipText("Enable/Disable telemetry and RDF transmissions");
139                 else
140                         radio_enable_value.setToolTipText("Firmware version does not support disabling radio");
141         }
142
143         void set_flight_log_max_tool_tip() {
144                 if (flight_log_max_value.isEnabled())
145                         flight_log_max_value.setToolTipText("Size reserved for each flight log (in kB)");
146                 else {
147                         if (is_telemetrum())
148                                 flight_log_max_value.setToolTipText("Cannot set max value with flight logs in memory");
149                         else if (is_telemini())
150                                 flight_log_max_value.setToolTipText("TeleMini stores only one flight");
151                         else
152                                 flight_log_max_value.setToolTipText("Cannot set max flight log value");
153                 }
154         }
155
156         void set_ignite_mode_tool_tip() {
157                 if (ignite_mode_value.isEnabled())
158                         ignite_mode_value.setToolTipText("Select when igniters will be fired");
159                 else
160                         ignite_mode_value.setToolTipText("Older firmware could not select ignite mode");
161         }
162
163         void set_pad_orientation_tool_tip() {
164                 if (pad_orientation_value.isEnabled())
165                         pad_orientation_value.setToolTipText("How will TeleMetrum be mounted in the airframe");
166                 else {
167                         if (is_telemetrum())
168                                 pad_orientation_value.setToolTipText("Older TeleMetrum firmware must fly antenna forward");
169                         else if (is_telemini())
170                                 pad_orientation_value.setToolTipText("TeleMini doesn't care how it is mounted");
171                         else
172                                 pad_orientation_value.setToolTipText("Can't select orientation");
173                 }
174         }
175
176         /* Build the UI using a grid bag */
177         public AltosConfigUI(JFrame in_owner, boolean remote) {
178                 super (in_owner, "Configure TeleMetrum", false);
179
180                 owner = in_owner;
181                 GridBagConstraints c;
182
183                 Insets il = new Insets(4,4,4,4);
184                 Insets ir = new Insets(4,4,4,4);
185
186                 pane = getContentPane();
187                 pane.setLayout(new GridBagLayout());
188
189                 /* Product */
190                 c = new GridBagConstraints();
191                 c.gridx = 0; c.gridy = 0;
192                 c.gridwidth = 4;
193                 c.fill = GridBagConstraints.NONE;
194                 c.anchor = GridBagConstraints.LINE_START;
195                 c.insets = il;
196                 product_label = new JLabel("Product:");
197                 pane.add(product_label, c);
198
199                 c = new GridBagConstraints();
200                 c.gridx = 4; c.gridy = 0;
201                 c.gridwidth = 4;
202                 c.fill = GridBagConstraints.HORIZONTAL;
203                 c.weightx = 1;
204                 c.anchor = GridBagConstraints.LINE_START;
205                 c.insets = ir;
206                 product_value = new JLabel("");
207                 pane.add(product_value, c);
208
209                 /* Version */
210                 c = new GridBagConstraints();
211                 c.gridx = 0; c.gridy = 1;
212                 c.gridwidth = 4;
213                 c.fill = GridBagConstraints.NONE;
214                 c.anchor = GridBagConstraints.LINE_START;
215                 c.insets = il;
216                 c.ipady = 5;
217                 version_label = new JLabel("Software version:");
218                 pane.add(version_label, c);
219
220                 c = new GridBagConstraints();
221                 c.gridx = 4; c.gridy = 1;
222                 c.gridwidth = 4;
223                 c.fill = GridBagConstraints.HORIZONTAL;
224                 c.weightx = 1;
225                 c.anchor = GridBagConstraints.LINE_START;
226                 c.insets = ir;
227                 c.ipady = 5;
228                 version_value = new JLabel("");
229                 pane.add(version_value, c);
230
231                 /* Serial */
232                 c = new GridBagConstraints();
233                 c.gridx = 0; c.gridy = 2;
234                 c.gridwidth = 4;
235                 c.fill = GridBagConstraints.NONE;
236                 c.anchor = GridBagConstraints.LINE_START;
237                 c.insets = il;
238                 c.ipady = 5;
239                 serial_label = new JLabel("Serial:");
240                 pane.add(serial_label, c);
241
242                 c = new GridBagConstraints();
243                 c.gridx = 4; c.gridy = 2;
244                 c.gridwidth = 4;
245                 c.fill = GridBagConstraints.HORIZONTAL;
246                 c.weightx = 1;
247                 c.anchor = GridBagConstraints.LINE_START;
248                 c.insets = ir;
249                 c.ipady = 5;
250                 serial_value = new JLabel("");
251                 pane.add(serial_value, c);
252
253                 /* Main deploy */
254                 c = new GridBagConstraints();
255                 c.gridx = 0; c.gridy = 3;
256                 c.gridwidth = 4;
257                 c.fill = GridBagConstraints.NONE;
258                 c.anchor = GridBagConstraints.LINE_START;
259                 c.insets = il;
260                 c.ipady = 5;
261                 main_deploy_label = new JLabel("Main Deploy Altitude(m):");
262                 pane.add(main_deploy_label, c);
263
264                 c = new GridBagConstraints();
265                 c.gridx = 4; c.gridy = 3;
266                 c.gridwidth = 4;
267                 c.fill = GridBagConstraints.HORIZONTAL;
268                 c.weightx = 1;
269                 c.anchor = GridBagConstraints.LINE_START;
270                 c.insets = ir;
271                 c.ipady = 5;
272                 main_deploy_value = new JComboBox(main_deploy_values);
273                 main_deploy_value.setEditable(true);
274                 main_deploy_value.addItemListener(this);
275                 pane.add(main_deploy_value, c);
276                 main_deploy_value.setToolTipText("Height above pad altitude to fire main charge");
277
278                 /* Apogee delay */
279                 c = new GridBagConstraints();
280                 c.gridx = 0; c.gridy = 4;
281                 c.gridwidth = 4;
282                 c.fill = GridBagConstraints.NONE;
283                 c.anchor = GridBagConstraints.LINE_START;
284                 c.insets = il;
285                 c.ipady = 5;
286                 apogee_delay_label = new JLabel("Apogee Delay(s):");
287                 pane.add(apogee_delay_label, c);
288
289                 c = new GridBagConstraints();
290                 c.gridx = 4; c.gridy = 4;
291                 c.gridwidth = 4;
292                 c.fill = GridBagConstraints.HORIZONTAL;
293                 c.weightx = 1;
294                 c.anchor = GridBagConstraints.LINE_START;
295                 c.insets = ir;
296                 c.ipady = 5;
297                 apogee_delay_value = new JComboBox(apogee_delay_values);
298                 apogee_delay_value.setEditable(true);
299                 apogee_delay_value.addItemListener(this);
300                 pane.add(apogee_delay_value, c);
301                 apogee_delay_value.setToolTipText("Delay after apogee before charge fires");
302
303                 /* Frequency */
304                 c = new GridBagConstraints();
305                 c.gridx = 0; c.gridy = 5;
306                 c.gridwidth = 4;
307                 c.fill = GridBagConstraints.NONE;
308                 c.anchor = GridBagConstraints.LINE_START;
309                 c.insets = il;
310                 c.ipady = 5;
311                 radio_frequency_label = new JLabel("Frequency:");
312                 pane.add(radio_frequency_label, c);
313
314                 c = new GridBagConstraints();
315                 c.gridx = 4; c.gridy = 5;
316                 c.gridwidth = 4;
317                 c.fill = GridBagConstraints.HORIZONTAL;
318                 c.weightx = 1;
319                 c.anchor = GridBagConstraints.LINE_START;
320                 c.insets = ir;
321                 c.ipady = 5;
322                 radio_frequency_value = new AltosFreqList();
323                 radio_frequency_value.addItemListener(this);
324                 pane.add(radio_frequency_value, c);
325                 radio_frequency_value.setToolTipText("Telemetry, RDF and packet frequency");
326
327                 /* Radio Calibration */
328                 c = new GridBagConstraints();
329                 c.gridx = 0; c.gridy = 6;
330                 c.gridwidth = 4;
331                 c.fill = GridBagConstraints.NONE;
332                 c.anchor = GridBagConstraints.LINE_START;
333                 c.insets = il;
334                 c.ipady = 5;
335                 radio_calibration_label = new JLabel("RF Calibration:");
336                 pane.add(radio_calibration_label, c);
337
338                 c = new GridBagConstraints();
339                 c.gridx = 4; c.gridy = 6;
340                 c.gridwidth = 4;
341                 c.fill = GridBagConstraints.HORIZONTAL;
342                 c.weightx = 1;
343                 c.anchor = GridBagConstraints.LINE_START;
344                 c.insets = ir;
345                 c.ipady = 5;
346                 radio_calibration_value = new JTextField(String.format("%d", 1186611));
347                 radio_calibration_value.getDocument().addDocumentListener(this);
348                 if (remote)
349                         radio_calibration_value.setEnabled(false);
350                 pane.add(radio_calibration_value, c);
351                 set_radio_calibration_tool_tip();
352
353                 /* Radio Enable */
354                 c = new GridBagConstraints();
355                 c.gridx = 0; c.gridy = 7;
356                 c.gridwidth = 4;
357                 c.fill = GridBagConstraints.NONE;
358                 c.anchor = GridBagConstraints.LINE_START;
359                 c.insets = il;
360                 c.ipady = 5;
361                 radio_enable_label = new JLabel("Telemetry/RDF Enable:");
362                 pane.add(radio_enable_label, c);
363
364                 c = new GridBagConstraints();
365                 c.gridx = 4; c.gridy = 7;
366                 c.gridwidth = 4;
367                 c.fill = GridBagConstraints.HORIZONTAL;
368                 c.weightx = 1;
369                 c.anchor = GridBagConstraints.LINE_START;
370                 c.insets = ir;
371                 c.ipady = 5;
372                 radio_enable_value = new JRadioButton("Enabled");
373                 radio_enable_value.addItemListener(this);
374                 pane.add(radio_enable_value, c);
375                 set_radio_enable_tool_tip();
376
377                 /* Callsign */
378                 c = new GridBagConstraints();
379                 c.gridx = 0; c.gridy = 8;
380                 c.gridwidth = 4;
381                 c.fill = GridBagConstraints.NONE;
382                 c.anchor = GridBagConstraints.LINE_START;
383                 c.insets = il;
384                 c.ipady = 5;
385                 callsign_label = new JLabel("Callsign:");
386                 pane.add(callsign_label, c);
387
388                 c = new GridBagConstraints();
389                 c.gridx = 4; c.gridy = 8;
390                 c.gridwidth = 4;
391                 c.fill = GridBagConstraints.HORIZONTAL;
392                 c.weightx = 1;
393                 c.anchor = GridBagConstraints.LINE_START;
394                 c.insets = ir;
395                 c.ipady = 5;
396                 callsign_value = new JTextField(AltosPreferences.callsign());
397                 callsign_value.getDocument().addDocumentListener(this);
398                 pane.add(callsign_value, c);
399                 callsign_value.setToolTipText("Callsign reported in telemetry data");
400
401                 /* Flight log max */
402                 c = new GridBagConstraints();
403                 c.gridx = 0; c.gridy = 9;
404                 c.gridwidth = 4;
405                 c.fill = GridBagConstraints.NONE;
406                 c.anchor = GridBagConstraints.LINE_START;
407                 c.insets = il;
408                 c.ipady = 5;
409                 flight_log_max_label = new JLabel("Maximum Flight Log Size:");
410                 pane.add(flight_log_max_label, c);
411
412                 c = new GridBagConstraints();
413                 c.gridx = 4; c.gridy = 9;
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                 flight_log_max_value = new JComboBox(flight_log_max_values);
421                 flight_log_max_value.setEditable(true);
422                 flight_log_max_value.addItemListener(this);
423                 pane.add(flight_log_max_value, c);
424                 set_flight_log_max_tool_tip();
425
426                 /* Ignite mode */
427                 c = new GridBagConstraints();
428                 c.gridx = 0; c.gridy = 10;
429                 c.gridwidth = 4;
430                 c.fill = GridBagConstraints.NONE;
431                 c.anchor = GridBagConstraints.LINE_START;
432                 c.insets = il;
433                 c.ipady = 5;
434                 ignite_mode_label = new JLabel("Igniter Firing Mode:");
435                 pane.add(ignite_mode_label, c);
436
437                 c = new GridBagConstraints();
438                 c.gridx = 4; c.gridy = 10;
439                 c.gridwidth = 4;
440                 c.fill = GridBagConstraints.HORIZONTAL;
441                 c.weightx = 1;
442                 c.anchor = GridBagConstraints.LINE_START;
443                 c.insets = ir;
444                 c.ipady = 5;
445                 ignite_mode_value = new JComboBox(ignite_mode_values);
446                 ignite_mode_value.setEditable(false);
447                 ignite_mode_value.addItemListener(this);
448                 pane.add(ignite_mode_value, c);
449                 set_ignite_mode_tool_tip();
450
451                 /* Pad orientation */
452                 c = new GridBagConstraints();
453                 c.gridx = 0; c.gridy = 11;
454                 c.gridwidth = 4;
455                 c.fill = GridBagConstraints.NONE;
456                 c.anchor = GridBagConstraints.LINE_START;
457                 c.insets = il;
458                 c.ipady = 5;
459                 pad_orientation_label = new JLabel("Pad Orientation:");
460                 pane.add(pad_orientation_label, c);
461
462                 c = new GridBagConstraints();
463                 c.gridx = 4; c.gridy = 11;
464                 c.gridwidth = 4;
465                 c.fill = GridBagConstraints.HORIZONTAL;
466                 c.weightx = 1;
467                 c.anchor = GridBagConstraints.LINE_START;
468                 c.insets = ir;
469                 c.ipady = 5;
470                 pad_orientation_value = new JComboBox(pad_orientation_values);
471                 pad_orientation_value.setEditable(false);
472                 pad_orientation_value.addItemListener(this);
473                 pane.add(pad_orientation_value, c);
474                 set_pad_orientation_tool_tip();
475
476                 /* Buttons */
477                 c = new GridBagConstraints();
478                 c.gridx = 0; c.gridy = 12;
479                 c.gridwidth = 2;
480                 c.fill = GridBagConstraints.NONE;
481                 c.anchor = GridBagConstraints.LINE_START;
482                 c.insets = il;
483                 save = new JButton("Save");
484                 pane.add(save, c);
485                 save.addActionListener(this);
486                 save.setActionCommand("Save");
487
488                 c = new GridBagConstraints();
489                 c.gridx = 2; c.gridy = 12;
490                 c.gridwidth = 2;
491                 c.fill = GridBagConstraints.NONE;
492                 c.anchor = GridBagConstraints.CENTER;
493                 c.insets = il;
494                 reset = new JButton("Reset");
495                 pane.add(reset, c);
496                 reset.addActionListener(this);
497                 reset.setActionCommand("Reset");
498
499                 c = new GridBagConstraints();
500                 c.gridx = 4; c.gridy = 12;
501                 c.gridwidth = 2;
502                 c.fill = GridBagConstraints.NONE;
503                 c.anchor = GridBagConstraints.CENTER;
504                 c.insets = il;
505                 reboot = new JButton("Reboot");
506                 pane.add(reboot, c);
507                 reboot.addActionListener(this);
508                 reboot.setActionCommand("Reboot");
509
510                 c = new GridBagConstraints();
511                 c.gridx = 6; c.gridy = 12;
512                 c.gridwidth = 2;
513                 c.fill = GridBagConstraints.NONE;
514                 c.anchor = GridBagConstraints.LINE_END;
515                 c.insets = il;
516                 close = new JButton("Close");
517                 pane.add(close, c);
518                 close.addActionListener(this);
519                 close.setActionCommand("Close");
520
521                 addWindowListener(new ConfigListener(this));
522         }
523
524         /* Once the initial values are set, the config code will show the dialog */
525         public void make_visible() {
526                 pack();
527                 setLocationRelativeTo(owner);
528                 setVisible(true);
529         }
530
531         /* If any values have been changed, confirm before closing */
532         public boolean check_dirty(String operation) {
533                 if (dirty) {
534                         Object[] options = { String.format("%s anyway", operation), "Keep editing" };
535                         int i;
536                         i = JOptionPane.showOptionDialog(this,
537                                                          String.format("Configuration modified. %s anyway?", operation),
538                                                          "Configuration Modified",
539                                                          JOptionPane.DEFAULT_OPTION,
540                                                          JOptionPane.WARNING_MESSAGE,
541                                                          null, options, options[1]);
542                         if (i != 0)
543                                 return false;
544                 }
545                 return true;
546         }
547
548         /* Listen for events from our buttons */
549         public void actionPerformed(ActionEvent e) {
550                 String  cmd = e.getActionCommand();
551
552                 if (cmd.equals("Close") || cmd.equals("Reboot"))
553                         if (!check_dirty(cmd))
554                                 return;
555                 listener.actionPerformed(e);
556                 if (cmd.equals("Close") || cmd.equals("Reboot")) {
557                         setVisible(false);
558                         dispose();
559                 }
560                 dirty = false;
561         }
562
563         /* ItemListener interface method */
564         public void itemStateChanged(ItemEvent e) {
565                 dirty = true;
566         }
567
568         /* DocumentListener interface methods */
569         public void changedUpdate(DocumentEvent e) {
570                 dirty = true;
571         }
572
573         public void insertUpdate(DocumentEvent e) {
574                 dirty = true;
575         }
576
577         public void removeUpdate(DocumentEvent e) {
578                 dirty = true;
579         }
580
581         /* Let the config code hook on a listener */
582         public void addActionListener(ActionListener l) {
583                 listener = l;
584         }
585
586         /* set and get all of the dialog values */
587         public void set_product(String product) {
588                 radio_frequency_value.set_product(product);
589                 product_value.setText(product);
590                 set_pad_orientation_tool_tip();
591                 set_flight_log_max_tool_tip();
592         }
593
594         public void set_version(String version) {
595                 version_value.setText(version);
596         }
597
598         public void set_serial(int serial) {
599                 radio_frequency_value.set_serial(serial);
600                 serial_value.setText(String.format("%d", serial));
601         }
602
603         public void set_main_deploy(int new_main_deploy) {
604                 main_deploy_value.setSelectedItem(Integer.toString(new_main_deploy));
605         }
606
607         public int main_deploy() {
608                 return Integer.parseInt(main_deploy_value.getSelectedItem().toString());
609         }
610
611         public void set_apogee_delay(int new_apogee_delay) {
612                 apogee_delay_value.setSelectedItem(Integer.toString(new_apogee_delay));
613         }
614
615         public int apogee_delay() {
616                 return Integer.parseInt(apogee_delay_value.getSelectedItem().toString());
617         }
618
619         public void set_radio_frequency(double new_radio_frequency) {
620                 int i;
621                 for (i = 0; i < radio_frequency_value.getItemCount(); i++) {
622                         AltosFrequency  f = (AltosFrequency) radio_frequency_value.getItemAt(i);
623                         
624                         if (f.close(new_radio_frequency)) {
625                                 radio_frequency_value.setSelectedIndex(i);
626                                 return;
627                         }
628                 }
629                 for (i = 0; i < radio_frequency_value.getItemCount(); i++) {
630                         AltosFrequency  f = (AltosFrequency) radio_frequency_value.getItemAt(i);
631                         
632                         if (new_radio_frequency < f.frequency)
633                                 break;
634                 }
635                 String  description = String.format("%s serial %s",
636                                                     product_value.getText(),
637                                                     serial_value.getText());
638                 AltosFrequency  new_frequency = new AltosFrequency(new_radio_frequency, description);
639                 AltosPreferences.add_common_frequency(new_frequency);
640                 radio_frequency_value.insertItemAt(new_frequency, i);
641                 radio_frequency_value.setSelectedIndex(i);
642         }
643
644         public double radio_frequency() {
645                 return radio_frequency_value.frequency();
646         }
647
648         public void set_radio_calibration(int new_radio_calibration) {
649                 radio_calibration_value.setText(String.format("%d", new_radio_calibration));
650         }
651
652         public int radio_calibration() {
653                 return Integer.parseInt(radio_calibration_value.getText());
654         }
655
656         public void set_radio_enable(int new_radio_enable) {
657                 if (new_radio_enable >= 0) {
658                         radio_enable_value.setSelected(new_radio_enable > 0);
659                         radio_enable_value.setEnabled(true);
660                 } else {
661                         radio_enable_value.setSelected(true);
662                         radio_enable_value.setEnabled(false);
663                 }
664                 set_radio_enable_tool_tip();
665         }
666
667         public int radio_enable() {
668                 if (radio_enable_value.isEnabled())
669                         return radio_enable_value.isSelected() ? 1 : 0;
670                 else
671                         return -1;
672         }
673
674         public void set_callsign(String new_callsign) {
675                 callsign_value.setText(new_callsign);
676         }
677
678         public String callsign() {
679                 return callsign_value.getText();
680         }
681
682         public void set_flight_log_max(int new_flight_log_max) {
683                 if (new_flight_log_max == 0)
684                         flight_log_max_value.setEnabled(false);
685                 flight_log_max_value.setSelectedItem(Integer.toString(new_flight_log_max));
686                 set_flight_log_max_tool_tip();
687         }
688
689         public void set_flight_log_max_enabled(boolean enable) {
690                 flight_log_max_value.setEnabled(enable);
691                 set_flight_log_max_tool_tip();
692         }
693
694         public int flight_log_max() {
695                 return Integer.parseInt(flight_log_max_value.getSelectedItem().toString());
696         }
697
698         public void set_flight_log_max_limit(int flight_log_max_limit) {
699                 boolean any_added = false;
700                 flight_log_max_value.removeAllItems();
701                 for (int i = 0; i < flight_log_max_values.length; i++) {
702                         if (Integer.parseInt(flight_log_max_values[i]) < flight_log_max_limit){
703                                 flight_log_max_value.addItem(flight_log_max_values[i]);
704                                 any_added = true;
705                         }
706                 }
707                 flight_log_max_value.addItem(String.format("%d", flight_log_max_limit));
708         }
709
710         public void set_ignite_mode(int new_ignite_mode) {
711                 if (new_ignite_mode >= ignite_mode_values.length)
712                         new_ignite_mode = 0;
713                 if (new_ignite_mode < 0) {
714                         ignite_mode_value.setEnabled(false);
715                         new_ignite_mode = 0;
716                 } else {
717                         ignite_mode_value.setEnabled(true);
718                 }
719                 ignite_mode_value.setSelectedIndex(new_ignite_mode);
720                 set_ignite_mode_tool_tip();
721         }
722
723         public int ignite_mode() {
724                 if (ignite_mode_value.isEnabled())
725                         return ignite_mode_value.getSelectedIndex();
726                 else
727                         return -1;
728         }
729
730
731         public void set_pad_orientation(int new_pad_orientation) {
732                 if (new_pad_orientation >= pad_orientation_values.length)
733                         new_pad_orientation = 0;
734                 if (new_pad_orientation < 0) {
735                         pad_orientation_value.setEnabled(false);
736                         new_pad_orientation = 0;
737                 } else {
738                         pad_orientation_value.setEnabled(true);
739                 }
740                 pad_orientation_value.setSelectedIndex(new_pad_orientation);
741                 set_pad_orientation_tool_tip();
742         }
743
744         public int pad_orientation() {
745                 if (pad_orientation_value.isEnabled())
746                         return pad_orientation_value.getSelectedIndex();
747                 else
748                         return -1;
749         }
750
751         public void set_clean() {
752                 dirty = false;
753         }
754 }