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