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