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