1 package net.sf.openrocket.util;
4 import java.awt.Dimension;
6 import java.awt.Toolkit;
8 import java.io.IOException;
9 import java.io.InputStream;
10 import java.util.HashMap;
12 import java.util.MissingResourceException;
13 import java.util.Properties;
14 import java.util.prefs.BackingStoreException;
15 import java.util.prefs.Preferences;
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.FlightDataBranch;
29 import net.sf.openrocket.simulation.RK4Simulator;
30 import net.sf.openrocket.simulation.SimulationConditions;
31 import net.sf.openrocket.unit.UnitGroup;
37 * Whether to use the debug-node instead of the normal node.
39 public static final boolean DEBUG = false;
42 * Whether to clear all preferences at application startup. This has an effect only
45 public static final boolean CLEARPREFS = true;
48 * The node name to use in the Java preferences storage.
50 public static final String NODENAME = (DEBUG?"OpenRocket-debug":"OpenRocket");
54 private static final String BUILD_VERSION;
55 private static final String BUILD_SOURCE;
59 InputStream is = ClassLoader.getSystemResourceAsStream("build.properties");
61 throw new MissingResourceException(
62 "build.properties not found, distribution built wrong",
63 "build.properties", "build.version");
66 Properties props = new Properties();
70 BUILD_VERSION = props.getProperty("build.version");
71 if (BUILD_VERSION == null) {
72 throw new MissingResourceException(
73 "build.version not found in property file",
74 "build.properties", "build.version");
77 BUILD_SOURCE = props.getProperty("build.source");
79 } catch (IOException e) {
80 throw new MissingResourceException(
81 "Error reading build.properties",
82 "build.properties", "build.version");
87 public static final String BODY_COMPONENT_INSERT_POSITION_KEY = "BodyComponentInsertPosition";
90 public static final String CONFIRM_DELETE_SIMULATION = "ConfirmDeleteSimulation";
92 // Preferences related to data export
93 public static final String EXPORT_FIELD_SEPARATOR = "ExportFieldSeparator";
94 public static final String EXPORT_SIMULATION_COMMENT = "ExportSimulationComment";
95 public static final String EXPORT_FIELD_NAME_COMMENT = "ExportFieldDescriptionComment";
96 public static final String EXPORT_EVENT_COMMENTS = "ExportEventComments";
97 public static final String EXPORT_COMMENT_CHARACTER = "ExportCommentCharacter";
101 * Node to this application's preferences.
102 * @deprecated Use the static methods instead.
105 public static final Preferences NODE;
106 private static final Preferences PREFNODE;
110 Preferences root = Preferences.userRoot();
111 if (DEBUG && CLEARPREFS) {
113 if (root.nodeExists(NODENAME)) {
114 root.node(NODENAME).removeNode();
116 } catch (BackingStoreException e) {
117 throw new RuntimeException("Unable to clear preference node",e);
120 PREFNODE = root.node(NODENAME);
127 ///////// Default component attributes
129 private static final HashMap<Class<?>,String> DEFAULT_COLORS =
130 new HashMap<Class<?>,String>();
132 DEFAULT_COLORS.put(BodyComponent.class, "0,0,240");
133 DEFAULT_COLORS.put(FinSet.class, "0,0,200");
134 DEFAULT_COLORS.put(LaunchLug.class, "0,0,180");
135 DEFAULT_COLORS.put(InternalComponent.class, "170,0,100");
136 DEFAULT_COLORS.put(MassObject.class, "0,0,0");
137 DEFAULT_COLORS.put(RecoveryDevice.class, "255,0,0");
141 private static final HashMap<Class<?>,String> DEFAULT_LINE_STYLES =
142 new HashMap<Class<?>,String>();
144 DEFAULT_LINE_STYLES.put(RocketComponent.class, LineStyle.SOLID.name());
145 DEFAULT_LINE_STYLES.put(MassObject.class, LineStyle.DASHED.name());
149 private static final Material DEFAULT_LINE_MATERIAL =
150 Databases.findMaterial(Material.Type.LINE, "Elastic cord (round 2mm, 1/16 in)", 0.0018);
151 private static final Material DEFAULT_SURFACE_MATERIAL =
152 Databases.findMaterial(Material.Type.SURFACE, "Ripstop nylon", 0.067);
153 private static final Material DEFAULT_BULK_MATERIAL =
154 Databases.findMaterial(Material.Type.BULK, "Cardboard", 680);
157 //////////////////////
160 public static String getVersion() {
161 return BUILD_VERSION;
165 public static String getBuildSource() {
171 public static void storeVersion() {
172 PREFNODE.put("OpenRocketVersion", getVersion());
177 * Returns a limited-range integer value from the preferences. If the value
178 * in the preferences is negative or greater than max, then the default value
181 * @param key The preference to retrieve.
182 * @param max Maximum allowed value for the choice.
183 * @param def Default value.
184 * @return The preference value.
186 public static int getChoise(String key, int max, int def) {
187 int v = PREFNODE.getInt(key, def);
188 if ((v<0) || (v>max))
195 * Helper method that puts an integer choice value into the preferences.
197 * @param key the preference key.
198 * @param value the value to store.
200 public static void putChoise(String key, int value) {
201 PREFNODE.putInt(key, value);
207 public static String getString(String key, String def) {
208 return PREFNODE.get(key, def);
211 public static void putString(String key, String value) {
212 PREFNODE.put(key, value);
217 public static boolean getBoolean(String key, boolean def) {
218 return PREFNODE.getBoolean(key, def);
221 public static void putBoolean(String key, boolean value) {
222 PREFNODE.putBoolean(key, value);
230 public static File getDefaultDirectory() {
231 String file = PREFNODE.get("defaultDirectory", null);
234 return new File(file);
237 public static void setDefaultDirectory(File dir) {
242 d = dir.getAbsolutePath();
244 PREFNODE.put("defaultDirectory", d);
250 public static Color getDefaultColor(Class<? extends RocketComponent> c) {
251 String color = get("componentColors", c, DEFAULT_COLORS);
255 String[] rgb = color.split(",");
258 int red = MathUtil.clamp(Integer.parseInt(rgb[0]),0,255);
259 int green = MathUtil.clamp(Integer.parseInt(rgb[1]),0,255);
260 int blue = MathUtil.clamp(Integer.parseInt(rgb[2]),0,255);
261 return new Color(red,green,blue);
262 } catch (NumberFormatException ignore) { }
268 public static void setDefaultColor(Class<? extends RocketComponent> c, Color color) {
271 String string = color.getRed() + "," + color.getGreen() + "," + color.getBlue();
272 set("componentColors", c, string);
275 public static Color getMotorBorderColor() {
276 // TODO: MEDIUM: Motor color (settable?)
277 return new Color(0,0,0,200);
281 public static Color getMotorFillColor() {
282 // TODO: MEDIUM: Motor fill color (settable?)
283 return new Color(0,0,0,100);
287 public static LineStyle getDefaultLineStyle(Class<? extends RocketComponent> c) {
288 String value = get("componentStyle", c, DEFAULT_LINE_STYLES);
290 return LineStyle.valueOf(value);
291 } catch (Exception e) {
292 return LineStyle.SOLID;
296 public static void setDefaultLineStyle(Class<? extends RocketComponent> c,
300 set("componentStyle", c, style.name());
305 * Return the DPI setting of the monitor. This is either the setting provided
306 * by the system or a user-specified DPI setting.
308 * @return the DPI setting to use.
310 public static double getDPI() {
311 int dpi = PREFNODE.getInt("DPI", 0); // Tenths of a dpi
314 dpi = Toolkit.getDefaultToolkit().getScreenResolution()*10;
319 return ((double)dpi)/10.0;
323 public static double getDefaultMach() {
324 // TODO: HIGH: implement custom default mach number
331 public static Material getDefaultComponentMaterial(
332 Class<? extends RocketComponent> componentClass,
333 Material.Type type) {
335 String material = get("componentMaterials", componentClass, null);
336 if (material != null) {
338 Material m = Material.fromStorableString(material, false);
339 if (m.getType() == type)
341 } catch (IllegalArgumentException ignore) { }
346 return DEFAULT_LINE_MATERIAL;
348 return DEFAULT_SURFACE_MATERIAL;
350 return DEFAULT_BULK_MATERIAL;
352 throw new IllegalArgumentException("Unknown material type: "+type);
355 public static void setDefaultComponentMaterial(
356 Class<? extends RocketComponent> componentClass, Material material) {
358 set("componentMaterials", componentClass,
359 material==null ? null : material.toStorableString());
363 public static int getMaxThreadCount() {
364 return Runtime.getRuntime().availableProcessors();
369 public static Point getWindowPosition(Class<?> c) {
371 String pref = PREFNODE.node("windows").get("position." + c.getCanonicalName(), null);
376 if (pref.indexOf(',')<0)
380 x = Integer.parseInt(pref.substring(0,pref.indexOf(',')));
381 y = Integer.parseInt(pref.substring(pref.indexOf(',')+1));
382 } catch (NumberFormatException e) {
385 return new Point(x,y);
388 public static void setWindowPosition(Class<?> c, Point p) {
389 PREFNODE.node("windows").put("position." + c.getCanonicalName(), "" + p.x + "," + p.y);
396 public static Dimension getWindowSize(Class<?> c) {
398 String pref = PREFNODE.node("windows").get("size." + c.getCanonicalName(), null);
403 if (pref.indexOf(',')<0)
407 x = Integer.parseInt(pref.substring(0,pref.indexOf(',')));
408 y = Integer.parseInt(pref.substring(pref.indexOf(',')+1));
409 } catch (NumberFormatException e) {
412 return new Dimension(x,y);
415 public static void setWindowSize(Class<?> c, Dimension d) {
416 PREFNODE.node("windows").put("size." + c.getCanonicalName(), "" + d.width + "," + d.height);
421 //// Background flight data computation
423 public static boolean computeFlightInBackground() {
424 return PREFNODE.getBoolean("backgroundFlight", true);
427 public static Simulation getBackgroundSimulation(Rocket rocket) {
428 Simulation s = new Simulation(rocket);
429 SimulationConditions cond = s.getConditions();
431 cond.setTimeStep(RK4Simulator.RECOMMENDED_TIME_STEP*2);
432 cond.setWindSpeedAverage(1.0);
433 cond.setWindSpeedDeviation(0.1);
434 cond.setLaunchRodLength(5);
440 ///////// Export variables
442 public static boolean isExportSelected(FlightDataBranch.Type type) {
443 Preferences prefs = PREFNODE.node("exports");
444 return prefs.getBoolean(type.getName(), false);
447 public static void setExportSelected(FlightDataBranch.Type type, boolean selected) {
448 Preferences prefs = PREFNODE.node("exports");
449 prefs.putBoolean(type.getName(), selected);
454 ///////// Default unit storage
456 public static void loadDefaultUnits() {
457 Preferences prefs = PREFNODE.node("units");
460 for (String key: prefs.keys()) {
461 UnitGroup group = UnitGroup.UNITS.get(key);
465 group.setDefaultUnit(prefs.get(key, null));
468 } catch (BackingStoreException e) {
469 System.err.println("BackingStoreException:");
474 public static void storeDefaultUnits() {
475 Preferences prefs = PREFNODE.node("units");
477 for (String key: UnitGroup.UNITS.keySet()) {
478 UnitGroup group = UnitGroup.UNITS.get(key);
479 if (group == null || group.getUnitCount() < 2)
482 prefs.put(key, group.getDefaultUnit().getUnit());
490 private static String get(String directory,
491 Class<? extends RocketComponent> componentClass,
492 Map<Class<?>, String> defaultMap) {
494 // Search preferences
495 Class<?> c = componentClass;
496 Preferences prefs = PREFNODE.node(directory);
497 while (c!=null && RocketComponent.class.isAssignableFrom(c)) {
498 String value = prefs.get(c.getSimpleName(), null);
501 c = c.getSuperclass();
504 if (defaultMap == null)
509 while (RocketComponent.class.isAssignableFrom(c)) {
510 String value = defaultMap.get(c);
513 c = c.getSuperclass();
520 private static void set(String directory, Class<? extends RocketComponent> componentClass,
522 Preferences prefs = PREFNODE.node(directory);
524 prefs.remove(componentClass.getSimpleName());
526 prefs.put(componentClass.getSimpleName(), value);