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