import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Properties;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
+import net.sf.openrocket.arch.SystemInfo;
import net.sf.openrocket.database.Databases;
import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.gui.main.ExceptionHandler;
+import net.sf.openrocket.l10n.Translator;
+import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.material.Material;
import net.sf.openrocket.rocketcomponent.BodyComponent;
import net.sf.openrocket.rocketcomponent.FinSet;
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent;
-import net.sf.openrocket.simulation.FlightDataBranch;
-import net.sf.openrocket.simulation.RK4Simulator;
-import net.sf.openrocket.simulation.SimulationConditions;
+import net.sf.openrocket.simulation.FlightDataType;
+import net.sf.openrocket.simulation.GUISimulationConditions;
+import net.sf.openrocket.simulation.RK4SimulationStepper;
+import net.sf.openrocket.startup.Application;
import net.sf.openrocket.unit.UnitGroup;
public class Prefs {
+ private static final LogHelper log = Application.getLogger();
+
+ private static final String SPLIT_CHARACTER = "|";
+
/**
* Whether to use the debug-node instead of the normal node.
*/
- public static final boolean DEBUG = false;
+ private static final boolean DEBUG;
+ static {
+ DEBUG = (System.getProperty("openrocket.debug.prefs") != null);
+ }
/**
* Whether to clear all preferences at application startup. This has an effect only
* if DEBUG is true.
*/
- public static final boolean CLEARPREFS = true;
+ private static final boolean CLEARPREFS = true;
/**
* The node name to use in the Java preferences storage.
*/
- public static final String NODENAME = (DEBUG?"OpenRocket-debug":"OpenRocket");
-
-
+ private static final String NODENAME = (DEBUG ? "OpenRocket-debug" : "OpenRocket");
- private static final String BUILD_VERSION;
- private static final String BUILD_SOURCE;
- public static final String DEFAULT_BUILD_SOURCE = "default";
- static {
- try {
- InputStream is = ClassLoader.getSystemResourceAsStream("build.properties");
- if (is == null) {
- throw new MissingResourceException(
- "build.properties not found, distribution built wrong" +
- " path:"+System.getProperty("java.class.path"),
- "build.properties", "build.version");
- }
-
- Properties props = new Properties();
- props.load(is);
- is.close();
-
- BUILD_VERSION = props.getProperty("build.version");
- if (BUILD_VERSION == null) {
+ /*
+ * Load property file only when necessary.
+ */
+ private static class BuildPropertyHolder {
+
+ public static final Properties PROPERTIES;
+ public static final String BUILD_VERSION;
+ public static final String BUILD_SOURCE;
+ public static final boolean DEFAULT_CHECK_UPDATES;
+
+ static {
+ try {
+ InputStream is = ClassLoader.getSystemResourceAsStream("build.properties");
+ if (is == null) {
+ throw new MissingResourceException(
+ "build.properties not found, distribution built wrong" +
+ " classpath:" + System.getProperty("java.class.path"),
+ "build.properties", "build.version");
+ }
+
+ PROPERTIES = new Properties();
+ PROPERTIES.load(is);
+ is.close();
+
+ String version = PROPERTIES.getProperty("build.version");
+ if (version == null) {
+ throw new MissingResourceException(
+ "build.version not found in property file",
+ "build.properties", "build.version");
+ }
+ BUILD_VERSION = version.trim();
+
+ BUILD_SOURCE = PROPERTIES.getProperty("build.source");
+ if (BUILD_SOURCE == null) {
+ throw new MissingResourceException(
+ "build.source not found in property file",
+ "build.properties", "build.source");
+ }
+
+ String value = PROPERTIES.getProperty("build.checkupdates");
+ if (value != null)
+ DEFAULT_CHECK_UPDATES = Boolean.parseBoolean(value);
+ else
+ DEFAULT_CHECK_UPDATES = true;
+
+ } catch (IOException e) {
throw new MissingResourceException(
- "build.version not found in property file",
+ "Error reading build.properties",
"build.properties", "build.version");
}
-
- BUILD_SOURCE = props.getProperty("build.source");
-
- } catch (IOException e) {
- throw new MissingResourceException(
- "Error reading build.properties",
- "build.properties", "build.version");
}
}
-
public static final String BODY_COMPONENT_INSERT_POSITION_KEY = "BodyComponentInsertPosition";
+ public static final String USER_THRUST_CURVES_KEY = "UserThrustCurves";
public static final String CONFIRM_DELETE_SIMULATION = "ConfirmDeleteSimulation";
-
+
// Preferences related to data export
public static final String EXPORT_FIELD_SEPARATOR = "ExportFieldSeparator";
public static final String EXPORT_SIMULATION_COMMENT = "ExportSimulationComment";
public static final String PLOT_SHOW_POINTS = "ShowPlotPoints";
+ private static final String CHECK_UPDATES = "CheckUpdates";
+ public static final String LAST_UPDATE = "LastUpdateVersion";
+
+ public static final String MOTOR_DIAMETER_FILTER = "MotorDiameterMatch";
+ public static final String MOTOR_HIDE_SIMILAR = "MotorHideSimilar";
+
+
+ // Node names
+ public static final String PREFERRED_THRUST_CURVE_MOTOR_NODE = "preferredThrustCurveMotors";
+
+
/**
* Node to this application's preferences.
* @deprecated Use the static methods instead.
public static final Preferences NODE;
private static final Preferences PREFNODE;
-
+
+ // Clear the preferences if debug mode and clearprefs is defined
static {
Preferences root = Preferences.userRoot();
if (DEBUG && CLEARPREFS) {
root.node(NODENAME).removeNode();
}
} catch (BackingStoreException e) {
- throw new RuntimeException("Unable to clear preference node",e);
+ throw new BugException("Unable to clear preference node", e);
}
}
PREFNODE = root.node(NODENAME);
NODE = PREFNODE;
}
-
-
-
+
+
+
///////// Default component attributes
- private static final HashMap<Class<?>,String> DEFAULT_COLORS =
- new HashMap<Class<?>,String>();
+ private static final HashMap<Class<?>, String> DEFAULT_COLORS =
+ new HashMap<Class<?>, String>();
static {
DEFAULT_COLORS.put(BodyComponent.class, "0,0,240");
DEFAULT_COLORS.put(FinSet.class, "0,0,200");
DEFAULT_COLORS.put(RecoveryDevice.class, "255,0,0");
}
-
- private static final HashMap<Class<?>,String> DEFAULT_LINE_STYLES =
- new HashMap<Class<?>,String>();
+
+ private static final HashMap<Class<?>, String> DEFAULT_LINE_STYLES =
+ new HashMap<Class<?>, String>();
static {
DEFAULT_LINE_STYLES.put(RocketComponent.class, LineStyle.SOLID.name());
DEFAULT_LINE_STYLES.put(MassObject.class, LineStyle.DASHED.name());
}
- private static final Material DEFAULT_LINE_MATERIAL =
- Databases.findMaterial(Material.Type.LINE, "Elastic cord (round 2mm, 1/16 in)",
- 0.0018, false);
- private static final Material DEFAULT_SURFACE_MATERIAL =
- Databases.findMaterial(Material.Type.SURFACE, "Ripstop nylon", 0.067, false);
- private static final Material DEFAULT_BULK_MATERIAL =
- Databases.findMaterial(Material.Type.BULK, "Cardboard", 680, false);
+ /*
+ * Within a holder class so they will load only when needed.
+ */
+ private static class DefaultMaterialHolder {
+ private static final Translator trans = Application.getTranslator();
+ //// Elastic cord (round 2mm, 1/16 in)
+ private static final Material DEFAULT_LINE_MATERIAL =
+ Databases.findMaterial(Material.Type.LINE, trans.get("Databases.materials.Elasticcordround2mm"),
+ 0.0018, false);
+ //// Ripstop nylon
+ private static final Material DEFAULT_SURFACE_MATERIAL =
+ Databases.findMaterial(Material.Type.SURFACE, trans.get("Databases.materials.Ripstopnylon"), 0.067, false);
+ //// Cardboard
+ private static final Material DEFAULT_BULK_MATERIAL =
+ Databases.findMaterial(Material.Type.BULK, trans.get("Databases.materials.Cardboard"), 680, false);
+ }
//////////////////////
-
+
+ /**
+ * Return the OpenRocket version number.
+ */
public static String getVersion() {
- return BUILD_VERSION;
+ return BuildPropertyHolder.BUILD_VERSION;
}
+ /**
+ * Return the OpenRocket build source (e.g. "default" or "Debian")
+ */
public static String getBuildSource() {
- return BUILD_SOURCE;
+ return BuildPropertyHolder.BUILD_SOURCE;
}
+ /**
+ * Return the OpenRocket unique ID.
+ *
+ * @return a random ID string that stays constant between OpenRocket executions
+ */
public static String getUniqueID() {
String id = PREFNODE.get("id", null);
if (id == null) {
- id = UniqueID.generateHashedID();
+ id = UniqueID.uuid();
PREFNODE.put("id", id);
}
return id;
}
-
- public static void storeVersion() {
+
+ /**
+ * Store the current OpenRocket version into the preferences to allow for preferences migration.
+ */
+ private static void storeVersion() {
PREFNODE.put("OpenRocketVersion", getVersion());
}
*/
public static int getChoise(String key, int max, int def) {
int v = PREFNODE.getInt(key, def);
- if ((v<0) || (v>max))
+ if ((v < 0) || (v > max))
return def;
return v;
}
}
-
+ /**
+ * Return a string preference.
+ *
+ * @param key the preference key.
+ * @param def the default if no preference is stored
+ * @return the preference value
+ */
public static String getString(String key, String def) {
return PREFNODE.get(key, def);
}
+ /**
+ * Set a string preference.
+ *
+ * @param key the preference key
+ * @param value the value to set, or <code>null</code> to remove the key
+ */
public static void putString(String key, String value) {
+ if (value == null) {
+ PREFNODE.remove(key);
+ return;
+ }
PREFNODE.put(key, value);
storeVersion();
}
-
+ /**
+ * Return a boolean preference.
+ *
+ * @param key the preference key
+ * @param def the default if no preference is stored
+ * @return the preference value
+ */
public static boolean getBoolean(String key, boolean def) {
return PREFNODE.getBoolean(key, def);
}
+ /**
+ * Set a boolean preference.
+ *
+ * @param key the preference key
+ * @param value the value to set
+ */
public static void putBoolean(String key, boolean value) {
PREFNODE.putBoolean(key, value);
storeVersion();
}
-
+
+
+ /**
+ * Return a preferences object for the specified node name.
+ *
+ * @param nodeName the node name
+ * @return the preferences object for that node
+ */
+ public static Preferences getNode(String nodeName) {
+ return PREFNODE.node(nodeName);
+ }
//////////////////
+
+
+
+ public static boolean getCheckUpdates() {
+ return PREFNODE.getBoolean(CHECK_UPDATES, BuildPropertyHolder.DEFAULT_CHECK_UPDATES);
+ }
+
+ public static void setCheckUpdates(boolean check) {
+ PREFNODE.putBoolean(CHECK_UPDATES, check);
+ storeVersion();
+ }
+
public static File getDefaultDirectory() {
String file = PREFNODE.get("defaultDirectory", null);
if (file == null)
}
+ /**
+ * Return a list of files/directories to be loaded as custom thrust curves.
+ * <p>
+ * If this property has not been set, the directory "ThrustCurves" in the user
+ * application directory will be used. The directory will be created if it does not
+ * exist.
+ *
+ * @return a list of files to load as thrust curves.
+ */
+ public static List<File> getUserThrustCurveFiles() {
+ List<File> list = new ArrayList<File>();
+
+ String files = getString(USER_THRUST_CURVES_KEY, null);
+ if (files == null) {
+ // Default to application directory
+ File tcdir = getDefaultUserThrustCurveFile();
+ if (!tcdir.isDirectory()) {
+ tcdir.mkdirs();
+ }
+ list.add(tcdir);
+ } else {
+ for (String file : files.split("\\" + SPLIT_CHARACTER)) {
+ file = file.trim();
+ if (file.length() > 0) {
+ list.add(new File(file));
+ }
+ }
+ }
+
+ return list;
+ }
+
+ public static File getDefaultUserThrustCurveFile() {
+ File appdir = SystemInfo.getUserApplicationDirectory();
+ File tcdir = new File(appdir, "ThrustCurves");
+ return tcdir;
+ }
+
+
+ /**
+ * Set the list of files/directories to be loaded as custom thrust curves.
+ *
+ * @param files the files to load, or <code>null</code> to reset to default value.
+ */
+ public static void setUserThrustCurveFiles(List<File> files) {
+ if (files == null) {
+ putString(USER_THRUST_CURVES_KEY, null);
+ return;
+ }
+
+ String str = "";
+
+ for (File file : files) {
+ if (str.length() > 0) {
+ str += SPLIT_CHARACTER;
+ }
+ str += file.getAbsolutePath();
+ }
+ putString(USER_THRUST_CURVES_KEY, str);
+ }
+
+
+
+
public static Color getDefaultColor(Class<? extends RocketComponent> c) {
String color = get("componentColors", c, DEFAULT_COLORS);
if (color == null)
return Color.BLACK;
-
+
String[] rgb = color.split(",");
- if (rgb.length==3) {
+ if (rgb.length == 3) {
try {
- int red = MathUtil.clamp(Integer.parseInt(rgb[0]),0,255);
- int green = MathUtil.clamp(Integer.parseInt(rgb[1]),0,255);
- int blue = MathUtil.clamp(Integer.parseInt(rgb[2]),0,255);
- return new Color(red,green,blue);
- } catch (NumberFormatException ignore) { }
+ int red = MathUtil.clamp(Integer.parseInt(rgb[0]), 0, 255);
+ int green = MathUtil.clamp(Integer.parseInt(rgb[1]), 0, 255);
+ int blue = MathUtil.clamp(Integer.parseInt(rgb[2]), 0, 255);
+ return new Color(red, green, blue);
+ } catch (NumberFormatException ignore) {
+ }
}
-
+
return Color.BLACK;
}
public static void setDefaultColor(Class<? extends RocketComponent> c, Color color) {
- if (color==null)
+ if (color == null)
return;
String string = color.getRed() + "," + color.getGreen() + "," + color.getBlue();
set("componentColors", c, string);
public static Color getMotorBorderColor() {
// TODO: MEDIUM: Motor color (settable?)
- return new Color(0,0,0,200);
+ return new Color(0, 0, 0, 200);
}
-
+
public static Color getMotorFillColor() {
// TODO: MEDIUM: Motor fill color (settable?)
- return new Color(0,0,0,100);
+ return new Color(0, 0, 0, 100);
}
set("componentStyle", c, style.name());
}
-
+
/**
* Return the DPI setting of the monitor. This is either the setting provided
* by the system or a user-specified DPI setting.
* @return the DPI setting to use.
*/
public static double getDPI() {
- int dpi = PREFNODE.getInt("DPI", 0); // Tenths of a dpi
+ int dpi = PREFNODE.getInt("DPI", 0); // Tenths of a dpi
if (dpi < 10) {
- dpi = Toolkit.getDefaultToolkit().getScreenResolution()*10;
+ dpi = Toolkit.getDefaultToolkit().getScreenResolution() * 10;
}
if (dpi < 10)
dpi = 960;
- return ((double)dpi)/10.0;
+ return (dpi) / 10.0;
}
}
-
-
+
+
public static Material getDefaultComponentMaterial(
Class<? extends RocketComponent> componentClass,
Material.Type type) {
Material m = Material.fromStorableString(material, false);
if (m.getType() == type)
return m;
- } catch (IllegalArgumentException ignore) { }
+ } catch (IllegalArgumentException ignore) {
+ }
}
switch (type) {
case LINE:
- return DEFAULT_LINE_MATERIAL;
+ return DefaultMaterialHolder.DEFAULT_LINE_MATERIAL;
case SURFACE:
- return DEFAULT_SURFACE_MATERIAL;
+ return DefaultMaterialHolder.DEFAULT_SURFACE_MATERIAL;
case BULK:
- return DEFAULT_BULK_MATERIAL;
+ return DefaultMaterialHolder.DEFAULT_BULK_MATERIAL;
}
- throw new IllegalArgumentException("Unknown material type: "+type);
+ throw new IllegalArgumentException("Unknown material type: " + type);
}
public static void setDefaultComponentMaterial(
Class<? extends RocketComponent> componentClass, Material material) {
- set("componentMaterials", componentClass,
- material==null ? null : material.toStorableString());
+ set("componentMaterials", componentClass,
+ material == null ? null : material.toStorableString());
}
}
+ /**
+ * Return whether to use additional safety code checks.
+ */
+ public static boolean useSafetyChecks() {
+ // Currently default to false unless openrocket.debug.safetycheck is defined
+ String s = System.getProperty("openrocket.debug.safetycheck");
+ if (s != null && !(s.equalsIgnoreCase("false") || s.equalsIgnoreCase("off"))) {
+ return true;
+ }
+ return false;
+ }
+
public static Point getWindowPosition(Class<?> c) {
int x, y;
if (pref == null)
return null;
- if (pref.indexOf(',')<0)
+ if (pref.indexOf(',') < 0)
return null;
try {
- x = Integer.parseInt(pref.substring(0,pref.indexOf(',')));
- y = Integer.parseInt(pref.substring(pref.indexOf(',')+1));
+ x = Integer.parseInt(pref.substring(0, pref.indexOf(',')));
+ y = Integer.parseInt(pref.substring(pref.indexOf(',') + 1));
} catch (NumberFormatException e) {
return null;
}
- return new Point(x,y);
+ return new Point(x, y);
}
public static void setWindowPosition(Class<?> c, Point p) {
}
-
+
public static Dimension getWindowSize(Class<?> c) {
int x, y;
if (pref == null)
return null;
- if (pref.indexOf(',')<0)
+ if (pref.indexOf(',') < 0)
return null;
try {
- x = Integer.parseInt(pref.substring(0,pref.indexOf(',')));
- y = Integer.parseInt(pref.substring(pref.indexOf(',')+1));
+ x = Integer.parseInt(pref.substring(0, pref.indexOf(',')));
+ y = Integer.parseInt(pref.substring(pref.indexOf(',') + 1));
} catch (NumberFormatException e) {
return null;
}
- return new Dimension(x,y);
+ return new Dimension(x, y);
}
public static void setWindowSize(Class<?> c, Dimension d) {
public static Simulation getBackgroundSimulation(Rocket rocket) {
Simulation s = new Simulation(rocket);
- SimulationConditions cond = s.getConditions();
+ GUISimulationConditions cond = s.getConditions();
- cond.setTimeStep(RK4Simulator.RECOMMENDED_TIME_STEP*2);
+ cond.setTimeStep(RK4SimulationStepper.RECOMMENDED_TIME_STEP * 2);
cond.setWindSpeedAverage(1.0);
cond.setWindSpeedDeviation(0.1);
cond.setLaunchRodLength(5);
}
-
+
///////// Export variables
- public static boolean isExportSelected(FlightDataBranch.Type type) {
+ public static boolean isExportSelected(FlightDataType type) {
Preferences prefs = PREFNODE.node("exports");
return prefs.getBoolean(type.getName(), false);
}
- public static void setExportSelected(FlightDataBranch.Type type, boolean selected) {
+ public static void setExportSelected(FlightDataType type, boolean selected) {
Preferences prefs = PREFNODE.node("exports");
prefs.putBoolean(type.getName(), selected);
}
-
+
///////// Default unit storage
public static void loadDefaultUnits() {
Preferences prefs = PREFNODE.node("units");
try {
- for (String key: prefs.keys()) {
+ for (String key : prefs.keys()) {
UnitGroup group = UnitGroup.UNITS.get(key);
if (group == null)
continue;
- group.setDefaultUnit(prefs.get(key, null));
+ try {
+ group.setDefaultUnit(prefs.get(key, null));
+ } catch (IllegalArgumentException ignore) {
+ }
}
} catch (BackingStoreException e) {
public static void storeDefaultUnits() {
Preferences prefs = PREFNODE.node("units");
- for (String key: UnitGroup.UNITS.keySet()) {
+ for (String key : UnitGroup.UNITS.keySet()) {
UnitGroup group = UnitGroup.UNITS.get(key);
if (group == null || group.getUnitCount() < 2)
continue;
}
-
- //// Material storage
+ //// Material storage
+
/**
* Add a user-defined material to the preferences. The preferences are
* first checked for an existing material matching the provided one using
public static void addUserMaterial(Material m) {
Preferences prefs = PREFNODE.node("userMaterials");
-
+
// Check whether material already exists
if (getUserMaterials().contains(m)) {
return;
}
-
+
// Add material using next free key (key is not used when loading)
String mat = m.toStorableString();
- for (int i = 0; ; i++) {
+ for (int i = 0;; i++) {
String key = "material" + i;
if (prefs.get(key, null) == null) {
prefs.put(key, mat);
}
}
}
-
+
/**
* Remove a user-defined material from the preferences. The matching is performed
try {
// Iterate through materials and remove all keys with a matching material
- for (String key: prefs.keys()) {
+ for (String key : prefs.keys()) {
String value = prefs.get(key, null);
try {
prefs.remove(key);
}
- } catch (IllegalArgumentException ignore) { }
+ } catch (IllegalArgumentException ignore) {
+ }
}
HashSet<Material> materials = new HashSet<Material>();
try {
- for (String key: prefs.keys()) {
+ for (String key : prefs.keys()) {
String value = prefs.get(key, null);
try {
Material m = Material.fromStorableString(value, true);
materials.add(m);
- } catch (IllegalArgumentException e) {
- System.err.println("Illegal material string " + value);
+ } catch (IllegalArgumentException e) {
+ log.warn("Illegal material string " + value);
}
}
//// Helper methods
- private static String get(String directory,
+ private static String get(String directory,
Class<? extends RocketComponent> componentClass,
Map<Class<?>, String> defaultMap) {
-
+
// Search preferences
Class<?> c = componentClass;
Preferences prefs = PREFNODE.node(directory);
- while (c!=null && RocketComponent.class.isAssignableFrom(c)) {
+ while (c != null && RocketComponent.class.isAssignableFrom(c)) {
String value = prefs.get(c.getSimpleName(), null);
if (value != null)
return value;
if (defaultMap == null)
return null;
-
+
// Search defaults
c = componentClass;
while (RocketComponent.class.isAssignableFrom(c)) {
return null;
}
-
-
+
+
private static void set(String directory, Class<? extends RocketComponent> componentClass,
String value) {
Preferences prefs = PREFNODE.node(directory);
prefs.put(componentClass.getSimpleName(), value);
storeVersion();
}
-
+
}