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.HashSet;
10 import java.util.List;
11 import java.util.Locale;
13 import java.util.prefs.BackingStoreException;
14 import java.util.prefs.Preferences;
16 import net.sf.openrocket.arch.SystemInfo;
17 import net.sf.openrocket.document.Simulation;
18 import net.sf.openrocket.logging.LogHelper;
19 import net.sf.openrocket.material.Material;
20 import net.sf.openrocket.rocketcomponent.Rocket;
21 import net.sf.openrocket.simulation.FlightDataType;
22 import net.sf.openrocket.simulation.RK4SimulationStepper;
23 import net.sf.openrocket.simulation.SimulationOptions;
24 import net.sf.openrocket.startup.Application;
25 import net.sf.openrocket.unit.UnitGroup;
26 import net.sf.openrocket.util.BugException;
27 import net.sf.openrocket.util.BuildProperties;
30 public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
31 private static final LogHelper log = Application.getLogger();
33 private static final String SPLIT_CHARACTER = "|";
36 private static final List<Locale> SUPPORTED_LOCALES;
38 List<Locale> list = new ArrayList<Locale>();
39 for (String lang : new String[] { "en", "de", "es", "fr", "it", "ru" }) {
40 list.add(new Locale(lang));
42 SUPPORTED_LOCALES = Collections.unmodifiableList(list);
47 * Whether to use the debug-node instead of the normal node.
49 private static final boolean DEBUG;
51 DEBUG = (System.getProperty("openrocket.debug.prefs") != null);
55 * Whether to clear all preferences at application startup. This has an effect only
58 private static final boolean CLEARPREFS = true;
61 * The node name to use in the Java preferences storage.
63 private static final String NODENAME = (DEBUG ? "OpenRocket-debug" : "OpenRocket");
65 private final Preferences PREFNODE;
68 public SwingPreferences() {
69 Preferences root = Preferences.userRoot();
70 if (DEBUG && CLEARPREFS) {
72 if (root.nodeExists(NODENAME)) {
73 root.node(NODENAME).removeNode();
75 } catch (BackingStoreException e) {
76 throw new BugException("Unable to clear preference node", e);
79 PREFNODE = root.node(NODENAME);
85 //////////////////////
90 * Store the current OpenRocket version into the preferences to allow for preferences migration.
92 private void storeVersion() {
93 PREFNODE.put("OpenRocketVersion", BuildProperties.getVersion());
97 * Return a string preference.
99 * @param key the preference key.
100 * @param def the default if no preference is stored
101 * @return the preference value
104 public String getString(String key, String def) {
105 return PREFNODE.get(key, def);
109 public String getString( String directory, String key, String defaultValue ) {
110 Preferences p = PREFNODE.node(directory);
111 return p.get(key,defaultValue);
115 * Set a string preference.
117 * @param key the preference key
118 * @param value the value to set, or <code>null</code> to remove the key
121 public void putString(String key, String value) {
123 PREFNODE.remove(key);
125 PREFNODE.put(key, value);
131 public void putString(String directory, String key, String value ) {
132 Preferences p = PREFNODE.node(directory);
133 if ( value == null ) {
142 * Return a boolean preference.
144 * @param key the preference key
145 * @param def the default if no preference is stored
146 * @return the preference value
149 public boolean getBoolean(String key, boolean def) {
150 return PREFNODE.getBoolean(key, def);
154 * Set a boolean preference.
156 * @param key the preference key
157 * @param value the value to set
160 public void putBoolean(String key, boolean value) {
161 PREFNODE.putBoolean(key, value);
166 public int getInt( String key, int defaultValue ) {
167 return PREFNODE.getInt(key, defaultValue);
171 public void putInt( String key , int value ) {
172 PREFNODE.putInt(key, value );
177 public double getDouble(String key, double defaultValue) {
178 return PREFNODE.getDouble(key, defaultValue );
182 public void putDouble(String key, double value) {
183 PREFNODE.putDouble(key,value);
190 * Return a preferences object for the specified node name.
192 * @param nodeName the node name
193 * @return the preferences object for that node
195 public Preferences getNode(String nodeName) {
196 return PREFNODE.node(nodeName);
203 public static List<Locale> getSupportedLocales() {
204 return SUPPORTED_LOCALES;
207 public File getDefaultDirectory() {
208 String file = getString("defaultDirectory", null);
211 return new File(file);
214 public void setDefaultDirectory(File dir) {
219 d = dir.getAbsolutePath();
221 putString("defaultDirectory", d);
227 * Return a list of files/directories to be loaded as custom thrust curves.
229 * If this property has not been set, the directory "ThrustCurves" in the user
230 * application directory will be used. The directory will be created if it does not
233 * @return a list of files to load as thrust curves.
235 public List<File> getUserThrustCurveFiles() {
236 List<File> list = new ArrayList<File>();
238 String files = getString(USER_THRUST_CURVES_KEY, null);
240 // Default to application directory
241 File tcdir = getDefaultUserThrustCurveFile();
242 if (!tcdir.isDirectory()) {
247 for (String file : files.split("\\" + SPLIT_CHARACTER)) {
249 if (file.length() > 0) {
250 list.add(new File(file));
258 public File getDefaultUserThrustCurveFile() {
259 File appdir = SystemInfo.getUserApplicationDirectory();
260 File tcdir = new File(appdir, "ThrustCurves");
266 * Set the list of files/directories to be loaded as custom thrust curves.
268 * @param files the files to load, or <code>null</code> to reset to default value.
270 public void setUserThrustCurveFiles(List<File> files) {
272 putString(USER_THRUST_CURVES_KEY, null);
278 for (File file : files) {
279 if (str.length() > 0) {
280 str += SPLIT_CHARACTER;
282 str += file.getAbsolutePath();
284 putString(USER_THRUST_CURVES_KEY, str);
287 public Color getMotorBorderColor() {
288 // TODO: MEDIUM: Motor color (settable?)
289 return new Color(0, 0, 0, 200);
293 public Color getMotorFillColor() {
294 // TODO: MEDIUM: Motor fill color (settable?)
295 return new Color(0, 0, 0, 100);
300 public static int getMaxThreadCount() {
301 return Runtime.getRuntime().availableProcessors();
306 public Point getWindowPosition(Class<?> c) {
308 String pref = PREFNODE.node("windows").get("position." + c.getCanonicalName(), null);
313 if (pref.indexOf(',') < 0)
317 x = Integer.parseInt(pref.substring(0, pref.indexOf(',')));
318 y = Integer.parseInt(pref.substring(pref.indexOf(',') + 1));
319 } catch (NumberFormatException e) {
322 return new Point(x, y);
325 public void setWindowPosition(Class<?> c, Point p) {
326 PREFNODE.node("windows").put("position." + c.getCanonicalName(), "" + p.x + "," + p.y);
333 public Dimension getWindowSize(Class<?> c) {
335 String pref = PREFNODE.node("windows").get("size." + c.getCanonicalName(), null);
340 if (pref.indexOf(',') < 0)
344 x = Integer.parseInt(pref.substring(0, pref.indexOf(',')));
345 y = Integer.parseInt(pref.substring(pref.indexOf(',') + 1));
346 } catch (NumberFormatException e) {
349 return new Dimension(x, y);
353 public boolean isWindowMaximized(Class<?> c) {
354 String pref = PREFNODE.node("windows").get("size." + c.getCanonicalName(), null);
355 return "max".equals(pref);
358 public void setWindowSize(Class<?> c, Dimension d) {
359 PREFNODE.node("windows").put("size." + c.getCanonicalName(), "" + d.width + "," + d.height);
363 public void setWindowMaximized(Class<?> c) {
364 PREFNODE.node("windows").put("size." + c.getCanonicalName(), "max");
369 * this class returns a java.awt.Color object for the specified key.
370 * you can pass (java.awt.Color) null to the second argument to
373 public Color getColor( String key, Color defaultValue ) {
374 net.sf.openrocket.util.Color c = super.getColor(key, (net.sf.openrocket.util.Color) null);
378 return ColorConversion.toAwtColor(c);
384 public void putColor( String key, Color value ) {
385 net.sf.openrocket.util.Color c = ColorConversion.fromAwtColor(value);
386 super.putColor(key, c);
392 //// Background flight data computation
394 public boolean computeFlightInBackground() {
395 return PREFNODE.getBoolean("backgroundFlight", true);
398 public Simulation getBackgroundSimulation(Rocket rocket) {
399 Simulation s = new Simulation(rocket);
400 SimulationOptions cond = s.getOptions();
402 cond.setTimeStep(RK4SimulationStepper.RECOMMENDED_TIME_STEP * 2);
403 cond.setWindSpeedAverage(1.0);
404 cond.setWindSpeedDeviation(0.1);
405 cond.setLaunchRodLength(5);
411 ///////// Export variables
413 public boolean isExportSelected(FlightDataType type) {
414 Preferences prefs = PREFNODE.node("exports");
415 return prefs.getBoolean(type.getName(), false);
418 public void setExportSelected(FlightDataType type, boolean selected) {
419 Preferences prefs = PREFNODE.node("exports");
420 prefs.putBoolean(type.getName(), selected);
425 ///////// Default unit storage
427 public void loadDefaultUnits() {
428 Preferences prefs = PREFNODE.node("units");
431 for (String key : prefs.keys()) {
432 UnitGroup group = UnitGroup.UNITS.get(key);
437 group.setDefaultUnit(prefs.get(key, null));
438 } catch (IllegalArgumentException ignore) {
442 } catch (BackingStoreException e) {
443 Application.getExceptionHandler().handleErrorCondition(e);
447 public void storeDefaultUnits() {
448 Preferences prefs = PREFNODE.node("units");
450 for (String key : UnitGroup.UNITS.keySet()) {
451 UnitGroup group = UnitGroup.UNITS.get(key);
452 if (group == null || group.getUnitCount() < 2)
455 prefs.put(key, group.getDefaultUnit().getUnit());
461 //// Material storage
465 * Add a user-defined material to the preferences. The preferences are
466 * first checked for an existing material matching the provided one using
467 * {@link Material#equals(Object)}.
469 * @param m the material to add.
471 public void addUserMaterial(Material m) {
472 Preferences prefs = PREFNODE.node("userMaterials");
475 // Check whether material already exists
476 if (getUserMaterials().contains(m)) {
480 // Add material using next free key (key is not used when loading)
481 String mat = m.toStorableString();
482 for (int i = 0;; i++) {
483 String key = "material" + i;
484 if (prefs.get(key, null) == null) {
493 * Remove a user-defined material from the preferences. The matching is performed
494 * using {@link Material#equals(Object)}.
496 * @param m the material to remove.
498 public void removeUserMaterial(Material m) {
499 Preferences prefs = PREFNODE.node("userMaterials");
503 // Iterate through materials and remove all keys with a matching material
504 for (String key : prefs.keys()) {
505 String value = prefs.get(key, null);
508 Material existing = Material.fromStorableString(value, true);
509 if (existing.equals(m)) {
513 } catch (IllegalArgumentException ignore) {
518 } catch (BackingStoreException e) {
519 throw new IllegalStateException("Cannot read preferences!", e);
525 * Return a set of all user-defined materials in the preferences. The materials
526 * are created marked as user-defined.
528 * @return a set of all user-defined materials.
530 public Set<Material> getUserMaterials() {
531 Preferences prefs = PREFNODE.node("userMaterials");
533 HashSet<Material> materials = new HashSet<Material>();
536 for (String key : prefs.keys()) {
537 String value = prefs.get(key, null);
540 Material m = Material.fromStorableString(value, true);
543 } catch (IllegalArgumentException e) {
544 log.warn("Illegal material string " + value);
549 } catch (BackingStoreException e) {
550 throw new IllegalStateException("Cannot read preferences!", e);