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.OpenRocketDocument;
19 import net.sf.openrocket.document.Simulation;
20 import net.sf.openrocket.logging.LogHelper;
21 import net.sf.openrocket.material.Material;
22 import net.sf.openrocket.preset.ComponentPreset;
23 import net.sf.openrocket.rocketcomponent.Rocket;
24 import net.sf.openrocket.simulation.FlightDataType;
25 import net.sf.openrocket.simulation.RK4SimulationStepper;
26 import net.sf.openrocket.simulation.SimulationOptions;
27 import net.sf.openrocket.startup.Application;
28 import net.sf.openrocket.unit.UnitGroup;
29 import net.sf.openrocket.util.BugException;
30 import net.sf.openrocket.util.BuildProperties;
33 public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
34 private static final LogHelper log = Application.getLogger();
36 private static final String SPLIT_CHARACTER = "|";
39 private static final List<Locale> SUPPORTED_LOCALES;
41 List<Locale> list = new ArrayList<Locale>();
42 for (String lang : new String[] { "en", "de", "es", "fr", "it", "ru","cs" }) {
43 list.add(new Locale(lang));
45 SUPPORTED_LOCALES = Collections.unmodifiableList(list);
50 * Whether to use the debug-node instead of the normal node.
52 private static final boolean DEBUG;
54 DEBUG = (System.getProperty("openrocket.debug.prefs") != null);
58 * Whether to clear all preferences at application startup. This has an effect only
61 private static final boolean CLEARPREFS = true;
64 * The node name to use in the Java preferences storage.
66 private static final String NODENAME = (DEBUG ? "OpenRocket-debug" : "OpenRocket");
68 private final Preferences PREFNODE;
71 public SwingPreferences() {
72 Preferences root = Preferences.userRoot();
73 if (DEBUG && CLEARPREFS) {
75 if (root.nodeExists(NODENAME)) {
76 root.node(NODENAME).removeNode();
78 } catch (BackingStoreException e) {
79 throw new BugException("Unable to clear preference node", e);
82 PREFNODE = root.node(NODENAME);
88 //////////////////////
93 * Store the current OpenRocket version into the preferences to allow for preferences migration.
95 private void storeVersion() {
96 PREFNODE.put("OpenRocketVersion", BuildProperties.getVersion());
100 * Return a string preference.
102 * @param key the preference key.
103 * @param def the default if no preference is stored
104 * @return the preference value
107 public String getString(String key, String def) {
108 return PREFNODE.get(key, def);
112 public String getString( String directory, String key, String defaultValue ) {
113 Preferences p = PREFNODE.node(directory);
114 return p.get(key,defaultValue);
118 * Set a string preference.
120 * @param key the preference key
121 * @param value the value to set, or <code>null</code> to remove the key
124 public void putString(String key, String value) {
126 PREFNODE.remove(key);
128 PREFNODE.put(key, value);
134 public void putString(String directory, String key, String value ) {
135 Preferences p = PREFNODE.node(directory);
136 if ( value == null ) {
145 * Return a boolean preference.
147 * @param key the preference key
148 * @param def the default if no preference is stored
149 * @return the preference value
152 public boolean getBoolean(String key, boolean def) {
153 return PREFNODE.getBoolean(key, def);
157 * Set a boolean preference.
159 * @param key the preference key
160 * @param value the value to set
163 public void putBoolean(String key, boolean value) {
164 PREFNODE.putBoolean(key, value);
169 public int getInt( String key, int defaultValue ) {
170 return PREFNODE.getInt(key, defaultValue);
174 public void putInt( String key , int value ) {
175 PREFNODE.putInt(key, value );
180 public double getDouble(String key, double defaultValue) {
181 return PREFNODE.getDouble(key, defaultValue );
185 public void putDouble(String key, double value) {
186 PREFNODE.putDouble(key,value);
193 * Return a preferences object for the specified node name.
195 * @param nodeName the node name
196 * @return the preferences object for that node
198 public Preferences getNode(String nodeName) {
199 return PREFNODE.node(nodeName);
206 public static List<Locale> getSupportedLocales() {
207 return SUPPORTED_LOCALES;
210 public File getDefaultDirectory() {
211 String file = getString("defaultDirectory", null);
214 return new File(file);
217 public void setDefaultDirectory(File dir) {
222 d = dir.getAbsolutePath();
224 putString("defaultDirectory", d);
230 * Return a list of files/directories to be loaded as custom thrust curves.
232 * If this property has not been set, the directory "ThrustCurves" in the user
233 * application directory will be used. The directory will be created if it does not
236 * @return a list of files to load as thrust curves.
238 public List<File> getUserThrustCurveFiles() {
239 List<File> list = new ArrayList<File>();
241 String files = getString(USER_THRUST_CURVES_KEY, null);
243 // Default to application directory
244 File tcdir = getDefaultUserThrustCurveFile();
245 if (!tcdir.isDirectory()) {
250 for (String file : files.split("\\" + SPLIT_CHARACTER)) {
252 if (file.length() > 0) {
253 list.add(new File(file));
261 public File getDefaultUserThrustCurveFile() {
262 File appdir = SystemInfo.getUserApplicationDirectory();
263 File tcdir = new File(appdir, "ThrustCurves");
269 * Set the list of files/directories to be loaded as custom thrust curves.
271 * @param files the files to load, or <code>null</code> to reset to default value.
273 public void setUserThrustCurveFiles(List<File> files) {
275 putString(USER_THRUST_CURVES_KEY, null);
281 for (File file : files) {
282 if (str.length() > 0) {
283 str += SPLIT_CHARACTER;
285 str += file.getAbsolutePath();
287 putString(USER_THRUST_CURVES_KEY, str);
290 public Color getMotorBorderColor() {
291 // TODO: MEDIUM: Motor color (settable?)
292 return new Color(0, 0, 0, 200);
296 public Color getMotorFillColor() {
297 // TODO: MEDIUM: Motor fill color (settable?)
298 return new Color(0, 0, 0, 100);
303 public static int getMaxThreadCount() {
304 return Runtime.getRuntime().availableProcessors();
309 public Point getWindowPosition(Class<?> c) {
311 String pref = PREFNODE.node("windows").get("position." + c.getCanonicalName(), null);
316 if (pref.indexOf(',') < 0)
320 x = Integer.parseInt(pref.substring(0, pref.indexOf(',')));
321 y = Integer.parseInt(pref.substring(pref.indexOf(',') + 1));
322 } catch (NumberFormatException e) {
325 return new Point(x, y);
328 public void setWindowPosition(Class<?> c, Point p) {
329 PREFNODE.node("windows").put("position." + c.getCanonicalName(), "" + p.x + "," + p.y);
336 public Dimension getWindowSize(Class<?> c) {
338 String pref = PREFNODE.node("windows").get("size." + c.getCanonicalName(), null);
343 if (pref.indexOf(',') < 0)
347 x = Integer.parseInt(pref.substring(0, pref.indexOf(',')));
348 y = Integer.parseInt(pref.substring(pref.indexOf(',') + 1));
349 } catch (NumberFormatException e) {
352 return new Dimension(x, y);
356 public boolean isWindowMaximized(Class<?> c) {
357 String pref = PREFNODE.node("windows").get("size." + c.getCanonicalName(), null);
358 return "max".equals(pref);
361 public void setWindowSize(Class<?> c, Dimension d) {
362 PREFNODE.node("windows").put("size." + c.getCanonicalName(), "" + d.width + "," + d.height);
366 public void setWindowMaximized(Class<?> c) {
367 PREFNODE.node("windows").put("size." + c.getCanonicalName(), "max");
372 * this class returns a java.awt.Color object for the specified key.
373 * you can pass (java.awt.Color) null to the second argument to
376 public Color getColor( String key, Color defaultValue ) {
377 net.sf.openrocket.util.Color c = super.getColor(key, (net.sf.openrocket.util.Color) null);
381 return ColorConversion.toAwtColor(c);
387 public void putColor( String key, Color value ) {
388 net.sf.openrocket.util.Color c = ColorConversion.fromAwtColor(value);
389 super.putColor(key, c);
395 //// Background flight data computation
397 public boolean computeFlightInBackground() {
398 return PREFNODE.getBoolean("backgroundFlight", true);
401 public Simulation getBackgroundSimulation(Rocket rocket) {
402 Simulation s = new Simulation(new OpenRocketDocument(rocket), rocket);
403 SimulationOptions cond = s.getOptions();
405 cond.setTimeStep(RK4SimulationStepper.RECOMMENDED_TIME_STEP * 2);
406 cond.setWindSpeedAverage(1.0);
407 cond.setWindSpeedDeviation(0.1);
408 cond.setLaunchRodLength(5);
414 ///////// Export variables
416 public boolean isExportSelected(FlightDataType type) {
417 Preferences prefs = PREFNODE.node("exports");
418 return prefs.getBoolean(type.getName(), false);
421 public void setExportSelected(FlightDataType type, boolean selected) {
422 Preferences prefs = PREFNODE.node("exports");
423 prefs.putBoolean(type.getName(), selected);
428 ///////// Default unit storage
430 public void loadDefaultUnits() {
431 Preferences prefs = PREFNODE.node("units");
434 for (String key : prefs.keys()) {
435 UnitGroup group = UnitGroup.UNITS.get(key);
440 group.setDefaultUnit(prefs.get(key, null));
441 } catch (IllegalArgumentException ignore) {
445 } catch (BackingStoreException e) {
446 Application.getExceptionHandler().handleErrorCondition(e);
450 public void storeDefaultUnits() {
451 Preferences prefs = PREFNODE.node("units");
453 for (String key : UnitGroup.UNITS.keySet()) {
454 UnitGroup group = UnitGroup.UNITS.get(key);
455 if (group == null || group.getUnitCount() < 2)
458 prefs.put(key, group.getDefaultUnit().getUnit());
464 //// Material storage
468 * Add a user-defined material to the preferences. The preferences are
469 * first checked for an existing material matching the provided one using
470 * {@link Material#equals(Object)}.
472 * @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.
501 public void removeUserMaterial(Material m) {
502 Preferences prefs = PREFNODE.node("userMaterials");
506 // Iterate through materials and remove all keys with a matching material
507 for (String key : prefs.keys()) {
508 String value = prefs.get(key, null);
511 Material existing = Material.fromStorableString(value, true);
512 if (existing.equals(m)) {
516 } catch (IllegalArgumentException ignore) {
521 } catch (BackingStoreException e) {
522 throw new IllegalStateException("Cannot read preferences!", e);
528 * Return a set of all user-defined materials in the preferences. The materials
529 * are created marked as user-defined.
531 * @return a set of all user-defined materials.
533 public Set<Material> getUserMaterials() {
534 Preferences prefs = PREFNODE.node("userMaterials");
536 HashSet<Material> materials = new HashSet<Material>();
539 for (String key : prefs.keys()) {
540 String value = prefs.get(key, null);
543 Material m = Material.fromStorableString(value, true);
546 } catch (IllegalArgumentException e) {
547 log.warn("Illegal material string " + value);
552 } catch (BackingStoreException e) {
553 throw new IllegalStateException("Cannot read preferences!", e);
559 public void setComponentFavorite( ComponentPreset preset, boolean favorite ) {
560 Preferences prefs = PREFNODE.node("favoritePresets");
562 prefs.putBoolean(preset.preferenceKey(), true);
564 prefs.remove(preset.preferenceKey());
568 public Set<String> getComponentFavorites( ) {
569 Preferences prefs = PREFNODE.node("favoritePresets");
570 Set<String> collection = new HashSet<String>();
572 collection.addAll( Arrays.asList(prefs.keys()));
573 } catch ( BackingStoreException bex ) {