45f2e0dc2944d074d5f4d5153ed819f5258bf2b6
[debian/openrocket] / core / src / net / sf / openrocket / gui / util / SwingPreferences.java
1 package net.sf.openrocket.gui.util;
2
3 import java.awt.Color;
4 import java.awt.Dimension;
5 import java.awt.Point;
6 import java.io.File;
7 import java.util.ArrayList;
8 import java.util.Collections;
9 import java.util.HashSet;
10 import java.util.List;
11 import java.util.Locale;
12 import java.util.Set;
13 import java.util.prefs.BackingStoreException;
14 import java.util.prefs.Preferences;
15
16 import net.sf.openrocket.arch.SystemInfo;
17 import net.sf.openrocket.document.Simulation;
18 import net.sf.openrocket.logging.LogHelper;
19 import net.sf.openrocket.material.Material;
20 import net.sf.openrocket.rocketcomponent.Rocket;
21 import net.sf.openrocket.simulation.FlightDataType;
22 import net.sf.openrocket.simulation.RK4SimulationStepper;
23 import net.sf.openrocket.simulation.SimulationOptions;
24 import net.sf.openrocket.startup.Application;
25 import net.sf.openrocket.unit.UnitGroup;
26 import net.sf.openrocket.util.BugException;
27 import net.sf.openrocket.util.BuildProperties;
28
29
30 public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
31         private static final LogHelper log = Application.getLogger();
32         
33         private static final String SPLIT_CHARACTER = "|";
34         
35
36         private static final List<Locale> SUPPORTED_LOCALES;
37         static {
38                 List<Locale> list = new ArrayList<Locale>();
39                 for (String lang : new String[] { "en", "de", "es", "fr", "it", "ru" }) {
40                         list.add(new Locale(lang));
41                 }
42                 SUPPORTED_LOCALES = Collections.unmodifiableList(list);
43         }
44         
45
46         /**
47          * Whether to use the debug-node instead of the normal node.
48          */
49         private static final boolean DEBUG;
50         static {
51                 DEBUG = (System.getProperty("openrocket.debug.prefs") != null);
52         }
53         
54         /**
55          * Whether to clear all preferences at application startup.  This has an effect only
56          * if DEBUG is true.
57          */
58         private static final boolean CLEARPREFS = true;
59         
60         /**
61          * The node name to use in the Java preferences storage.
62          */
63         private static final String NODENAME = (DEBUG ? "OpenRocket-debug" : "OpenRocket");
64         
65         private  final Preferences PREFNODE;
66         
67
68         public SwingPreferences() {
69                 Preferences root = Preferences.userRoot();
70                 if (DEBUG && CLEARPREFS) {
71                         try {
72                                 if (root.nodeExists(NODENAME)) {
73                                         root.node(NODENAME).removeNode();
74                                 }
75                         } catch (BackingStoreException e) {
76                                 throw new BugException("Unable to clear preference node", e);
77                         }
78                 }
79                 PREFNODE = root.node(NODENAME);
80         }
81         
82
83
84         
85         //////////////////////
86         
87
88
89         /**
90          * Store the current OpenRocket version into the preferences to allow for preferences migration.
91          */
92         private void storeVersion() {
93                 PREFNODE.put("OpenRocketVersion", BuildProperties.getVersion());
94         }
95         
96         /**
97          * Return a string preference.
98          * 
99          * @param key   the preference key.
100          * @param def   the default if no preference is stored
101          * @return              the preference value
102          */
103         @Override
104         public String getString(String key, String def) {
105                 return PREFNODE.get(key, def);
106         }
107         
108         @Override
109         public String getString( String directory, String key, String defaultValue ) {
110                 Preferences p = PREFNODE.node(directory);
111                 return p.get(key,defaultValue);
112         }
113         
114         /**
115          * Set a string preference.
116          * 
117          * @param key           the preference key
118          * @param value         the value to set, or <code>null</code> to remove the key
119          */
120         @Override
121         public void putString(String key, String value) {
122                 if (value == null) {
123                         PREFNODE.remove(key);
124                 } else {
125                         PREFNODE.put(key, value);
126                 }
127                 storeVersion();
128         }
129         
130         @Override
131         public void putString(String directory, String key, String value ) {
132                 Preferences p = PREFNODE.node(directory);
133                 if ( value == null ) {
134                         p.remove(key);
135                 } else {
136                         p.put(key,value);
137                 }
138                 storeVersion();
139         }
140         
141         /**
142          * Return a boolean preference.
143          * 
144          * @param key   the preference key
145          * @param def   the default if no preference is stored
146          * @return              the preference value
147          */
148         @Override
149         public boolean getBoolean(String key, boolean def) {
150                 return PREFNODE.getBoolean(key, def);
151         }
152         
153         /**
154          * Set a boolean preference.
155          * 
156          * @param key           the preference key
157          * @param value         the value to set
158          */
159         @Override
160         public void putBoolean(String key, boolean value) {
161                 PREFNODE.putBoolean(key, value);
162                 storeVersion();
163         }
164         
165         @Override
166         public int getInt( String key, int defaultValue ) {
167                 return PREFNODE.getInt(key, defaultValue);
168         }
169         
170         @Override
171         public void putInt( String key , int value ) {
172                 PREFNODE.putInt(key, value );
173                 storeVersion();
174         }
175         
176         @Override
177         public double getDouble(String key, double defaultValue) {
178                 return PREFNODE.getDouble(key,  defaultValue );
179         }
180
181         @Override
182         public void putDouble(String key, double value) {
183                 PREFNODE.putDouble(key,value);
184                 storeVersion();
185         }
186
187
188
189         /**
190          * Return a preferences object for the specified node name.
191          * 
192          * @param nodeName      the node name
193          * @return                      the preferences object for that node
194          */
195         public Preferences getNode(String nodeName) {
196                 return PREFNODE.node(nodeName);
197         }
198         
199         
200         //////////////////
201         
202
203         public static List<Locale> getSupportedLocales() {
204                 return SUPPORTED_LOCALES;
205         }
206         
207         public File getDefaultDirectory() {
208                 String file = getString("defaultDirectory", null);
209                 if (file == null)
210                         return null;
211                 return new File(file);
212         }
213         
214         public void setDefaultDirectory(File dir) {
215                 String d;
216                 if (dir == null) {
217                         d = null;
218                 } else {
219                         d = dir.getAbsolutePath();
220                 }
221                 putString("defaultDirectory", d);
222                 storeVersion();
223         }
224         
225         
226         /**
227          * Return a list of files/directories to be loaded as custom thrust curves.
228          * <p>
229          * If this property has not been set, the directory "ThrustCurves" in the user
230          * application directory will be used.  The directory will be created if it does not
231          * exist.
232          * 
233          * @return      a list of files to load as thrust curves.
234          */
235         public List<File> getUserThrustCurveFiles() {
236                 List<File> list = new ArrayList<File>();
237                 
238                 String files = getString(USER_THRUST_CURVES_KEY, null);
239                 if (files == null) {
240                         // Default to application directory
241                         File tcdir = getDefaultUserThrustCurveFile();
242                         if (!tcdir.isDirectory()) {
243                                 tcdir.mkdirs();
244                         }
245                         list.add(tcdir);
246                 } else {
247                         for (String file : files.split("\\" + SPLIT_CHARACTER)) {
248                                 file = file.trim();
249                                 if (file.length() > 0) {
250                                         list.add(new File(file));
251                                 }
252                         }
253                 }
254                 
255                 return list;
256         }
257         
258         public File getDefaultUserThrustCurveFile() {
259                 File appdir = SystemInfo.getUserApplicationDirectory();
260                 File tcdir = new File(appdir, "ThrustCurves");
261                 return tcdir;
262         }
263         
264         
265         /**
266          * Set the list of files/directories to be loaded as custom thrust curves.
267          * 
268          * @param files         the files to load, or <code>null</code> to reset to default value.
269          */
270         public void setUserThrustCurveFiles(List<File> files) {
271                 if (files == null) {
272                         putString(USER_THRUST_CURVES_KEY, null);
273                         return;
274                 }
275                 
276                 String str = "";
277                 
278                 for (File file : files) {
279                         if (str.length() > 0) {
280                                 str += SPLIT_CHARACTER;
281                         }
282                         str += file.getAbsolutePath();
283                 }
284                 putString(USER_THRUST_CURVES_KEY, str);
285         }
286         
287         public Color getMotorBorderColor() {
288                 // TODO: MEDIUM:  Motor color (settable?)
289                 return new Color(0, 0, 0, 200);
290         }
291         
292         
293         public Color getMotorFillColor() {
294                 // TODO: MEDIUM:  Motor fill color (settable?)
295                 return new Color(0, 0, 0, 100);
296         }
297         
298         
299         
300         public static int getMaxThreadCount() {
301                 return Runtime.getRuntime().availableProcessors();
302         }
303         
304         
305         
306         public Point getWindowPosition(Class<?> c) {
307                 int x, y;
308                 String pref = PREFNODE.node("windows").get("position." + c.getCanonicalName(), null);
309                 
310                 if (pref == null)
311                         return null;
312                 
313                 if (pref.indexOf(',') < 0)
314                         return null;
315                 
316                 try {
317                         x = Integer.parseInt(pref.substring(0, pref.indexOf(',')));
318                         y = Integer.parseInt(pref.substring(pref.indexOf(',') + 1));
319                 } catch (NumberFormatException e) {
320                         return null;
321                 }
322                 return new Point(x, y);
323         }
324         
325         public void setWindowPosition(Class<?> c, Point p) {
326                 PREFNODE.node("windows").put("position." + c.getCanonicalName(), "" + p.x + "," + p.y);
327                 storeVersion();
328         }
329         
330         
331
332
333         public Dimension getWindowSize(Class<?> c) {
334                 int x, y;
335                 String pref = PREFNODE.node("windows").get("size." + c.getCanonicalName(), null);
336                 
337                 if (pref == null)
338                         return null;
339                 
340                 if (pref.indexOf(',') < 0)
341                         return null;
342                 
343                 try {
344                         x = Integer.parseInt(pref.substring(0, pref.indexOf(',')));
345                         y = Integer.parseInt(pref.substring(pref.indexOf(',') + 1));
346                 } catch (NumberFormatException e) {
347                         return null;
348                 }
349                 return new Dimension(x, y);
350         }
351         
352         
353         public boolean isWindowMaximized(Class<?> c) {
354                 String pref = PREFNODE.node("windows").get("size." + c.getCanonicalName(), null);
355                 return "max".equals(pref);
356         }
357         
358         public void setWindowSize(Class<?> c, Dimension d) {
359                 PREFNODE.node("windows").put("size." + c.getCanonicalName(), "" + d.width + "," + d.height);
360                 storeVersion();
361         }
362         
363         public void setWindowMaximized(Class<?> c) {
364                 PREFNODE.node("windows").put("size." + c.getCanonicalName(), "max");
365                 storeVersion();
366         }
367         
368         /**
369          * this class returns a java.awt.Color object for the specified key.
370          * you can pass (java.awt.Color) null to the second argument to
371          * disambiguate
372          */
373         public Color getColor( String key, Color defaultValue ) {
374                 net.sf.openrocket.util.Color c = super.getColor(key, (net.sf.openrocket.util.Color) null);
375                 if ( c == null ) {
376                         return defaultValue;
377                 }
378                 return ColorConversion.toAwtColor(c);
379         }
380         
381         /**
382          * 
383          */
384         public void putColor( String key, Color value ) {
385                 net.sf.openrocket.util.Color c = ColorConversion.fromAwtColor(value);
386                 super.putColor(key,  c); 
387         }
388         
389         ////  Printing
390         
391         
392         ////  Background flight data computation
393         
394         public boolean computeFlightInBackground() {
395                 return PREFNODE.getBoolean("backgroundFlight", true);
396         }
397         
398         public Simulation getBackgroundSimulation(Rocket rocket) {
399                 Simulation s = new Simulation(rocket);
400                 SimulationOptions cond = s.getOptions();
401                 
402                 cond.setTimeStep(RK4SimulationStepper.RECOMMENDED_TIME_STEP * 2);
403                 cond.setWindSpeedAverage(1.0);
404                 cond.setWindSpeedDeviation(0.1);
405                 cond.setLaunchRodLength(5);
406                 return s;
407         }
408         
409         
410
411         /////////  Export variables
412         
413         public boolean isExportSelected(FlightDataType type) {
414                 Preferences prefs = PREFNODE.node("exports");
415                 return prefs.getBoolean(type.getName(), false);
416         }
417         
418         public void setExportSelected(FlightDataType type, boolean selected) {
419                 Preferences prefs = PREFNODE.node("exports");
420                 prefs.putBoolean(type.getName(), selected);
421         }
422         
423         
424
425         /////////  Default unit storage
426         
427         public void loadDefaultUnits() {
428                 Preferences prefs = PREFNODE.node("units");
429                 try {
430                         
431                         for (String key : prefs.keys()) {
432                                 UnitGroup group = UnitGroup.UNITS.get(key);
433                                 if (group == null)
434                                         continue;
435                                 
436                                 try {
437                                         group.setDefaultUnit(prefs.get(key, null));
438                                 } catch (IllegalArgumentException ignore) {
439                                 }
440                         }
441                         
442                 } catch (BackingStoreException e) {
443                         Application.getExceptionHandler().handleErrorCondition(e);
444                 }
445         }
446         
447         public void storeDefaultUnits() {
448                 Preferences prefs = PREFNODE.node("units");
449                 
450                 for (String key : UnitGroup.UNITS.keySet()) {
451                         UnitGroup group = UnitGroup.UNITS.get(key);
452                         if (group == null || group.getUnitCount() < 2)
453                                 continue;
454                         
455                         prefs.put(key, group.getDefaultUnit().getUnit());
456                 }
457         }
458         
459         
460
461         ////  Material storage
462         
463
464         /**
465          * Add a user-defined material to the preferences.  The preferences are
466          * first checked for an existing material matching the provided one using
467          * {@link Material#equals(Object)}.
468          * 
469          * @param m             the material to add.
470          */
471         public void addUserMaterial(Material m) {
472                 Preferences prefs = PREFNODE.node("userMaterials");
473                 
474
475                 // Check whether material already exists
476                 if (getUserMaterials().contains(m)) {
477                         return;
478                 }
479                 
480                 // Add material using next free key (key is not used when loading)
481                 String mat = m.toStorableString();
482                 for (int i = 0;; i++) {
483                         String key = "material" + i;
484                         if (prefs.get(key, null) == null) {
485                                 prefs.put(key, mat);
486                                 return;
487                         }
488                 }
489         }
490         
491         
492         /**
493          * Remove a user-defined material from the preferences.  The matching is performed
494          * using {@link Material#equals(Object)}.
495          * 
496          * @param m             the material to remove.
497          */
498         public void removeUserMaterial(Material m) {
499                 Preferences prefs = PREFNODE.node("userMaterials");
500                 
501                 try {
502                         
503                         // Iterate through materials and remove all keys with a matching material
504                         for (String key : prefs.keys()) {
505                                 String value = prefs.get(key, null);
506                                 try {
507                                         
508                                         Material existing = Material.fromStorableString(value, true);
509                                         if (existing.equals(m)) {
510                                                 prefs.remove(key);
511                                         }
512                                         
513                                 } catch (IllegalArgumentException ignore) {
514                                 }
515                                 
516                         }
517                         
518                 } catch (BackingStoreException e) {
519                         throw new IllegalStateException("Cannot read preferences!", e);
520                 }
521         }
522         
523         
524         /**
525          * Return a set of all user-defined materials in the preferences.  The materials
526          * are created marked as user-defined.
527          * 
528          * @return      a set of all user-defined materials.
529          */
530         public Set<Material> getUserMaterials() {
531                 Preferences prefs = PREFNODE.node("userMaterials");
532                 
533                 HashSet<Material> materials = new HashSet<Material>();
534                 try {
535                         
536                         for (String key : prefs.keys()) {
537                                 String value = prefs.get(key, null);
538                                 try {
539                                         
540                                         Material m = Material.fromStorableString(value, true);
541                                         materials.add(m);
542                                         
543                                 } catch (IllegalArgumentException e) {
544                                         log.warn("Illegal material string " + value);
545                                 }
546                                 
547                         }
548                         
549                 } catch (BackingStoreException e) {
550                         throw new IllegalStateException("Cannot read preferences!", e);
551                 }
552                 
553                 return materials;
554         }
555         
556         
557         ////  Helper methods
558         
559 }