ca82aa3f1961d89a379abda2274e4fd57351cd6f
[debian/openrocket] / core / src / net / sf / openrocket / startup / Preferences.java
1 package net.sf.openrocket.startup;\r
2 \r
3 import java.util.HashMap;\r
4 import java.util.Map;\r
5 import java.util.Set;\r
6 \r
7 import net.sf.openrocket.database.Databases;\r
8 import net.sf.openrocket.l10n.Translator;\r
9 import net.sf.openrocket.material.Material;\r
10 import net.sf.openrocket.preset.ComponentPreset;\r
11 import net.sf.openrocket.rocketcomponent.BodyComponent;\r
12 import net.sf.openrocket.rocketcomponent.FinSet;\r
13 import net.sf.openrocket.rocketcomponent.InternalComponent;\r
14 import net.sf.openrocket.rocketcomponent.LaunchLug;\r
15 import net.sf.openrocket.rocketcomponent.MassObject;\r
16 import net.sf.openrocket.rocketcomponent.RecoveryDevice;\r
17 import net.sf.openrocket.rocketcomponent.RocketComponent;\r
18 import net.sf.openrocket.util.BugException;\r
19 import net.sf.openrocket.util.BuildProperties;\r
20 import net.sf.openrocket.util.Color;\r
21 import net.sf.openrocket.util.LineStyle;\r
22 import net.sf.openrocket.util.MathUtil;\r
23 import net.sf.openrocket.util.UniqueID;\r
24 \r
25 public abstract class Preferences {\r
26 \r
27         /*\r
28          * Well known string keys to preferences.\r
29          * There are other strings out there in the source as well.\r
30          */\r
31         public static final String BODY_COMPONENT_INSERT_POSITION_KEY = "BodyComponentInsertPosition";\r
32         public static final String USER_THRUST_CURVES_KEY = "UserThrustCurves";\r
33         public static final String CONFIRM_DELETE_SIMULATION = "ConfirmDeleteSimulation";\r
34         // Preferences related to data export\r
35         public static final String EXPORT_FIELD_SEPARATOR = "ExportFieldSeparator";\r
36         public static final String EXPORT_SIMULATION_COMMENT = "ExportSimulationComment";\r
37         public static final String EXPORT_FIELD_NAME_COMMENT = "ExportFieldDescriptionComment";\r
38         public static final String EXPORT_EVENT_COMMENTS = "ExportEventComments";\r
39         public static final String EXPORT_COMMENT_CHARACTER = "ExportCommentCharacter";\r
40         public static final String USER_LOCAL = "locale";\r
41         \r
42         public static final String PLOT_SHOW_POINTS = "ShowPlotPoints";\r
43         \r
44         private static final String CHECK_UPDATES = "CheckUpdates";\r
45         public static final String LAST_UPDATE = "LastUpdateVersion";\r
46         \r
47         public static final String MOTOR_DIAMETER_FILTER = "MotorDiameterMatch";\r
48         public static final String MOTOR_HIDE_SIMILAR = "MotorHideSimilar";\r
49 \r
50         // Node names\r
51         public static final String PREFERRED_THRUST_CURVE_MOTOR_NODE = "preferredThrustCurveMotors";\r
52 \r
53         /*\r
54          * ******************************************************************************************\r
55          * \r
56          * Abstract methods which must be implemented by any derived class.\r
57          */\r
58         public abstract boolean getBoolean( String key, boolean defaultValue );\r
59         public abstract void putBoolean( String key, boolean value );\r
60 \r
61         public abstract int getInt( String key, int defaultValue);\r
62         public abstract void putInt( String key, int value );\r
63 \r
64         public abstract double getDouble( String key, double defaultValue );\r
65         public abstract void putDouble( String key, double value );\r
66 \r
67         public abstract String getString( String key, String defaultValue );\r
68         public abstract void putString( String key, String value );\r
69         \r
70         /**\r
71          * Directory represents a way to collect multiple keys together.  Implementors may\r
72          * choose to concatenate the directory with the key using some special character.\r
73          * @param directory\r
74          * @param key\r
75          * @param defaultValue\r
76          * @return\r
77          */\r
78         public abstract String getString( String directory, String key, String defaultValue);\r
79 \r
80         public abstract void putString( String directory, String key, String value );\r
81 \r
82         /*\r
83          * ******************************************************************************************\r
84          */\r
85         public final boolean getCheckUpdates() {\r
86                 return this.getBoolean(CHECK_UPDATES, BuildProperties.getDefaultCheckUpdates());\r
87         }\r
88         \r
89         public final void setCheckUpdates(boolean check) {\r
90                 this.putBoolean(CHECK_UPDATES, check);\r
91         }\r
92         \r
93         public final double getDefaultMach() {\r
94                 // TODO: HIGH: implement custom default mach number\r
95                 return 0.3;\r
96         }\r
97         \r
98         /**\r
99          * Return the OpenRocket unique ID.\r
100          * \r
101          * @return      a random ID string that stays constant between OpenRocket executions\r
102          */\r
103         public final String getUniqueID() {\r
104                 String id = this.getString("id", null);\r
105                 if (id == null) {\r
106                         id = UniqueID.uuid();\r
107                         this.putString("id", id);\r
108                 }\r
109                 return id;\r
110         }\r
111         \r
112         /**\r
113          * Returns a limited-range integer value from the preferences.  If the value \r
114          * in the preferences is negative or greater than max, then the default value \r
115          * is returned.\r
116          * \r
117          * @param key  The preference to retrieve.\r
118          * @param max  Maximum allowed value for the choice.\r
119          * @param def  Default value.\r
120          * @return   The preference value.\r
121          */\r
122         public final int getChoice(String key, int max, int def) {\r
123                 int v = this.getInt(key, def);\r
124                 if ((v < 0) || (v > max))\r
125                         return def;\r
126                 return v;\r
127         }\r
128         \r
129         /**\r
130          * Helper method that puts an integer choice value into the preferences.\r
131          * \r
132          * @param key     the preference key.\r
133          * @param value   the value to store.\r
134          */\r
135         public final void putChoice(String key, int value) {\r
136                 this.putInt(key, value);\r
137         }\r
138         \r
139         /**\r
140          * Retrieve an enum value from the user preferences.\r
141          * \r
142          * @param <T>   the enum type\r
143          * @param key   the key\r
144          * @param def   the default value, cannot be null\r
145          * @return              the value in the preferences, or the default value\r
146          */\r
147         public final <T extends Enum<T>> T getEnum(String key, T def) {\r
148                 if (def == null) {\r
149                         throw new BugException("Default value cannot be null");\r
150                 }\r
151                 \r
152                 String value = getString(key, null);\r
153                 if (value == null) {\r
154                         return def;\r
155                 }\r
156                 \r
157                 try {\r
158                         return Enum.valueOf(def.getDeclaringClass(), value);\r
159                 } catch (IllegalArgumentException e) {\r
160                         return def;\r
161                 }\r
162         }\r
163         \r
164         /**\r
165          * Store an enum value to the user preferences.\r
166          * \r
167          * @param key           the key\r
168          * @param value         the value to store, or null to remove the value\r
169          */\r
170         public final void putEnum(String key, Enum<?> value) {\r
171                 if (value == null) {\r
172                         putString(key, null);\r
173                 } else {\r
174                         putString(key, value.name());\r
175                 }\r
176         }\r
177 \r
178         public Color getDefaultColor(Class<? extends RocketComponent> c) {\r
179                 String color = get("componentColors", c, DEFAULT_COLORS);\r
180                 if (color == null)\r
181                         return Color.BLACK;\r
182                 \r
183                 Color clr = parseColor(color);\r
184                 if (clr != null) {\r
185                         return clr;\r
186                 } else {\r
187                         return Color.BLACK;\r
188                 }\r
189         }\r
190         \r
191         public final void setDefaultColor(Class<? extends RocketComponent> c, Color color) {\r
192                 if (color == null)\r
193                         return;\r
194                 putString("componentColors", c.getSimpleName(), stringifyColor(color));\r
195         }\r
196 \r
197 \r
198         /**\r
199          * Retrieve a Line style for the given component.\r
200          * @param c\r
201          * @return\r
202          */\r
203         public final LineStyle getDefaultLineStyle(Class<? extends RocketComponent> c) {\r
204                 String value = get("componentStyle", c, DEFAULT_LINE_STYLES);\r
205                 try {\r
206                         return LineStyle.valueOf(value);\r
207                 } catch (Exception e) {\r
208                         return LineStyle.SOLID;\r
209                 }\r
210         }\r
211         \r
212         /**\r
213          * Set a default line style for the given component.\r
214          * @param c\r
215          * @param style\r
216          */\r
217         public final void setDefaultLineStyle(Class<? extends RocketComponent> c,\r
218                         LineStyle style) {\r
219                 if (style == null)\r
220                         return;\r
221                 putString("componentStyle", c.getSimpleName(), style.name());\r
222         }\r
223         \r
224         /**\r
225          * Get the default material type for the given component.\r
226          * @param componentClass\r
227          * @param type the Material.Type to return.\r
228          * @return\r
229          */\r
230         public Material getDefaultComponentMaterial(\r
231                         Class<? extends RocketComponent> componentClass,\r
232                         Material.Type type) {\r
233                 \r
234                 String material = get("componentMaterials", componentClass, null);\r
235                 if (material != null) {\r
236                         try {\r
237                                 Material m = Material.fromStorableString(material);\r
238                                 if (m.getType() == type)\r
239                                         return m;\r
240                         } catch (IllegalArgumentException ignore) {\r
241                         }\r
242                 }\r
243                 \r
244                 switch (type) {\r
245                 case LINE:\r
246                         return DefaultMaterialHolder.DEFAULT_LINE_MATERIAL;\r
247                 case SURFACE:\r
248                         return DefaultMaterialHolder.DEFAULT_SURFACE_MATERIAL;\r
249                 case BULK:\r
250                         return DefaultMaterialHolder.DEFAULT_BULK_MATERIAL;\r
251                 }\r
252                 throw new IllegalArgumentException("Unknown material type: " + type);\r
253         }\r
254         \r
255         /**\r
256          * Set the default material for a component type.\r
257          * @param componentClass\r
258          * @param material\r
259          */\r
260         public void setDefaultComponentMaterial(\r
261                         Class<? extends RocketComponent> componentClass, Material material) {\r
262                 \r
263                 putString("componentMaterials", componentClass.getSimpleName(),\r
264                                 material == null ? null : material.toStorableString());\r
265         }\r
266 \r
267         /**\r
268          * get a net.sf.openrocket.util.Color object for the given key.\r
269          * @param key\r
270          * @param defaultValue\r
271          * @return\r
272          */\r
273         public final Color getColor( String key, Color defaultValue ) {\r
274                 Color c = parseColor( getString(key,null) );\r
275                 if ( c == null ) {\r
276                         return defaultValue;\r
277                 }\r
278                 return c;\r
279         }\r
280         \r
281         /**\r
282          * set a net.sf.openrocket.util.Color preference value for the given key.\r
283          * @param key\r
284          * @param value\r
285          */\r
286         public final void putColor( String key, Color value ) {\r
287                 putString( key, stringifyColor(value) );\r
288         }\r
289 \r
290         /**\r
291          * Helper function to convert a string representation into a net.sf.openrocket.util.Color object.\r
292          * @param color\r
293          * @return\r
294          */\r
295         protected static Color parseColor(String color) {\r
296                 if (color == null) {\r
297                         return null;\r
298                 }\r
299                 \r
300                 String[] rgb = color.split(",");\r
301                 if (rgb.length == 3) {\r
302                         try {\r
303                                 int red = MathUtil.clamp(Integer.parseInt(rgb[0]), 0, 255);\r
304                                 int green = MathUtil.clamp(Integer.parseInt(rgb[1]), 0, 255);\r
305                                 int blue = MathUtil.clamp(Integer.parseInt(rgb[2]), 0, 255);\r
306                                 return new Color(red, green, blue);\r
307                         } catch (NumberFormatException ignore) {\r
308                         }\r
309                 }\r
310                 return null;\r
311         }\r
312         \r
313         /**\r
314          * Helper function to convert a net.sf.openrocket.util.Color object into a\r
315          * String before storing in a preference.\r
316          * @param color\r
317          * @return\r
318          */\r
319         protected static String stringifyColor(Color color) {\r
320                 String string = color.getRed() + "," + color.getGreen() + "," + color.getBlue();\r
321                 return string;\r
322         }\r
323 \r
324         /**\r
325          * Special helper function which allows for a map of default values.\r
326          * \r
327          * First getString(directory,componentClass.getSimpleName(), null) is invoked,\r
328          * if the returned value is null, the defaultMap is consulted for a value.\r
329          * \r
330          * @param directory\r
331          * @param componentClass\r
332          * @param defaultMap\r
333          * @return\r
334          */\r
335         protected String get(String directory,\r
336                         Class<? extends RocketComponent> componentClass,\r
337                         Map<Class<?>, String> defaultMap) {\r
338                 \r
339                 // Search preferences\r
340                 Class<?> c = componentClass;\r
341                 while (c != null && RocketComponent.class.isAssignableFrom(c)) {\r
342                         String value = this.getString(directory, c.getSimpleName(), null);\r
343                         if (value != null)\r
344                                 return value;\r
345                         c = c.getSuperclass();\r
346                 }\r
347                 \r
348                 if (defaultMap == null)\r
349                         return null;\r
350                 \r
351                 // Search defaults\r
352                 c = componentClass;\r
353                 while (RocketComponent.class.isAssignableFrom(c)) {\r
354                         String value = defaultMap.get(c);\r
355                         if (value != null)\r
356                                 return value;\r
357                         c = c.getSuperclass();\r
358                 }\r
359                 \r
360                 return null;\r
361         }\r
362 \r
363         public abstract void addUserMaterial(Material m);\r
364         public abstract Set<Material> getUserMaterials();\r
365         public abstract void removeUserMaterial(Material m);\r
366 \r
367         public abstract void setComponentFavorite( ComponentPreset preset, ComponentPreset.Type type, boolean favorite );\r
368         public abstract Set<String> getComponentFavorites( ComponentPreset.Type type );\r
369 \r
370         /*\r
371          * Map of default line styles\r
372          */\r
373         private static final HashMap<Class<?>, String> DEFAULT_LINE_STYLES =\r
374                         new HashMap<Class<?>, String>();\r
375         static {\r
376                 DEFAULT_LINE_STYLES.put(RocketComponent.class, LineStyle.SOLID.name());\r
377                 DEFAULT_LINE_STYLES.put(MassObject.class, LineStyle.DASHED.name());\r
378         }\r
379         \r
380         /*\r
381          * Within a holder class so they will load only when needed.\r
382          */\r
383         private static class DefaultMaterialHolder {\r
384                 private static final Translator trans = Application.getTranslator();\r
385                 \r
386                 //// Elastic cord (round 2mm, 1/16 in)\r
387                 private static final Material DEFAULT_LINE_MATERIAL =\r
388                                 Databases.findMaterial(Material.Type.LINE, "Elasticcordround2mm", trans.get("Databases.materials.Elasticcordround2mm"),0.0018);\r
389                 //// Ripstop nylon\r
390                 private static final Material DEFAULT_SURFACE_MATERIAL =\r
391                                 Databases.findMaterial(Material.Type.SURFACE, "Ripstopnylon", trans.get("Databases.materials.Ripstopnylon"), 0.067);\r
392                 //// Cardboard\r
393                 private static final Material DEFAULT_BULK_MATERIAL =\r
394                                 Databases.findMaterial(Material.Type.BULK, "Cardboard", trans.get("Databases.materials.Cardboard"), 680);\r
395         }\r
396         \r
397         private static final HashMap<Class<?>, String> DEFAULT_COLORS =\r
398                         new HashMap<Class<?>, String>();\r
399         static {\r
400                 DEFAULT_COLORS.put(BodyComponent.class, "0,0,240");\r
401                 DEFAULT_COLORS.put(FinSet.class, "0,0,200");\r
402                 DEFAULT_COLORS.put(LaunchLug.class, "0,0,180");\r
403                 DEFAULT_COLORS.put(InternalComponent.class, "170,0,100");\r
404                 DEFAULT_COLORS.put(MassObject.class, "0,0,0");\r
405                 DEFAULT_COLORS.put(RecoveryDevice.class, "255,0,0");\r
406         }\r
407         \r
408 \r
409 \r
410 }\r