1 package net.sf.openrocket.gui.util;
4 import java.awt.Dimension;
7 import java.util.ArrayList;
8 import java.util.Collections;
9 import java.util.HashMap;
10 import java.util.HashSet;
11 import java.util.List;
12 import java.util.Locale;
14 import java.util.prefs.BackingStoreException;
15 import java.util.prefs.Preferences;
17 import net.sf.openrocket.arch.SystemInfo;
18 import net.sf.openrocket.document.Simulation;
19 import net.sf.openrocket.gui.main.ExceptionHandler;
20 import net.sf.openrocket.logging.LogHelper;
21 import net.sf.openrocket.material.Material;
22 import net.sf.openrocket.rocketcomponent.BodyComponent;
23 import net.sf.openrocket.rocketcomponent.FinSet;
24 import net.sf.openrocket.rocketcomponent.InternalComponent;
25 import net.sf.openrocket.rocketcomponent.LaunchLug;
26 import net.sf.openrocket.rocketcomponent.MassObject;
27 import net.sf.openrocket.rocketcomponent.RecoveryDevice;
28 import net.sf.openrocket.rocketcomponent.Rocket;
29 import net.sf.openrocket.rocketcomponent.RocketComponent;
30 import net.sf.openrocket.simulation.FlightDataType;
31 import net.sf.openrocket.simulation.RK4SimulationStepper;
32 import net.sf.openrocket.simulation.SimulationOptions;
33 import net.sf.openrocket.startup.Application;
34 import net.sf.openrocket.unit.UnitGroup;
35 import net.sf.openrocket.util.BugException;
36 import net.sf.openrocket.util.BuildProperties;
39 public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
40 private static final LogHelper log = Application.getLogger();
42 private static final String SPLIT_CHARACTER = "|";
45 private static final List<Locale> SUPPORTED_LOCALES;
47 List<Locale> list = new ArrayList<Locale>();
48 for (String lang : new String[] { "en", "de", "es", "fr" }) {
49 list.add(new Locale(lang));
51 SUPPORTED_LOCALES = Collections.unmodifiableList(list);
56 * Whether to use the debug-node instead of the normal node.
58 private static final boolean DEBUG;
60 DEBUG = (System.getProperty("openrocket.debug.prefs") != null);
64 * Whether to clear all preferences at application startup. This has an effect only
67 private static final boolean CLEARPREFS = true;
70 * The node name to use in the Java preferences storage.
72 private static final String NODENAME = (DEBUG ? "OpenRocket-debug" : "OpenRocket");
74 private final Preferences PREFNODE;
77 public SwingPreferences() {
78 Preferences root = Preferences.userRoot();
79 if (DEBUG && CLEARPREFS) {
81 if (root.nodeExists(NODENAME)) {
82 root.node(NODENAME).removeNode();
84 } catch (BackingStoreException e) {
85 throw new BugException("Unable to clear preference node", e);
88 PREFNODE = root.node(NODENAME);
92 private static final HashMap<Class<?>, String> DEFAULT_COLORS =
93 new HashMap<Class<?>, String>();
95 DEFAULT_COLORS.put(BodyComponent.class, "0,0,240");
96 DEFAULT_COLORS.put(FinSet.class, "0,0,200");
97 DEFAULT_COLORS.put(LaunchLug.class, "0,0,180");
98 DEFAULT_COLORS.put(InternalComponent.class, "170,0,100");
99 DEFAULT_COLORS.put(MassObject.class, "0,0,0");
100 DEFAULT_COLORS.put(RecoveryDevice.class, "255,0,0");
105 //////////////////////
110 * Store the current OpenRocket version into the preferences to allow for preferences migration.
112 private void storeVersion() {
113 PREFNODE.put("OpenRocketVersion", BuildProperties.getVersion());
117 * Return a string preference.
119 * @param key the preference key.
120 * @param def the default if no preference is stored
121 * @return the preference value
124 public String getString(String key, String def) {
125 return PREFNODE.get(key, def);
129 public String getString( String directory, String key, String defaultValue ) {
130 Preferences p = PREFNODE.node(directory);
131 return p.get(key,defaultValue);
135 * Set a string preference.
137 * @param key the preference key
138 * @param value the value to set, or <code>null</code> to remove the key
141 public void putString(String key, String value) {
143 PREFNODE.remove(key);
145 PREFNODE.put(key, value);
151 public void putString(String directory, String key, String value ) {
152 Preferences p = PREFNODE.node(directory);
153 if ( value == null ) {
162 * Return a boolean preference.
164 * @param key the preference key
165 * @param def the default if no preference is stored
166 * @return the preference value
169 public boolean getBoolean(String key, boolean def) {
170 return PREFNODE.getBoolean(key, def);
174 * Set a boolean preference.
176 * @param key the preference key
177 * @param value the value to set
180 public void putBoolean(String key, boolean value) {
181 PREFNODE.putBoolean(key, value);
186 public int getInt( String key, int defaultValue ) {
187 return PREFNODE.getInt(key, defaultValue);
191 public void putInt( String key , int value ) {
192 PREFNODE.putInt(key, value );
197 public double getDouble(String key, double defaultValue) {
198 return PREFNODE.getDouble(key, defaultValue );
202 public void putDouble(String key, double value) {
203 PREFNODE.putDouble(key,value);
210 * Return a preferences object for the specified node name.
212 * @param nodeName the node name
213 * @return the preferences object for that node
215 public Preferences getNode(String nodeName) {
216 return PREFNODE.node(nodeName);
223 public static List<Locale> getSupportedLocales() {
224 return SUPPORTED_LOCALES;
227 public File getDefaultDirectory() {
228 String file = getString("defaultDirectory", null);
231 return new File(file);
234 public void setDefaultDirectory(File dir) {
239 d = dir.getAbsolutePath();
241 putString("defaultDirectory", d);
247 * Return a list of files/directories to be loaded as custom thrust curves.
249 * If this property has not been set, the directory "ThrustCurves" in the user
250 * application directory will be used. The directory will be created if it does not
253 * @return a list of files to load as thrust curves.
255 public List<File> getUserThrustCurveFiles() {
256 List<File> list = new ArrayList<File>();
258 String files = getString(USER_THRUST_CURVES_KEY, null);
260 // Default to application directory
261 File tcdir = getDefaultUserThrustCurveFile();
262 if (!tcdir.isDirectory()) {
267 for (String file : files.split("\\" + SPLIT_CHARACTER)) {
269 if (file.length() > 0) {
270 list.add(new File(file));
278 public File getDefaultUserThrustCurveFile() {
279 File appdir = SystemInfo.getUserApplicationDirectory();
280 File tcdir = new File(appdir, "ThrustCurves");
286 * Set the list of files/directories to be loaded as custom thrust curves.
288 * @param files the files to load, or <code>null</code> to reset to default value.
290 public void setUserThrustCurveFiles(List<File> files) {
292 putString(USER_THRUST_CURVES_KEY, null);
298 for (File file : files) {
299 if (str.length() > 0) {
300 str += SPLIT_CHARACTER;
302 str += file.getAbsolutePath();
304 putString(USER_THRUST_CURVES_KEY, str);
307 public Color getMotorBorderColor() {
308 // TODO: MEDIUM: Motor color (settable?)
309 return new Color(0, 0, 0, 200);
313 public Color getMotorFillColor() {
314 // TODO: MEDIUM: Motor fill color (settable?)
315 return new Color(0, 0, 0, 100);
318 public Color getDefaultColor(Class<? extends RocketComponent> c) {
319 String color = get("componentColors", c, DEFAULT_COLORS);
323 net.sf.openrocket.util.Color clr = parseColor(color);
325 return ColorConversion.toAwtColor(clr);
331 public final void setDefaultColor(Class<? extends RocketComponent> c, Color color) {
334 putString("componentColors", c.getSimpleName(), stringifyColor(ColorConversion.fromAwtColor(color)));
339 public static int getMaxThreadCount() {
340 return Runtime.getRuntime().availableProcessors();
345 public Point getWindowPosition(Class<?> c) {
347 String pref = PREFNODE.node("windows").get("position." + c.getCanonicalName(), null);
352 if (pref.indexOf(',') < 0)
356 x = Integer.parseInt(pref.substring(0, pref.indexOf(',')));
357 y = Integer.parseInt(pref.substring(pref.indexOf(',') + 1));
358 } catch (NumberFormatException e) {
361 return new Point(x, y);
364 public void setWindowPosition(Class<?> c, Point p) {
365 PREFNODE.node("windows").put("position." + c.getCanonicalName(), "" + p.x + "," + p.y);
372 public Dimension getWindowSize(Class<?> c) {
374 String pref = PREFNODE.node("windows").get("size." + c.getCanonicalName(), null);
379 if (pref.indexOf(',') < 0)
383 x = Integer.parseInt(pref.substring(0, pref.indexOf(',')));
384 y = Integer.parseInt(pref.substring(pref.indexOf(',') + 1));
385 } catch (NumberFormatException e) {
388 return new Dimension(x, y);
392 public boolean isWindowMaximized(Class<?> c) {
393 String pref = PREFNODE.node("windows").get("size." + c.getCanonicalName(), null);
394 return "max".equals(pref);
397 public void setWindowSize(Class<?> c, Dimension d) {
398 PREFNODE.node("windows").put("size." + c.getCanonicalName(), "" + d.width + "," + d.height);
402 public void setWindowMaximized(Class<?> c) {
403 PREFNODE.node("windows").put("size." + c.getCanonicalName(), "max");
408 * this class returns a java.awt.Color object for the specified key.
409 * you can pass (java.awt.Color) null to the second argument to
412 public Color getColor( String key, Color defaultValue ) {
413 net.sf.openrocket.util.Color c = super.getColor(key, (net.sf.openrocket.util.Color) null);
417 return ColorConversion.toAwtColor(c);
423 public void putColor( String key, Color value ) {
424 net.sf.openrocket.util.Color c = ColorConversion.fromAwtColor(value);
425 super.putColor(key, c);
431 //// Background flight data computation
433 public boolean computeFlightInBackground() {
434 return PREFNODE.getBoolean("backgroundFlight", true);
437 public Simulation getBackgroundSimulation(Rocket rocket) {
438 Simulation s = new Simulation(rocket);
439 SimulationOptions cond = s.getOptions();
441 cond.setTimeStep(RK4SimulationStepper.RECOMMENDED_TIME_STEP * 2);
442 cond.setWindSpeedAverage(1.0);
443 cond.setWindSpeedDeviation(0.1);
444 cond.setLaunchRodLength(5);
450 ///////// Export variables
452 public boolean isExportSelected(FlightDataType type) {
453 Preferences prefs = PREFNODE.node("exports");
454 return prefs.getBoolean(type.getName(), false);
457 public void setExportSelected(FlightDataType type, boolean selected) {
458 Preferences prefs = PREFNODE.node("exports");
459 prefs.putBoolean(type.getName(), selected);
464 ///////// Default unit storage
466 public void loadDefaultUnits() {
467 Preferences prefs = PREFNODE.node("units");
470 for (String key : prefs.keys()) {
471 UnitGroup group = UnitGroup.UNITS.get(key);
476 group.setDefaultUnit(prefs.get(key, null));
477 } catch (IllegalArgumentException ignore) {
481 } catch (BackingStoreException e) {
482 ExceptionHandler.handleErrorCondition(e);
486 public void storeDefaultUnits() {
487 Preferences prefs = PREFNODE.node("units");
489 for (String key : UnitGroup.UNITS.keySet()) {
490 UnitGroup group = UnitGroup.UNITS.get(key);
491 if (group == null || group.getUnitCount() < 2)
494 prefs.put(key, group.getDefaultUnit().getUnit());
500 //// Material storage
504 * Add a user-defined material to the preferences. The preferences are
505 * first checked for an existing material matching the provided one using
506 * {@link Material#equals(Object)}.
508 * @param m the material to add.
510 public void addUserMaterial(Material m) {
511 Preferences prefs = PREFNODE.node("userMaterials");
514 // Check whether material already exists
515 if (getUserMaterials().contains(m)) {
519 // Add material using next free key (key is not used when loading)
520 String mat = m.toStorableString();
521 for (int i = 0;; i++) {
522 String key = "material" + i;
523 if (prefs.get(key, null) == null) {
532 * Remove a user-defined material from the preferences. The matching is performed
533 * using {@link Material#equals(Object)}.
535 * @param m the material to remove.
537 public void removeUserMaterial(Material m) {
538 Preferences prefs = PREFNODE.node("userMaterials");
542 // Iterate through materials and remove all keys with a matching material
543 for (String key : prefs.keys()) {
544 String value = prefs.get(key, null);
547 Material existing = Material.fromStorableString(value, true);
548 if (existing.equals(m)) {
552 } catch (IllegalArgumentException ignore) {
557 } catch (BackingStoreException e) {
558 throw new IllegalStateException("Cannot read preferences!", e);
564 * Return a set of all user-defined materials in the preferences. The materials
565 * are created marked as user-defined.
567 * @return a set of all user-defined materials.
569 public Set<Material> getUserMaterials() {
570 Preferences prefs = PREFNODE.node("userMaterials");
572 HashSet<Material> materials = new HashSet<Material>();
575 for (String key : prefs.keys()) {
576 String value = prefs.get(key, null);
579 Material m = Material.fromStorableString(value, true);
582 } catch (IllegalArgumentException e) {
583 log.warn("Illegal material string " + value);
588 } catch (BackingStoreException e) {
589 throw new IllegalStateException("Cannot read preferences!", e);