updates
[debian/openrocket] / src / net / sf / openrocket / util / Prefs.java
1 package net.sf.openrocket.util;
2
3 import java.awt.Color;
4 import java.awt.Dimension;
5 import java.awt.Point;
6 import java.awt.Toolkit;
7 import java.io.File;
8 import java.io.IOException;
9 import java.io.InputStream;
10 import java.util.HashMap;
11 import java.util.Map;
12 import java.util.MissingResourceException;
13 import java.util.Properties;
14 import java.util.prefs.BackingStoreException;
15 import java.util.prefs.Preferences;
16
17 import net.sf.openrocket.database.Databases;
18 import net.sf.openrocket.document.Simulation;
19 import net.sf.openrocket.material.Material;
20 import net.sf.openrocket.rocketcomponent.BodyComponent;
21 import net.sf.openrocket.rocketcomponent.FinSet;
22 import net.sf.openrocket.rocketcomponent.InternalComponent;
23 import net.sf.openrocket.rocketcomponent.LaunchLug;
24 import net.sf.openrocket.rocketcomponent.MassObject;
25 import net.sf.openrocket.rocketcomponent.RecoveryDevice;
26 import net.sf.openrocket.rocketcomponent.Rocket;
27 import net.sf.openrocket.rocketcomponent.RocketComponent;
28 import net.sf.openrocket.simulation.RK4Simulator;
29 import net.sf.openrocket.simulation.SimulationConditions;
30 import net.sf.openrocket.unit.UnitGroup;
31
32
33 public class Prefs {
34
35         /**
36          * Whether to use the debug-node instead of the normal node.
37          */
38         public static final boolean DEBUG = false;
39         
40         /**
41          * Whether to clear all preferences at application startup.  This has an effect only
42          * if DEBUG is true.
43          */
44         public static final boolean CLEARPREFS = true;
45         
46         /**
47          * The node name to use in the Java preferences storage.
48          */
49         public static final String NODENAME = (DEBUG?"OpenRocket-debug":"OpenRocket");
50         
51         
52         
53         private static final String BUILD_VERSION;
54         private static final String BUILD_SOURCE;
55         
56         static {
57                 try {
58                         InputStream is = ClassLoader.getSystemResourceAsStream("build.properties");
59                         if (is == null) {
60                                 throw new MissingResourceException(
61                                                 "build.properties not found, distribution built wrong",
62                                                 "build.properties", "build.version");
63                         }
64                         
65                         Properties props = new Properties();
66                         props.load(is);
67                         is.close();
68                         
69                         BUILD_VERSION = props.getProperty("build.version");
70                         if (BUILD_VERSION == null) {
71                                 throw new MissingResourceException(
72                                                 "build.version not found in property file",
73                                                 "build.properties", "build.version");
74                         }
75                         
76                         BUILD_SOURCE = props.getProperty("build.source");
77                         
78                 } catch (IOException e) {
79                         throw new MissingResourceException(
80                                         "Error reading build.properties",
81                                         "build.properties", "build.version");
82                 }
83         }
84         
85         
86         public static final String BODY_COMPONENT_INSERT_POSITION_KEY = "BodyComponentInsertPosition";
87         
88         
89         public static final String CONFIRM_DELETE_SIMULATION = "ConfirmDeleteSimulation";
90
91         
92         /**
93          * Node to this application's preferences.
94          */
95         public static final Preferences NODE;
96         
97         
98         static {
99                 Preferences root = Preferences.userRoot();
100                 if (DEBUG && CLEARPREFS) {
101                         try {
102                                 if (root.nodeExists(NODENAME)) {
103                                         root.node(NODENAME).removeNode();
104                                 }
105                         } catch (BackingStoreException e) {
106                                 throw new RuntimeException("Unable to clear preference node",e);
107                         }
108                 }
109                 NODE = root.node(NODENAME);
110         }
111         
112         
113         
114         
115         /////////  Default component attributes
116         
117         private static final HashMap<Class<?>,String> DEFAULT_COLORS = 
118                 new HashMap<Class<?>,String>();
119         static {
120                 DEFAULT_COLORS.put(BodyComponent.class, "0,0,240");
121                 DEFAULT_COLORS.put(FinSet.class, "0,0,200");
122                 DEFAULT_COLORS.put(LaunchLug.class, "0,0,180");
123                 DEFAULT_COLORS.put(InternalComponent.class, "170,0,100");
124                 DEFAULT_COLORS.put(MassObject.class, "0,0,0");
125                 DEFAULT_COLORS.put(RecoveryDevice.class, "255,0,0");
126         }
127         
128         
129         private static final HashMap<Class<?>,String> DEFAULT_LINE_STYLES = 
130                 new HashMap<Class<?>,String>();
131         static {
132                 DEFAULT_LINE_STYLES.put(RocketComponent.class, LineStyle.SOLID.name());
133                 DEFAULT_LINE_STYLES.put(MassObject.class, LineStyle.DASHED.name());
134         }
135         
136         
137         private static final Material DEFAULT_LINE_MATERIAL = 
138                 Databases.findMaterial(Material.Type.LINE, "Elastic cord (round 2mm, 1/16 in)", 0.0018);
139         private static final Material DEFAULT_SURFACE_MATERIAL = 
140                 Databases.findMaterial(Material.Type.SURFACE, "Ripstop nylon", 0.067);
141         private static final Material DEFAULT_BULK_MATERIAL = 
142                 Databases.findMaterial(Material.Type.BULK, "Cardboard", 680);
143
144         
145         //////////////////////
146         
147         
148         public static String getVersion() {
149                 return BUILD_VERSION;
150         }
151         
152         
153         public static String getBuildSource() {
154                 return BUILD_SOURCE;
155         }
156         
157         
158         
159         public static void storeVersion() {
160                 NODE.put("OpenRocketVersion", getVersion());
161         }
162         
163         
164         /**
165          * Returns a limited-range integer value from the preferences.  If the value 
166          * in the preferences is negative or greater than max, then the default value 
167          * is returned.
168          * 
169          * @param key  The preference to retrieve.
170          * @param max  Maximum allowed value for the choice.
171          * @param def  Default value.
172          * @return   The preference value.
173          */
174         public static int getChoise(String key, int max, int def) {
175                 int v = NODE.getInt(key, def);
176                 if ((v<0) || (v>max))
177                         return def;
178                 return v;
179         }
180         
181         
182         /**
183          * Helper method that puts an integer choice value into the preferences.
184          * 
185          * @param key     the preference key.
186          * @param value   the value to store.
187          */
188         public static void putChoise(String key, int value) {
189                 NODE.putInt(key, value);
190                 storeVersion();
191         }
192         
193         
194         
195         public static String getString(String key, String def) {
196                 return NODE.get(key, def);
197         }
198         
199         public static void putString(String key, String value) {
200                 NODE.put(key, value);
201                 storeVersion();
202         }
203         
204
205         
206         
207         //////////////////
208         
209         public static File getDefaultDirectory() {
210                 String file = NODE.get("defaultDirectory", null);
211                 if (file == null)
212                         return null;
213                 return new File(file);
214         }
215         
216         public static void setDefaultDirectory(File dir) {
217                 String d;
218                 if (dir == null) {
219                         d = null;
220                 } else {
221                         d = dir.getAbsolutePath();
222                 }
223                 NODE.put("defaultDirectory", d);
224                 storeVersion();
225         }
226         
227         
228         
229         public static Color getDefaultColor(Class<? extends RocketComponent> c) {
230                 String color = get("componentColors", c, DEFAULT_COLORS);
231                 if (color == null)
232                         return Color.BLACK;
233
234                 String[] rgb = color.split(",");
235                 if (rgb.length==3) {
236                         try {
237                                 int red = MathUtil.clamp(Integer.parseInt(rgb[0]),0,255);
238                                 int green = MathUtil.clamp(Integer.parseInt(rgb[1]),0,255);
239                                 int blue = MathUtil.clamp(Integer.parseInt(rgb[2]),0,255);
240                                 return new Color(red,green,blue);
241                         } catch (NumberFormatException ignore) { }
242                 }
243
244                 return Color.BLACK;
245         }
246         
247         public static void setDefaultColor(Class<? extends RocketComponent> c, Color color) {
248                 if (color==null)
249                         return;
250                 String string = color.getRed() + "," + color.getGreen() + "," + color.getBlue();
251                 set("componentColors", c, string);
252         }
253         
254         public static Color getMotorBorderColor() {
255                 // TODO: MEDIUM:  Motor color (settable?)
256                 return new Color(0,0,0,200);
257         }
258
259         
260         public static Color getMotorFillColor() {
261                 // TODO: MEDIUM:  Motor fill color (settable?)
262                 return new Color(0,0,0,100);
263         }
264         
265         
266         public static LineStyle getDefaultLineStyle(Class<? extends RocketComponent> c) {
267                 String value = get("componentStyle", c, DEFAULT_LINE_STYLES);
268                 try {
269                         return LineStyle.valueOf(value);
270                 } catch (Exception e) {
271                         return LineStyle.SOLID;
272                 }
273         }
274         
275         public static void setDefaultLineStyle(Class<? extends RocketComponent> c,
276                         LineStyle style) {
277                 if (style == null)
278                         return;
279                 set("componentStyle", c, style.name());
280         }
281         
282
283         /**
284          * Return the DPI setting of the monitor.  This is either the setting provided
285          * by the system or a user-specified DPI setting.
286          * 
287          * @return    the DPI setting to use.
288          */
289         public static double getDPI() {
290                 int dpi = NODE.getInt("DPI", 0);  // Tenths of a dpi
291                 
292                 if (dpi < 10) {
293                         dpi = Toolkit.getDefaultToolkit().getScreenResolution()*10;
294                 }
295                 if (dpi < 10)
296                         dpi = 960;
297                 
298                 return ((double)dpi)/10.0;
299         }
300         
301         
302         public static double getDefaultMach() {
303                 // TODO: HIGH: implement custom default mach number
304                 return 0.3;
305         }
306         
307         
308         
309         
310         public static Material getDefaultComponentMaterial(
311                         Class<? extends RocketComponent> componentClass,
312                         Material.Type type) {
313                 
314                 String material = get("componentMaterials", componentClass, null);
315                 if (material != null) {
316                         try {
317                                 Material m = Material.fromStorableString(material);
318                                 if (m.getType() == type)
319                                         return m;
320                         } catch (IllegalArgumentException ignore) { }
321                 }
322                 
323                 switch (type) {
324                 case LINE:
325                         return DEFAULT_LINE_MATERIAL;
326                 case SURFACE:
327                         return DEFAULT_SURFACE_MATERIAL;
328                 case BULK:
329                         return DEFAULT_BULK_MATERIAL;
330                 }
331                 throw new IllegalArgumentException("Unknown material type: "+type);
332         }
333         
334         public static void setDefaultComponentMaterial(
335                         Class<? extends RocketComponent> componentClass, Material material) {
336                 
337                 set("componentMaterials", componentClass, 
338                                 material==null ? null : material.toStorableString());
339         }
340         
341         
342         public static int getMaxThreadCount() {
343                 return Runtime.getRuntime().availableProcessors();
344         }
345         
346         
347         
348         public static Point getWindowPosition(Class<?> c) {
349                 int x, y;
350                 String pref = NODE.node("windows").get("position." + c.getCanonicalName(), null);
351                 
352                 if (pref == null)
353                         return null;
354                 
355                 if (pref.indexOf(',')<0)
356                         return null;
357                 
358                 try {
359                         x = Integer.parseInt(pref.substring(0,pref.indexOf(',')));
360                         y = Integer.parseInt(pref.substring(pref.indexOf(',')+1));
361                 } catch (NumberFormatException e) {
362                         return null;
363                 }
364                 return new Point(x,y);
365         }
366         
367         public static void setWindowPosition(Class<?> c, Point p) {
368                 NODE.node("windows").put("position." + c.getCanonicalName(), "" + p.x + "," + p.y);
369                 storeVersion();
370         }
371         
372         
373         
374
375         public static Dimension getWindowSize(Class<?> c) {
376                 int x, y;
377                 String pref = NODE.node("windows").get("size." + c.getCanonicalName(), null);
378                 
379                 if (pref == null)
380                         return null;
381                 
382                 if (pref.indexOf(',')<0)
383                         return null;
384                 
385                 try {
386                         x = Integer.parseInt(pref.substring(0,pref.indexOf(',')));
387                         y = Integer.parseInt(pref.substring(pref.indexOf(',')+1));
388                 } catch (NumberFormatException e) {
389                         return null;
390                 }
391                 return new Dimension(x,y);
392         }
393         
394         public static void setWindowSize(Class<?> c, Dimension d) {
395                 NODE.node("windows").put("size." + c.getCanonicalName(), "" + d.width + "," + d.height);
396                 storeVersion();
397         }
398         
399         
400         ////  Background flight data computation
401         
402         public static boolean computeFlightInBackground() {
403                 return NODE.getBoolean("backgroundFlight", true);
404         }
405         
406         public static Simulation getBackgroundSimulation(Rocket rocket) {
407                 Simulation s = new Simulation(rocket);
408                 SimulationConditions cond = s.getConditions();
409                 
410                 cond.setTimeStep(RK4Simulator.RECOMMENDED_TIME_STEP*2);
411                 cond.setWindSpeedAverage(1.0);
412                 cond.setWindSpeedDeviation(0.1);
413                 cond.setLaunchRodLength(5);
414                 return s;
415         }
416         
417         
418         
419         
420         /////////  Default unit storage
421         
422         public static void loadDefaultUnits() {
423                 Preferences prefs = NODE.node("units");
424                 try {
425                         
426                         for (String key: prefs.keys()) {
427                                 UnitGroup group = UnitGroup.UNITS.get(key);
428                                 if (group == null)
429                                         continue;
430                                 
431                                 group.setDefaultUnit(prefs.get(key, null));
432                         }
433                         
434                 } catch (BackingStoreException e) {
435                         System.err.println("BackingStoreException:");
436                         e.printStackTrace();
437                 }
438         }
439         
440         public static void storeDefaultUnits() {
441                 Preferences prefs = NODE.node("units");
442                 
443                 for (String key: UnitGroup.UNITS.keySet()) {
444                         UnitGroup group = UnitGroup.UNITS.get(key);
445                         if (group == null || group.getUnitCount() < 2)
446                                 continue;
447                         
448                         prefs.put(key, group.getDefaultUnit().getUnit());
449                 }
450         }
451         
452         
453         
454         ////  Helper methods
455         
456         private static String get(String directory, 
457                         Class<? extends RocketComponent> componentClass,
458                         Map<Class<?>, String> defaultMap) {
459
460                 // Search preferences
461                 Class<?> c = componentClass;
462                 Preferences prefs = NODE.node(directory);
463                 while (c!=null && RocketComponent.class.isAssignableFrom(c)) {
464                         String value = prefs.get(c.getSimpleName(), null);
465                         if (value != null)
466                                 return value;
467                         c = c.getSuperclass();
468                 }
469                 
470                 if (defaultMap == null)
471                         return null;
472
473                 // Search defaults
474                 c = componentClass;
475                 while (RocketComponent.class.isAssignableFrom(c)) {
476                         String value = defaultMap.get(c);
477                         if (value != null)
478                                 return value;
479                         c = c.getSuperclass();
480                 }
481                 
482                 return null;
483         }
484
485
486         private static void set(String directory, Class<? extends RocketComponent> componentClass,
487                         String value) {
488                 Preferences prefs = NODE.node(directory);
489                 if (value == null)
490                         prefs.remove(componentClass.getSimpleName());
491                 else
492                         prefs.put(componentClass.getSimpleName(), value);
493                 storeVersion();
494         }
495
496 }