1 package net.sf.openrocket.gui.util;
4 import java.awt.Dimension;
7 import java.util.ArrayList;
8 import java.util.Arrays;
9 import java.util.Collections;
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.logging.LogHelper;
20 import net.sf.openrocket.material.Material;
21 import net.sf.openrocket.preset.ComponentPreset;
22 import net.sf.openrocket.rocketcomponent.Rocket;
23 import net.sf.openrocket.simulation.FlightDataType;
24 import net.sf.openrocket.simulation.RK4SimulationStepper;
25 import net.sf.openrocket.simulation.SimulationOptions;
26 import net.sf.openrocket.startup.Application;
27 import net.sf.openrocket.unit.UnitGroup;
28 import net.sf.openrocket.util.BugException;
29 import net.sf.openrocket.util.BuildProperties;
32 public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
33 private static final LogHelper log = Application.getLogger();
35 private static final String SPLIT_CHARACTER = "|";
38 private static final List<Locale> SUPPORTED_LOCALES;
40 List<Locale> list = new ArrayList<Locale>();
41 for (String lang : new String[] { "en", "de", "es", "fr", "it", "ru", "cs", "pl" }) {
42 list.add(new Locale(lang));
44 SUPPORTED_LOCALES = Collections.unmodifiableList(list);
49 * Whether to use the debug-node instead of the normal node.
51 private static final boolean DEBUG;
53 DEBUG = (System.getProperty("openrocket.debug.prefs") != null);
57 * Whether to clear all preferences at application startup. This has an effect only
60 private static final boolean CLEARPREFS = true;
63 * The node name to use in the Java preferences storage.
65 private static final String NODENAME = (DEBUG ? "OpenRocket-debug" : "OpenRocket");
67 private final Preferences PREFNODE;
70 public SwingPreferences() {
71 Preferences root = Preferences.userRoot();
72 if (DEBUG && CLEARPREFS) {
74 if (root.nodeExists(NODENAME)) {
75 root.node(NODENAME).removeNode();
77 } catch (BackingStoreException e) {
78 throw new BugException("Unable to clear preference node", e);
81 PREFNODE = root.node(NODENAME);
87 //////////////////////
92 * Store the current OpenRocket version into the preferences to allow for preferences migration.
94 private void storeVersion() {
95 PREFNODE.put("OpenRocketVersion", BuildProperties.getVersion());
99 * Return a string preference.
101 * @param key the preference key.
102 * @param def the default if no preference is stored
103 * @return the preference value
106 public String getString(String key, String def) {
107 return PREFNODE.get(key, def);
111 public String getString(String directory, String key, String defaultValue) {
112 Preferences p = PREFNODE.node(directory);
113 return p.get(key, defaultValue);
117 * Set a string preference.
119 * @param key the preference key
120 * @param value the value to set, or <code>null</code> to remove the key
123 public void putString(String key, String value) {
125 PREFNODE.remove(key);
127 PREFNODE.put(key, value);
133 public void putString(String directory, String key, String value) {
134 Preferences p = PREFNODE.node(directory);
144 * Return a boolean preference.
146 * @param key the preference key
147 * @param def the default if no preference is stored
148 * @return the preference value
151 public boolean getBoolean(String key, boolean def) {
152 return PREFNODE.getBoolean(key, def);
156 * Set a boolean preference.
158 * @param key the preference key
159 * @param value the value to set
162 public void putBoolean(String key, boolean value) {
163 PREFNODE.putBoolean(key, value);
168 public int getInt(String key, int defaultValue) {
169 return PREFNODE.getInt(key, defaultValue);
173 public void putInt(String key, int value) {
174 PREFNODE.putInt(key, value);
179 public double getDouble(String key, double defaultValue) {
180 return PREFNODE.getDouble(key, defaultValue);
184 public void putDouble(String key, double value) {
185 PREFNODE.putDouble(key, value);
192 * Return a preferences object for the specified node name.
194 * @param nodeName the node name
195 * @return the preferences object for that node
197 public Preferences getNode(String nodeName) {
198 return PREFNODE.node(nodeName);
205 public static List<Locale> getSupportedLocales() {
206 return SUPPORTED_LOCALES;
209 public File getDefaultDirectory() {
210 String file = getString("defaultDirectory", null);
213 return new File(file);
216 public void setDefaultDirectory(File dir) {
221 d = dir.getAbsolutePath();
223 putString("defaultDirectory", d);
229 * Return a list of files/directories to be loaded as custom thrust curves.
231 * If this property has not been set, the directory "ThrustCurves" in the user
232 * application directory will be used. The directory will be created if it does not
235 * @return a list of files to load as thrust curves.
237 public List<File> getUserThrustCurveFiles() {
238 List<File> list = new ArrayList<File>();
240 String files = getString(USER_THRUST_CURVES_KEY, null);
242 // Default to application directory
243 File tcdir = getDefaultUserThrustCurveFile();
244 if (!tcdir.isDirectory()) {
249 for (String file : files.split("\\" + SPLIT_CHARACTER)) {
251 if (file.length() > 0) {
252 list.add(new File(file));
260 public File getDefaultUserThrustCurveFile() {
261 File appdir = SystemInfo.getUserApplicationDirectory();
262 File tcdir = new File(appdir, "ThrustCurves");
268 * Set the list of files/directories to be loaded as custom thrust curves.
270 * @param files the files to load, or <code>null</code> to reset to default value.
272 public void setUserThrustCurveFiles(List<File> files) {
274 putString(USER_THRUST_CURVES_KEY, null);
280 for (File file : files) {
281 if (str.length() > 0) {
282 str += SPLIT_CHARACTER;
284 str += file.getAbsolutePath();
286 putString(USER_THRUST_CURVES_KEY, str);
289 public Color getMotorBorderColor() {
290 // TODO: MEDIUM: Motor color (settable?)
291 return new Color(0, 0, 0, 200);
295 public Color getMotorFillColor() {
296 // TODO: MEDIUM: Motor fill color (settable?)
297 return new Color(0, 0, 0, 100);
302 public static int getMaxThreadCount() {
303 return Runtime.getRuntime().availableProcessors();
308 public Point getWindowPosition(Class<?> c) {
310 String pref = PREFNODE.node("windows").get("position." + c.getCanonicalName(), null);
315 if (pref.indexOf(',') < 0)
319 x = Integer.parseInt(pref.substring(0, pref.indexOf(',')));
320 y = Integer.parseInt(pref.substring(pref.indexOf(',') + 1));
321 } catch (NumberFormatException e) {
324 return new Point(x, y);
327 public void setWindowPosition(Class<?> c, Point p) {
328 PREFNODE.node("windows").put("position." + c.getCanonicalName(), "" + p.x + "," + p.y);
335 public Dimension getWindowSize(Class<?> c) {
337 String pref = PREFNODE.node("windows").get("size." + c.getCanonicalName(), null);
342 if (pref.indexOf(',') < 0)
346 x = Integer.parseInt(pref.substring(0, pref.indexOf(',')));
347 y = Integer.parseInt(pref.substring(pref.indexOf(',') + 1));
348 } catch (NumberFormatException e) {
351 return new Dimension(x, y);
355 public boolean isWindowMaximized(Class<?> c) {
356 String pref = PREFNODE.node("windows").get("size." + c.getCanonicalName(), null);
357 return "max".equals(pref);
360 public void setWindowSize(Class<?> c, Dimension d) {
361 PREFNODE.node("windows").put("size." + c.getCanonicalName(), "" + d.width + "," + d.height);
365 public void setWindowMaximized(Class<?> c) {
366 PREFNODE.node("windows").put("size." + c.getCanonicalName(), "max");
371 * this class returns a java.awt.Color object for the specified key.
372 * you can pass (java.awt.Color) null to the second argument to
375 public Color getColor(String key, Color defaultValue) {
376 net.sf.openrocket.util.Color c = super.getColor(key, (net.sf.openrocket.util.Color) null);
380 return ColorConversion.toAwtColor(c);
386 public void putColor(String key, Color value) {
387 net.sf.openrocket.util.Color c = ColorConversion.fromAwtColor(value);
388 super.putColor(key, c);
394 //// Background flight data computation
396 public boolean computeFlightInBackground() {
397 return PREFNODE.getBoolean("backgroundFlight", true);
400 public Simulation getBackgroundSimulation(Rocket rocket) {
401 Simulation s = new Simulation(rocket);
402 SimulationOptions cond = s.getOptions();
404 cond.setTimeStep(RK4SimulationStepper.RECOMMENDED_TIME_STEP * 2);
405 cond.setWindSpeedAverage(1.0);
406 cond.setWindSpeedDeviation(0.1);
407 cond.setLaunchRodLength(5);
413 ///////// Export variables
415 public boolean isExportSelected(FlightDataType type) {
416 Preferences prefs = PREFNODE.node("exports");
417 return prefs.getBoolean(type.getName(), false);
420 public void setExportSelected(FlightDataType type, boolean selected) {
421 Preferences prefs = PREFNODE.node("exports");
422 prefs.putBoolean(type.getName(), selected);
427 ///////// Default unit storage
429 public void loadDefaultUnits() {
430 Preferences prefs = PREFNODE.node("units");
433 for (String key : prefs.keys()) {
434 UnitGroup group = UnitGroup.UNITS.get(key);
439 group.setDefaultUnit(prefs.get(key, null));
440 } catch (IllegalArgumentException ignore) {
444 } catch (BackingStoreException e) {
445 Application.getExceptionHandler().handleErrorCondition(e);
449 public void storeDefaultUnits() {
450 Preferences prefs = PREFNODE.node("units");
452 for (String key : UnitGroup.UNITS.keySet()) {
453 UnitGroup group = UnitGroup.UNITS.get(key);
454 if (group == null || group.getUnitCount() < 2)
457 prefs.put(key, group.getDefaultUnit().getUnit());
463 //// Material storage
467 * Add a user-defined material to the preferences. The preferences are
468 * first checked for an existing material matching the provided one using
469 * {@link Material#equals(Object)}.
471 * @param m the material to add.
474 public void addUserMaterial(Material m) {
475 Preferences prefs = PREFNODE.node("userMaterials");
478 // Check whether material already exists
479 if (getUserMaterials().contains(m)) {
483 // Add material using next free key (key is not used when loading)
484 String mat = m.toStorableString();
485 for (int i = 0;; i++) {
486 String key = "material" + i;
487 if (prefs.get(key, null) == null) {
496 * Remove a user-defined material from the preferences. The matching is performed
497 * using {@link Material#equals(Object)}.
499 * @param m the material to remove.
502 public void removeUserMaterial(Material m) {
503 Preferences prefs = PREFNODE.node("userMaterials");
507 // Iterate through materials and remove all keys with a matching material
508 for (String key : prefs.keys()) {
509 String value = prefs.get(key, null);
512 Material existing = Material.fromStorableString(value, true);
513 if (existing.equals(m)) {
517 } catch (IllegalArgumentException ignore) {
522 } catch (BackingStoreException e) {
523 throw new IllegalStateException("Cannot read preferences!", e);
529 * Return a set of all user-defined materials in the preferences. The materials
530 * are created marked as user-defined.
532 * @return a set of all user-defined materials.
535 public Set<Material> getUserMaterials() {
536 Preferences prefs = PREFNODE.node("userMaterials");
538 HashSet<Material> materials = new HashSet<Material>();
541 for (String key : prefs.keys()) {
542 String value = prefs.get(key, null);
545 Material m = Material.fromStorableString(value, true);
548 } catch (IllegalArgumentException e) {
549 log.warn("Illegal material string " + value);
554 } catch (BackingStoreException e) {
555 throw new IllegalStateException("Cannot read preferences!", e);
562 public void setComponentFavorite(ComponentPreset preset, ComponentPreset.Type type, boolean favorite) {
563 Preferences prefs = PREFNODE.node("favoritePresets").node(type.name());
565 prefs.putBoolean(preset.preferenceKey(), true);
567 prefs.remove(preset.preferenceKey());
572 public Set<String> getComponentFavorites(ComponentPreset.Type type) {
573 Preferences prefs = PREFNODE.node("favoritePresets").node(type.name());
574 Set<String> collection = new HashSet<String>();
576 collection.addAll(Arrays.asList(prefs.keys()));
577 } catch (BackingStoreException bex) {