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