716559ab7e0a9dc06e7c94f5a18a225eb6baa044
[fw/altos] / altosui / AltosPreferences.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 import java.io.*;
20 import java.util.*;
21 import java.text.*;
22 import java.util.prefs.*;
23 import java.util.concurrent.LinkedBlockingQueue;
24 import java.awt.Component;
25 import javax.swing.*;
26 import javax.swing.filechooser.FileSystemView;
27
28 class AltosPreferences {
29         static Preferences preferences;
30
31         /* logdir preference name */
32         final static String logdirPreference = "LOGDIR";
33
34         /* channel preference name */
35         final static String channelPreferenceFormat = "CHANNEL-%d";
36
37         /* frequency preference name */
38         final static String frequencyPreferenceFormat = "FREQUENCY-%d";
39
40         /* telemetry format preference name */
41         final static String telemetryPreferenceFormat = "TELEMETRY-%d";
42
43         /* voice preference name */
44         final static String voicePreference = "VOICE";
45
46         /* callsign preference name */
47         final static String callsignPreference = "CALLSIGN";
48
49         /* firmware directory preference name */
50         final static String firmwaredirPreference = "FIRMWARE";
51
52         /* serial debug preference name */
53         final static String serialDebugPreference = "SERIAL-DEBUG";
54
55         /* scanning telemetry preferences name */
56         final static String scanningTelemetryPreference = "SCANNING-TELEMETRY";
57
58         /* font size preferences name */
59         final static String fontSizePreference = "FONT-SIZE";
60
61         /* Default logdir is ~/TeleMetrum */
62         final static String logdirName = "TeleMetrum";
63
64         /* UI Component to pop dialogs up */
65         static Component component;
66
67         /* Log directory */
68         static File logdir;
69
70         /* Map directory -- hangs of logdir */
71         static File mapdir;
72
73         /* Frequency (map serial to frequency) */
74         static Hashtable<Integer, Double> frequencies;
75
76         /* Telemetry (map serial to telemetry format) */
77         static Hashtable<Integer, Integer> telemetries;
78
79         /* Voice preference */
80         static boolean voice;
81
82         /* Callsign preference */
83         static String callsign;
84
85         /* Firmware directory */
86         static File firmwaredir;
87
88         /* Serial debug */
89         static boolean serial_debug;
90
91         /* Scanning telemetry */
92         static int scanning_telemetry;
93
94         static LinkedList<AltosFontListener> font_listeners;
95
96         static int font_size = Altos.font_size_medium;
97
98         /* List of frequencies */
99         final static String common_frequencies_node_name = "COMMON-FREQUENCIES";
100         static AltosFrequency[] common_frequencies;
101
102         final static String     frequency_count = "COUNT";
103         final static String     frequency_format = "FREQUENCY-%d";
104         final static String     description_format = "DESCRIPTION-%d";
105
106         static AltosFrequency[] load_common_frequencies() {
107                 AltosFrequency[] frequencies = null;
108                 boolean existing = false;
109                 try {
110                         existing = preferences.nodeExists(common_frequencies_node_name);
111                 } catch (BackingStoreException be) {
112                         existing = false;
113                 }
114                 if (existing) {
115                         Preferences     node = preferences.node(common_frequencies_node_name);
116                         int             count = node.getInt(frequency_count, 0);
117
118                         frequencies = new AltosFrequency[count];
119                         for (int i = 0; i < count; i++) {
120                                 double  frequency;
121                                 String  description;
122
123                                 frequency = node.getDouble(String.format(frequency_format, i), 0.0);
124                                 description = node.get(String.format(description_format, i), null);
125                                 frequencies[i] = new AltosFrequency(frequency, description);
126                         }
127                 } else {
128                         frequencies = new AltosFrequency[10];
129                         for (int i = 0; i < 10; i++) {
130                                 frequencies[i] = new AltosFrequency(434.550 + i * .1,
131                                                                            String.format("Channel %d", i));
132                         }
133                 }
134                 return frequencies;
135         }
136
137         static void save_common_frequencies(AltosFrequency[] frequencies) {
138                 Preferences     node = preferences.node(common_frequencies_node_name);
139
140                 node.putInt(frequency_count, frequencies.length);
141                 for (int i = 0; i < frequencies.length; i++) {
142                         node.putDouble(String.format(frequency_format, i), frequencies[i].frequency);
143                         node.put(String.format(description_format, i), frequencies[i].description);
144                 }
145         }
146
147         public static void init() {
148                 preferences = Preferences.userRoot().node("/org/altusmetrum/altosui");
149
150                 /* Initialize logdir from preferences */
151                 String logdir_string = preferences.get(logdirPreference, null);
152                 if (logdir_string != null)
153                         logdir = new File(logdir_string);
154                 else {
155                         /* Use the file system view default directory */
156                         logdir = new File(FileSystemView.getFileSystemView().getDefaultDirectory(), logdirName);
157                         if (!logdir.exists())
158                                 logdir.mkdirs();
159                 }
160                 mapdir = new File(logdir, "maps");
161                 if (!mapdir.exists())
162                         mapdir.mkdirs();
163
164                 frequencies = new Hashtable<Integer, Double>();
165
166                 telemetries = new Hashtable<Integer,Integer>();
167
168                 voice = preferences.getBoolean(voicePreference, true);
169
170                 callsign = preferences.get(callsignPreference,"N0CALL");
171
172                 scanning_telemetry = preferences.getInt(scanningTelemetryPreference,(1 << Altos.ao_telemetry_standard));
173
174                 font_listeners = new LinkedList<AltosFontListener>();
175
176                 font_size = preferences.getInt(fontSizePreference, Altos.font_size_medium);
177                 Altos.set_fonts(font_size);
178
179                 String firmwaredir_string = preferences.get(firmwaredirPreference, null);
180                 if (firmwaredir_string != null)
181                         firmwaredir = new File(firmwaredir_string);
182                 else
183                         firmwaredir = null;
184
185                 serial_debug = preferences.getBoolean(serialDebugPreference, false);
186                 AltosSerial.set_debug(serial_debug);
187
188                 common_frequencies = load_common_frequencies();
189         }
190
191         static { init(); }
192
193         static void set_component(Component in_component) {
194                 component = in_component;
195         }
196
197         static void flush_preferences() {
198                 try {
199                         preferences.flush();
200                 } catch (BackingStoreException ee) {
201                         if (component != null)
202                                 JOptionPane.showMessageDialog(component,
203                                                               preferences.absolutePath(),
204                                                               "Cannot save prefernces",
205                                                               JOptionPane.ERROR_MESSAGE);
206                         else
207                                 System.err.printf("Cannot save preferences\n");
208                 }
209         }
210
211         public static void set_logdir(File new_logdir) {
212                 logdir = new_logdir;
213                 mapdir = new File(logdir, "maps");
214                 if (!mapdir.exists())
215                         mapdir.mkdirs();
216                 synchronized (preferences) {
217                         preferences.put(logdirPreference, logdir.getPath());
218                         flush_preferences();
219                 }
220         }
221
222         private static boolean check_dir(File dir) {
223                 if (!dir.exists()) {
224                         if (!dir.mkdirs()) {
225                                 JOptionPane.showMessageDialog(component,
226                                                               dir.getName(),
227                                                               "Cannot create directory",
228                                                               JOptionPane.ERROR_MESSAGE);
229                                 return false;
230                         }
231                 } else if (!dir.isDirectory()) {
232                         JOptionPane.showMessageDialog(component,
233                                                       dir.getName(),
234                                                       "Is not a directory",
235                                                       JOptionPane.ERROR_MESSAGE);
236                         return false;
237                 }
238                 return true;
239         }
240
241         /* Configure the log directory. This is where all telemetry and eeprom files
242          * will be written to, and where replay will look for telemetry files
243          */
244         public static void ConfigureLog() {
245                 JFileChooser    logdir_chooser = new JFileChooser(logdir.getParentFile());
246
247                 logdir_chooser.setDialogTitle("Configure Data Logging Directory");
248                 logdir_chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
249
250                 if (logdir_chooser.showDialog(component, "Select Directory") == JFileChooser.APPROVE_OPTION) {
251                         File dir = logdir_chooser.getSelectedFile();
252                         if (check_dir(dir))
253                                 set_logdir(dir);
254                 }
255         }
256
257         public static File logdir() {
258                 return logdir;
259         }
260
261         public static File mapdir() {
262                 return mapdir;
263         }
264
265         public static void set_frequency(int serial, double new_frequency) {
266                 frequencies.put(serial, new_frequency);
267                 synchronized (preferences) {
268                         preferences.putDouble(String.format(frequencyPreferenceFormat, serial), new_frequency);
269                         flush_preferences();
270                 }
271         }
272
273         public static double frequency(int serial) {
274                 if (frequencies.containsKey(serial))
275                         return frequencies.get(serial);
276                 double frequency = preferences.getDouble(String.format(frequencyPreferenceFormat, serial), 0);
277                 if (frequency == 0.0) {
278                         int channel = preferences.getInt(String.format(channelPreferenceFormat, serial), 0);
279                         frequency = AltosConvert.radio_channel_to_frequency(channel);
280                 }
281                 frequencies.put(serial, frequency);
282                 return frequency;
283         }
284
285         public static void set_telemetry(int serial, int new_telemetry) {
286                 telemetries.put(serial, new_telemetry);
287                 synchronized (preferences) {
288                         preferences.putInt(String.format(telemetryPreferenceFormat, serial), new_telemetry);
289                         flush_preferences();
290                 }
291         }
292
293         public static int telemetry(int serial) {
294                 if (telemetries.containsKey(serial))
295                         return telemetries.get(serial);
296                 int telemetry = preferences.getInt(String.format(telemetryPreferenceFormat, serial),
297                                                    Altos.ao_telemetry_standard);
298                 telemetries.put(serial, telemetry);
299                 return telemetry;
300         }
301
302         public static void set_scanning_telemetry(int new_scanning_telemetry) {
303                 scanning_telemetry = new_scanning_telemetry;
304                 synchronized (preferences) {
305                         preferences.putInt(scanningTelemetryPreference, scanning_telemetry);
306                         flush_preferences();
307                 }
308         }
309
310         public static int scanning_telemetry() {
311                 return scanning_telemetry;
312         }
313
314         public static void set_voice(boolean new_voice) {
315                 voice = new_voice;
316                 synchronized (preferences) {
317                         preferences.putBoolean(voicePreference, voice);
318                         flush_preferences();
319                 }
320         }
321
322         public static boolean voice() {
323                 return voice;
324         }
325
326         public static void set_callsign(String new_callsign) {
327                 callsign = new_callsign;
328                 synchronized(preferences) {
329                         preferences.put(callsignPreference, callsign);
330                         flush_preferences();
331                 }
332         }
333
334         public static String callsign() {
335                 return callsign;
336         }
337
338         public static void set_firmwaredir(File new_firmwaredir) {
339                 firmwaredir = new_firmwaredir;
340                 synchronized (preferences) {
341                         preferences.put(firmwaredirPreference, firmwaredir.getPath());
342                         flush_preferences();
343                 }
344         }
345
346         public static File firmwaredir() {
347                 return firmwaredir;
348         }
349
350         public static int font_size() {
351                 return font_size;
352         }
353
354         static void set_fonts() {
355         }
356
357         public static void set_font_size(int new_font_size) {
358                 font_size = new_font_size;
359                 synchronized (preferences) {
360                         preferences.putInt(fontSizePreference, font_size);
361                         flush_preferences();
362                         Altos.set_fonts(font_size);
363                         for (AltosFontListener l : font_listeners)
364                                 l.font_size_changed(font_size);
365                 }
366         }
367
368         public static void register_font_listener(AltosFontListener l) {
369                 synchronized (preferences) {
370                         font_listeners.add(l);
371                 }
372         }
373
374         public static void unregister_font_listener(AltosFontListener l) {
375                 synchronized (preferences) {
376                         font_listeners.remove(l);
377                 }
378         }
379
380         public static void set_serial_debug(boolean new_serial_debug) {
381                 serial_debug = new_serial_debug;
382                 AltosSerial.set_debug(serial_debug);
383                 synchronized (preferences) {
384                         preferences.putBoolean(serialDebugPreference, serial_debug);
385                         flush_preferences();
386                 }
387         }
388
389         public static boolean serial_debug() {
390                 return serial_debug;
391         }
392
393         public static Preferences bt_devices() {
394                 return preferences.node("bt_devices");
395         }
396
397         public static AltosFrequency[] common_frequencies() {
398                 return common_frequencies;
399         }
400
401         public static void set_common_frequencies(AltosFrequency[] frequencies) {
402                 common_frequencies = frequencies;
403                 synchronized(preferences) {
404                         save_common_frequencies(frequencies);
405                         flush_preferences();
406                 }
407         }
408
409         public static void add_common_frequency(AltosFrequency frequency) {
410                 AltosFrequency[]        new_frequencies = new AltosFrequency[common_frequencies.length + 1];
411                 int                     i;
412
413                 for (i = 0; i < common_frequencies.length; i++) {
414                         if (frequency.frequency == common_frequencies[i].frequency)
415                                 return;
416                         if (frequency.frequency < common_frequencies[i].frequency)
417                                 break;
418                         new_frequencies[i] = common_frequencies[i];
419                 }
420                 new_frequencies[i] = frequency;
421                 for (; i < common_frequencies.length; i++)
422                         new_frequencies[i+1] = common_frequencies[i];
423                 set_common_frequencies(new_frequencies);
424         }
425 }