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.RK4Simulator;
29 import net.sf.openrocket.simulation.SimulationConditions;
30 import net.sf.openrocket.unit.UnitGroup;
36 * Whether to use the debug-node instead of the normal node.
38 public static final boolean DEBUG = false;
41 * Whether to clear all preferences at application startup. This has an effect only
44 public static final boolean CLEARPREFS = true;
47 * The node name to use in the Java preferences storage.
49 public static final String NODENAME = (DEBUG?"OpenRocket-debug":"OpenRocket");
53 private static final String BUILD_VERSION;
54 private static final String BUILD_SOURCE;
58 InputStream is = ClassLoader.getSystemResourceAsStream("build.properties");
60 throw new MissingResourceException(
61 "build.properties not found, distribution built wrong",
62 "build.properties", "build.version");
65 Properties props = new Properties();
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");
76 BUILD_SOURCE = props.getProperty("build.source");
78 } catch (IOException e) {
79 throw new MissingResourceException(
80 "Error reading build.properties",
81 "build.properties", "build.version");
86 public static final String BODY_COMPONENT_INSERT_POSITION_KEY = "BodyComponentInsertPosition";
89 public static final String CONFIRM_DELETE_SIMULATION = "ConfirmDeleteSimulation";
93 * Node to this application's preferences.
95 public static final Preferences NODE;
99 Preferences root = Preferences.userRoot();
100 if (DEBUG && CLEARPREFS) {
102 if (root.nodeExists(NODENAME)) {
103 root.node(NODENAME).removeNode();
105 } catch (BackingStoreException e) {
106 throw new RuntimeException("Unable to clear preference node",e);
109 NODE = root.node(NODENAME);
115 ///////// Default component attributes
117 private static final HashMap<Class<?>,String> DEFAULT_COLORS =
118 new HashMap<Class<?>,String>();
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");
129 private static final HashMap<Class<?>,String> DEFAULT_LINE_STYLES =
130 new HashMap<Class<?>,String>();
132 DEFAULT_LINE_STYLES.put(RocketComponent.class, LineStyle.SOLID.name());
133 DEFAULT_LINE_STYLES.put(MassObject.class, LineStyle.DASHED.name());
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);
145 //////////////////////
148 public static String getVersion() {
149 return BUILD_VERSION;
153 public static String getBuildSource() {
159 public static void storeVersion() {
160 NODE.put("OpenRocketVersion", getVersion());
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
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.
174 public static int getChoise(String key, int max, int def) {
175 int v = NODE.getInt(key, def);
176 if ((v<0) || (v>max))
183 * Helper method that puts an integer choice value into the preferences.
185 * @param key the preference key.
186 * @param value the value to store.
188 public static void putChoise(String key, int value) {
189 NODE.putInt(key, value);
195 public static String getString(String key, String def) {
196 return NODE.get(key, def);
199 public static void putString(String key, String value) {
200 NODE.put(key, value);
209 public static File getDefaultDirectory() {
210 String file = NODE.get("defaultDirectory", null);
213 return new File(file);
216 public static void setDefaultDirectory(File dir) {
221 d = dir.getAbsolutePath();
223 NODE.put("defaultDirectory", d);
229 public static Color getDefaultColor(Class<? extends RocketComponent> c) {
230 String color = get("componentColors", c, DEFAULT_COLORS);
234 String[] rgb = color.split(",");
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) { }
247 public static void setDefaultColor(Class<? extends RocketComponent> c, Color color) {
250 String string = color.getRed() + "," + color.getGreen() + "," + color.getBlue();
251 set("componentColors", c, string);
254 public static Color getMotorBorderColor() {
255 // TODO: MEDIUM: Motor color (settable?)
256 return new Color(0,0,0,200);
260 public static Color getMotorFillColor() {
261 // TODO: MEDIUM: Motor fill color (settable?)
262 return new Color(0,0,0,100);
266 public static LineStyle getDefaultLineStyle(Class<? extends RocketComponent> c) {
267 String value = get("componentStyle", c, DEFAULT_LINE_STYLES);
269 return LineStyle.valueOf(value);
270 } catch (Exception e) {
271 return LineStyle.SOLID;
275 public static void setDefaultLineStyle(Class<? extends RocketComponent> c,
279 set("componentStyle", c, style.name());
284 * Return the DPI setting of the monitor. This is either the setting provided
285 * by the system or a user-specified DPI setting.
287 * @return the DPI setting to use.
289 public static double getDPI() {
290 int dpi = NODE.getInt("DPI", 0); // Tenths of a dpi
293 dpi = Toolkit.getDefaultToolkit().getScreenResolution()*10;
298 return ((double)dpi)/10.0;
302 public static double getDefaultMach() {
303 // TODO: HIGH: implement custom default mach number
310 public static Material getDefaultComponentMaterial(
311 Class<? extends RocketComponent> componentClass,
312 Material.Type type) {
314 String material = get("componentMaterials", componentClass, null);
315 if (material != null) {
317 Material m = Material.fromStorableString(material);
318 if (m.getType() == type)
320 } catch (IllegalArgumentException ignore) { }
325 return DEFAULT_LINE_MATERIAL;
327 return DEFAULT_SURFACE_MATERIAL;
329 return DEFAULT_BULK_MATERIAL;
331 throw new IllegalArgumentException("Unknown material type: "+type);
334 public static void setDefaultComponentMaterial(
335 Class<? extends RocketComponent> componentClass, Material material) {
337 set("componentMaterials", componentClass,
338 material==null ? null : material.toStorableString());
342 public static int getMaxThreadCount() {
343 return Runtime.getRuntime().availableProcessors();
348 public static Point getWindowPosition(Class<?> c) {
350 String pref = NODE.node("windows").get("position." + c.getCanonicalName(), null);
355 if (pref.indexOf(',')<0)
359 x = Integer.parseInt(pref.substring(0,pref.indexOf(',')));
360 y = Integer.parseInt(pref.substring(pref.indexOf(',')+1));
361 } catch (NumberFormatException e) {
364 return new Point(x,y);
367 public static void setWindowPosition(Class<?> c, Point p) {
368 NODE.node("windows").put("position." + c.getCanonicalName(), "" + p.x + "," + p.y);
375 public static Dimension getWindowSize(Class<?> c) {
377 String pref = NODE.node("windows").get("size." + c.getCanonicalName(), null);
382 if (pref.indexOf(',')<0)
386 x = Integer.parseInt(pref.substring(0,pref.indexOf(',')));
387 y = Integer.parseInt(pref.substring(pref.indexOf(',')+1));
388 } catch (NumberFormatException e) {
391 return new Dimension(x,y);
394 public static void setWindowSize(Class<?> c, Dimension d) {
395 NODE.node("windows").put("size." + c.getCanonicalName(), "" + d.width + "," + d.height);
400 //// Background flight data computation
402 public static boolean computeFlightInBackground() {
403 return NODE.getBoolean("backgroundFlight", true);
406 public static Simulation getBackgroundSimulation(Rocket rocket) {
407 Simulation s = new Simulation(rocket);
408 SimulationConditions cond = s.getConditions();
410 cond.setTimeStep(RK4Simulator.RECOMMENDED_TIME_STEP*2);
411 cond.setWindSpeedAverage(1.0);
412 cond.setWindSpeedDeviation(0.1);
413 cond.setLaunchRodLength(5);
420 ///////// Default unit storage
422 public static void loadDefaultUnits() {
423 Preferences prefs = NODE.node("units");
426 for (String key: prefs.keys()) {
427 UnitGroup group = UnitGroup.UNITS.get(key);
431 group.setDefaultUnit(prefs.get(key, null));
434 } catch (BackingStoreException e) {
435 System.err.println("BackingStoreException:");
440 public static void storeDefaultUnits() {
441 Preferences prefs = NODE.node("units");
443 for (String key: UnitGroup.UNITS.keySet()) {
444 UnitGroup group = UnitGroup.UNITS.get(key);
445 if (group == null || group.getUnitCount() < 2)
448 prefs.put(key, group.getDefaultUnit().getUnit());
456 private static String get(String directory,
457 Class<? extends RocketComponent> componentClass,
458 Map<Class<?>, String> defaultMap) {
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);
467 c = c.getSuperclass();
470 if (defaultMap == null)
475 while (RocketComponent.class.isAssignableFrom(c)) {
476 String value = defaultMap.get(c);
479 c = c.getSuperclass();
486 private static void set(String directory, Class<? extends RocketComponent> componentClass,
488 Preferences prefs = NODE.node(directory);
490 prefs.remove(componentClass.getSimpleName());
492 prefs.put(componentClass.getSimpleName(), value);