" + (mass.getRadialDirection() * 180.0 / Math.PI)
diff --git a/core/src/net/sf/openrocket/file/openrocket/savers/NoseConeSaver.java b/core/src/net/sf/openrocket/file/openrocket/savers/NoseConeSaver.java
index e5c24e571..9c39cca72 100644
--- a/core/src/net/sf/openrocket/file/openrocket/savers/NoseConeSaver.java
+++ b/core/src/net/sf/openrocket/file/openrocket/savers/NoseConeSaver.java
@@ -27,7 +27,7 @@ public class NoseConeSaver extends TransitionSaver {
super.addParams(c, elements);
if (noseCone.isBaseRadiusAutomatic())
- elements.add("auto " + noseCone.getBaseRadiusNoAutomatic() + "");
+ elements.add("auto " + noseCone.getBaseRadius() + "");
else
elements.add("" + noseCone.getBaseRadius() + "");
diff --git a/core/src/net/sf/openrocket/file/openrocket/savers/TransitionSaver.java b/core/src/net/sf/openrocket/file/openrocket/savers/TransitionSaver.java
index b3b2ae8d6..331a95303 100644
--- a/core/src/net/sf/openrocket/file/openrocket/savers/TransitionSaver.java
+++ b/core/src/net/sf/openrocket/file/openrocket/savers/TransitionSaver.java
@@ -46,12 +46,12 @@ public class TransitionSaver extends SymmetricComponentSaver {
}
if (trans.isForeRadiusAutomatic())
- elements.add("auto " + trans.getForeRadiusNoAutomatic() + "");
+ elements.add("auto " + trans.getForeRadius() + "");
else
elements.add("" + trans.getForeRadius() + "");
if (trans.isAftRadiusAutomatic())
- elements.add("auto " + trans.getAftRadiusNoAutomatic() + "");
+ elements.add("auto " + trans.getAftRadius() + "");
else
elements.add("" + trans.getAftRadius() + "");
diff --git a/core/src/net/sf/openrocket/models/atmosphere/InterpolatingAtmosphericModel.java b/core/src/net/sf/openrocket/models/atmosphere/InterpolatingAtmosphericModel.java
index 780adc300..3b34fb9aa 100644
--- a/core/src/net/sf/openrocket/models/atmosphere/InterpolatingAtmosphericModel.java
+++ b/core/src/net/sf/openrocket/models/atmosphere/InterpolatingAtmosphericModel.java
@@ -36,6 +36,10 @@ public abstract class InterpolatingAtmosphericModel implements AtmosphericModel
int n = (int) (altitude / DELTA);
double d = (altitude - n * DELTA) / DELTA;
AtmosphericConditions c = new AtmosphericConditions();
+ // TODO: LOW: levels[n] returned null in some cases, see GitHub issue #2180 for more information
+ if (levels[n] == null) {
+ computeLayers();
+ }
c.setTemperature(levels[n].getTemperature() * (1 - d) + levels[n + 1].getTemperature() * d);
c.setPressure(levels[n].getPressure() * (1 - d) + levels[n + 1].getPressure() * d);
diff --git a/core/src/net/sf/openrocket/rocketcomponent/BodyTube.java b/core/src/net/sf/openrocket/rocketcomponent/BodyTube.java
index 0064356a9..5b42fdcda 100644
--- a/core/src/net/sf/openrocket/rocketcomponent/BodyTube.java
+++ b/core/src/net/sf/openrocket/rocketcomponent/BodyTube.java
@@ -75,35 +75,35 @@ public class BodyTube extends SymmetricComponent implements BoxBounded, MotorMou
@Override
public double getOuterRadius() {
if (autoRadius) {
- // Return auto radius from front or rear
- double r = -1;
- SymmetricComponent c = this.getPreviousSymmetricComponent();
- // Don't use the radius of a component who already has its auto diameter enabled
- if (c != null && !c.usesNextCompAutomatic()) {
- r = c.getFrontAutoRadius();
- refComp = c;
- }
- if (r < 0) {
- c = this.getNextSymmetricComponent();
- // Don't use the radius of a component who already has its auto diameter enabled
- if (c != null && !c.usesPreviousCompAutomatic()) {
- r = c.getRearAutoRadius();
- refComp = c;
- }
- }
- if (r < 0)
- r = DEFAULT_RADIUS;
- return r;
+ outerRadius = getAutoOuterRadius();
}
return outerRadius;
}
/**
- * Return the outer radius that was manually entered, so not the value that the component received from automatic
- * outer radius.
+ * Returns the automatic outer radius, taken from the previous/next component. Returns the default radius if there
+ * is no previous/next component.
*/
- public double getOuterRadiusNoAutomatic() {
- return outerRadius;
+ private double getAutoOuterRadius() {
+ // Return auto radius from front or rear
+ double r = -1;
+ SymmetricComponent c = this.getPreviousSymmetricComponent();
+ // Don't use the radius of a component who already has its auto diameter enabled
+ if (c != null && !c.usesNextCompAutomatic()) {
+ r = c.getFrontAutoRadius();
+ refComp = c;
+ }
+ if (r < 0) {
+ c = this.getNextSymmetricComponent();
+ // Don't use the radius of a component who already has its auto diameter enabled
+ if (c != null && !c.usesPreviousCompAutomatic()) {
+ r = c.getRearAutoRadius();
+ refComp = c;
+ }
+ }
+ if (r < 0)
+ r = DEFAULT_RADIUS;
+ return r;
}
/**
diff --git a/core/src/net/sf/openrocket/rocketcomponent/MassObject.java b/core/src/net/sf/openrocket/rocketcomponent/MassObject.java
index 3d9fa5e6f..d370e3b8e 100644
--- a/core/src/net/sf/openrocket/rocketcomponent/MassObject.java
+++ b/core/src/net/sf/openrocket/rocketcomponent/MassObject.java
@@ -23,6 +23,7 @@ public abstract class MassObject extends InternalComponent {
protected double radius;
private boolean autoRadius = false;
+ private double volume; // (Packed) volume of the object
private double radialPosition;
private double radialDirection;
@@ -40,6 +41,7 @@ public abstract class MassObject extends InternalComponent {
this.length = length;
this.radius = radius;
+ updateVolume(radius);
this.setAxialMethod( AxialMethod.TOP);
this.setAxialOffset(0.0);
@@ -50,40 +52,18 @@ public abstract class MassObject extends InternalComponent {
return false;
}
+ private void updateVolume(double radius) {
+ volume = Math.pow(radius, 2) * length; // Math.PI left out, not needed
+ }
+
@Override
public double getLength() {
- if (this.autoRadius) {
- // Calculate the volume using the non auto radius and the non auto length, and transform that back
- // to the auto radius situation to get the auto radius length (the volume in both situations is the same).
- double volume = Math.pow(this.radius, 2) * this.length; // Math.PI left out, not needed
- return volume / Math.pow(getRadius(), 2);
+ if (autoRadius) {
+ length = getAutoLength();
}
return length;
}
- public double getLengthNoAuto() {
- return length;
- }
-
- /**
- * Set the length, ignoring the auto radius setting.
- * @param length new length
- */
- public void setLengthNoAuto(double length) {
- for (RocketComponent listener : configListeners) {
- if (listener instanceof MassObject) {
- ((MassObject) listener).setLengthNoAuto(length);
- }
- }
-
- length = Math.max(length, 0);
- if (MathUtil.equals(this.length, length)) {
- return;
- }
- this.length = length;
- fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
- }
-
public void setLength(double length) {
for (RocketComponent listener : configListeners) {
if (listener instanceof MassObject) {
@@ -92,45 +72,54 @@ public abstract class MassObject extends InternalComponent {
}
length = Math.max(length, 0);
- if (this.autoRadius) {
- // Calculate the volume using the auto radius and the new "auto" length, and transform that back
- // to the non auto radius situation to set this.length (the volume in both situations is the same).
- double volume = Math.pow(getRadius(), 2) * length; // Math.PI left out, not needed
- double newLength = volume / Math.pow(this.radius, 2);
- if (MathUtil.equals(this.length, newLength))
- return;
- this.length = newLength;
- } else {
- if (MathUtil.equals(this.length, length)) {
- return;
- }
- this.length = length;
+
+ if (MathUtil.equals(this.length, length)) {
+ return;
}
+ this.length = length;
+ updateVolume(autoRadius ? getAutoRadius() : radius);
+
fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
}
+
+ /**
+ * Calculate the length from the current volume.
+ */
+ private double getAutoLength() {
+ // Calculate the volume using the auto radius and the new "auto" length, and transform that back
+ // to the non auto radius situation to set this.length (the volume in both situations is the same).
+ return volume / Math.pow(radius, 2);
+ }
public double getRadius() {
if (autoRadius) {
- if (parent == null) {
- return radius;
- }
- if (parent instanceof NoseCone) {
- return ((NoseCone) parent).getAftRadius();
- } else if (parent instanceof Transition) {
- double foreRadius = ((Transition) parent).getForeRadius();
- double aftRadius = ((Transition) parent).getAftRadius();
- return (Math.max(foreRadius, aftRadius));
- } else if (parent instanceof BodyComponent) {
- return ((BodyComponent) parent).getInnerRadius();
- } else if (parent instanceof RingComponent) {
- return ((RingComponent) parent).getInnerRadius();
- }
+ radius = getAutoRadius();
+ length = getAutoLength();
}
return radius;
}
- public double getRadiusNoAuto() {
+ /**
+ * Return the radius determined by its parent component.
+ * @return the radius determined by its parent component
+ */
+ public double getAutoRadius() {
+ if (parent == null) {
+ return radius;
+ }
+ if (parent instanceof NoseCone) {
+ return ((NoseCone) parent).getAftRadius();
+ } else if (parent instanceof Transition) {
+ double foreRadius = ((Transition) parent).getForeRadius();
+ double aftRadius = ((Transition) parent).getAftRadius();
+ return (Math.max(foreRadius, aftRadius));
+ } else if (parent instanceof BodyComponent) {
+ return ((BodyComponent) parent).getInnerRadius();
+ } else if (parent instanceof RingComponent) {
+ return ((RingComponent) parent).getInnerRadius();
+ }
+
return radius;
}
@@ -148,6 +137,7 @@ public abstract class MassObject extends InternalComponent {
this.autoRadius = false;
this.radius = radius;
+ updateVolume(radius);
fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
}
@@ -167,10 +157,6 @@ public abstract class MassObject extends InternalComponent {
autoRadius = auto;
- // Set the length
- double volume = (Math.PI * Math.pow(getRadius(), 2) * length);
- length = volume / (Math.PI * Math.pow(getRadius(), 2));
-
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
}
diff --git a/core/src/net/sf/openrocket/rocketcomponent/NoseCone.java b/core/src/net/sf/openrocket/rocketcomponent/NoseCone.java
index 5e174bc24..ea76f4be1 100644
--- a/core/src/net/sf/openrocket/rocketcomponent/NoseCone.java
+++ b/core/src/net/sf/openrocket/rocketcomponent/NoseCone.java
@@ -52,14 +52,6 @@ public class NoseCone extends Transition implements InsideColorComponent {
return isFlipped ? getForeRadius() : getAftRadius();
}
- /**
- * Returns the raw base radius of the nose cone (independent of whether the nose cone is flipped or not).
- * This method should be used over {@link #getAftRadiusNoAutomatic()} because it works for both normal and flipped nose cones.
- */
- public double getBaseRadiusNoAutomatic() {
- return isFlipped ? getForeRadiusNoAutomatic() : getAftRadiusNoAutomatic();
- }
-
/**
* Sets the base radius of the nose cone (independent of whether the nose cone is flipped or not).
* This method should be used over {@link #setAftRadius(double)} because it works for both normal and flipped nose cones.
@@ -203,7 +195,7 @@ public class NoseCone extends Transition implements InsideColorComponent {
boolean previousByPass = isBypassComponentChangeEvent();
setBypassChangeEvent(true);
if (flipped) {
- setForeRadius(getAftRadiusNoAutomatic());
+ setForeRadius(getAftRadius());
setForeRadiusAutomatic(isAftRadiusAutomatic(), sanityCheck);
setForeShoulderLength(getAftShoulderLength());
setForeShoulderRadius(getAftShoulderRadius());
@@ -212,7 +204,7 @@ public class NoseCone extends Transition implements InsideColorComponent {
resetAftRadius();
} else {
- setAftRadius(getForeRadiusNoAutomatic());
+ setAftRadius(getForeRadius());
setAftRadiusAutomatic(isForeRadiusAutomatic(), sanityCheck);
setAftShoulderLength(getForeShoulderLength());
setAftShoulderRadius(getForeShoulderRadius());
diff --git a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java
index 313022179..01b5b6820 100644
--- a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java
+++ b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java
@@ -1338,8 +1338,19 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
if( 0 == thisIndex ) {
this.position = this.position.setX(0.);
}else if( 0 < thisIndex ) {
- RocketComponent referenceComponent = parent.getChild( thisIndex - 1 );
-
+ int idx = thisIndex - 1;
+ RocketComponent referenceComponent = parent.getChild(idx);
+ while (!getRocket().getSelectedConfiguration().isComponentActive(referenceComponent) && idx > 0) {
+ idx--;
+ referenceComponent = parent.getChild(idx);
+ }
+
+ // If previous components are inactive, set this as the new reference point
+ if (!getRocket().getSelectedConfiguration().isComponentActive(referenceComponent)) {
+ this.position = this.position.setX(0.);
+ return;
+ }
+
double refLength = referenceComponent.getLength();
double refRelX = referenceComponent.getPosition().x;
@@ -1668,6 +1679,22 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
/////////// Children handling ///////////
+ /**
+ * Adds a child to the rocket component tree. The component is added to the end
+ * of the component's child list. This is a helper method that calls
+ * {@link #addChild(RocketComponent,int)}.
+ *
+ * @param component The component to add.
+ * @param trackStage If component is a stage, this check will decide whether the rocket should track that stage (add it to the stageList etc.)
+ * @throws IllegalArgumentException if the component is already part of some
+ * component tree.
+ * @see #addChild(RocketComponent,int)
+ */
+ public final void addChild(RocketComponent component, boolean trackStage) {
+ checkState();
+ addChild(component, children.size(), trackStage);
+ }
+
/**
* Adds a child to the rocket component tree. The component is added to the end
* of the component's child list. This is a helper method that calls
@@ -1679,10 +1706,9 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
* @see #addChild(RocketComponent,int)
*/
public final void addChild(RocketComponent component) {
- checkState();
- addChild(component, children.size());
+ addChild(component, true);
}
-
+
/**
* Adds a child to the rocket component tree. The component is added to
* the given position of the component's child list.
@@ -1692,28 +1718,29 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
*
* @param component The component to add.
* @param index Position to add component to.
+ * @param trackStage If component is a stage, this check will decide whether the rocket should track that stage (add it to the stageList etc.)
* @throws IllegalArgumentException If the component is already part of
* some component tree.
*/
- public void addChild(RocketComponent component, int index) {
+ public void addChild(RocketComponent component, int index, boolean trackStage) {
checkState();
-
+
if (component.parent != null) {
throw new IllegalArgumentException("component " + component.getComponentName() +
" is already in a tree");
}
-
+
// Ensure that the no loops are created in component tree [A -> X -> Y -> B, B.addChild(A)]
if (this.getRoot().equals(component)) {
throw new IllegalStateException("Component " + component.getComponentName() +
" is a parent of " + this.getComponentName() + ", attempting to create cycle in tree.");
}
-
+
if (!isCompatible(component)) {
throw new IllegalStateException("Component: " + component.getComponentName() +
" not currently compatible with component: " + getComponentName());
}
-
+
children.add(index, component);
component.parent = this;
if (this.massOverridden && this.overrideSubcomponentsMass) {
@@ -1746,18 +1773,48 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
child.CDOverriddenBy = component.CDOverriddenBy;
}
}
-
- if (component instanceof AxialStage) {
+
+ if (trackStage && (component instanceof AxialStage)) {
AxialStage nStage = (AxialStage) component;
this.getRocket().trackStage(nStage);
}
-
+
this.checkComponentStructure();
component.checkComponentStructure();
-
+
fireAddRemoveEvent(component);
}
+ /**
+ * Adds a child to the rocket component tree. The component is added to
+ * the given position of the component's child list.
+ *
+ * This method may be overridden to enforce more strict component addition rules.
+ * The tests should be performed first and then this method called.
+ *
+ * @param component The component to add.
+ * @param index Position to add component to.
+ * @throws IllegalArgumentException If the component is already part of
+ * some component tree.
+ */
+ public void addChild(RocketComponent component, int index) {
+ addChild(component, index, true);
+ }
+
+ /**
+ * Removes a child from the rocket component tree.
+ * (redirect to the removed-by-component
+ *
+ * @param n remove the n'th child.
+ * @param trackStage If component is a stage, this check will decide whether the rocket should track that stage (remove it to the stageList etc.)
+ * @throws IndexOutOfBoundsException if n is out of bounds
+ */
+ public final void removeChild(int n, boolean trackStage) {
+ checkState();
+ RocketComponent component = this.getChild(n);
+ this.removeChild(component, trackStage);
+ }
+
/**
* Removes a child from the rocket component tree.
* (redirect to the removed-by-component
@@ -1766,9 +1823,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
* @throws IndexOutOfBoundsException if n is out of bounds
*/
public final void removeChild(int n) {
- checkState();
- RocketComponent component = this.getChild(n);
- this.removeChild(component);
+ removeChild(n, true);
}
/**
@@ -1776,9 +1831,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
* is not present as a child.
*
* @param component the component to remove
+ * @param trackStage If component is a stage, this check will decide whether the rocket should track that stage (remove it to the stageList etc.)
* @return whether the component was a child
*/
- public final boolean removeChild(RocketComponent component) {
+ public final boolean removeChild(RocketComponent component, boolean trackStage) {
checkState();
component.checkComponentStructure();
@@ -1800,15 +1856,17 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
c.CDOverriddenBy = null;
}
}
-
- if (component instanceof AxialStage) {
- AxialStage stage = (AxialStage) component;
- this.getRocket().forgetStage(stage);
- }
- // Remove sub-stages of the removed component
- for (AxialStage stage : component.getSubStages()) {
- this.getRocket().forgetStage(stage);
+ if (trackStage) {
+ if (component instanceof AxialStage) {
+ AxialStage stage = (AxialStage) component;
+ this.getRocket().forgetStage(stage);
+ }
+
+ // Remove sub-stages of the removed component
+ for (AxialStage stage : component.getSubStages()) {
+ this.getRocket().forgetStage(stage);
+ }
}
this.checkComponentStructure();
@@ -1821,6 +1879,17 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
}
return false;
}
+
+ /**
+ * Removes a child from the rocket component tree. Does nothing if the component
+ * is not present as a child.
+ *
+ * @param component the component to remove
+ * @return whether the component was a child
+ */
+ public final boolean removeChild(RocketComponent component) {
+ return removeChild(component, true);
+ }
diff --git a/core/src/net/sf/openrocket/rocketcomponent/Transition.java b/core/src/net/sf/openrocket/rocketcomponent/Transition.java
index 5fb9822eb..a518844bf 100644
--- a/core/src/net/sf/openrocket/rocketcomponent/Transition.java
+++ b/core/src/net/sf/openrocket/rocketcomponent/Transition.java
@@ -77,7 +77,7 @@ public class Transition extends SymmetricComponent implements InsideColorCompone
@Override
public double getForeRadius() {
if (isForeRadiusAutomatic()) {
- return getAutoForeRadius();
+ foreRadius = getAutoForeRadius();
}
return foreRadius;
}
@@ -95,14 +95,6 @@ public class Transition extends SymmetricComponent implements InsideColorCompone
}
}
- /**
- * Return the fore radius that was manually entered, so not the value that the component received from automatic
- * fore radius.
- */
- public double getForeRadiusNoAutomatic() {
- return foreRadius;
- }
-
/**
* Set the new fore radius, with option to clamp the thickness to the new radius if it's too large.
* @param radius new radius
@@ -174,7 +166,7 @@ public class Transition extends SymmetricComponent implements InsideColorCompone
@Override
public double getAftRadius() {
if (isAftRadiusAutomatic()) {
- return getAutoAftRadius();
+ aftRadius = getAutoAftRadius();
}
return aftRadius;
}
@@ -192,14 +184,6 @@ public class Transition extends SymmetricComponent implements InsideColorCompone
}
}
- /**
- * Return the aft radius that was manually entered, so not the value that the component received from automatic
- * zft radius.
- */
- public double getAftRadiusNoAutomatic() {
- return aftRadius;
- }
-
/**
* Set the new aft radius, with option to clamp the thickness to the new radius if it's too large.
* @param radius new radius
diff --git a/core/src/net/sf/openrocket/startup/Preferences.java b/core/src/net/sf/openrocket/startup/Preferences.java
index ade03131e..0830222ae 100644
--- a/core/src/net/sf/openrocket/startup/Preferences.java
+++ b/core/src/net/sf/openrocket/startup/Preferences.java
@@ -53,7 +53,8 @@ public abstract class Preferences implements ChangeSource {
public static final String EXPORT_EVENT_COMMENTS = "ExportEventComments";
public static final String EXPORT_COMMENT_CHARACTER = "ExportCommentCharacter";
public static final String USER_LOCAL = "locale";
-
+ public static final String DEFAULT_DIRECTORY = "defaultDirectory";
+
public static final String PLOT_SHOW_POINTS = "ShowPlotPoints";
private static final String IGNORE_WELCOME = "IgnoreWelcome";
@@ -73,20 +74,22 @@ public abstract class Preferences implements ChangeSource {
public static final String MATCH_AFT_DIAMETER = "MatchAftDiameter";
// Node names
- public static final String PREFERRED_THRUST_CURVE_MOTOR_NODE = "preferredThrustCurveMotors";
- private static final String AUTO_OPEN_LAST_DESIGN = "AUTO_OPEN_LAST_DESIGN";
- private static final String OPEN_LEFTMOST_DESIGN_TAB = "OPEN_LEFTMOST_DESIGN_TAB";
+ public static final String PREFERRED_THRUST_CURVE_MOTOR_NODE = "PreferredThrustCurveMotors";
+ private static final String AUTO_OPEN_LAST_DESIGN = "AutoOpenLastDesign";
+ private static final String OPEN_LEFTMOST_DESIGN_TAB = "OpenLeftmostDesignTab";
private static final String SHOW_DISCARD_CONFIRMATION = "IgnoreDiscardEditingWarning";
private static final String SHOW_DISCARD_SIMULATION_CONFIRMATION = "IgnoreDiscardSimulationEditingWarning";
public static final String MARKER_STYLE_ICON = "MARKER_STYLE_ICON";
private static final String SHOW_MARKERS = "SHOW_MARKERS";
private static final String SHOW_RASAERO_FORMAT_WARNING = "SHOW_RASAERO_FORMAT_WARNING";
private static final String SHOW_ROCKSIM_FORMAT_WARNING = "SHOW_ROCKSIM_FORMAT_WARNING";
+ private static final String EXPORT_USER_DIRECTORIES = "ExportUserDirectories";
+ private static final String EXPORT_WINDOW_INFORMATION = "ExportWindowInformation";
//Preferences related to 3D graphics
- public static final String OPENGL_ENABLED = "OpenGL_Is_Enabled";
- public static final String OPENGL_ENABLE_AA = "OpenGL_Antialiasing_Is_Enabled";
- public static final String OPENGL_USE_FBO = "OpenGL_Use_FBO";
+ public static final String OPENGL_ENABLED = "OpenGLIsEnabled";
+ public static final String OPENGL_ENABLE_AA = "OpenGLAntialiasingIsEnabled";
+ public static final String OPENGL_USE_FBO = "OpenGLUseFBO";
public static final String ROCKET_INFO_FONT_SIZE = "RocketInfoFontSize";
@@ -241,7 +244,23 @@ public abstract class Preferences implements ChangeSource {
public final void setShowRockSimFormatWarning(boolean check) {
this.putBoolean(SHOW_ROCKSIM_FORMAT_WARNING, check);
}
-
+
+ public final boolean getExportUserDirectories() {
+ return this.getBoolean(EXPORT_USER_DIRECTORIES, false);
+ }
+
+ public final void setExportUserDirectories(boolean check) {
+ this.putBoolean(EXPORT_USER_DIRECTORIES, check);
+ }
+
+ public final boolean getExportWindowInformation() {
+ return this.getBoolean(EXPORT_WINDOW_INFORMATION, false);
+ }
+
+ public final void setExportWindowInformation(boolean check) {
+ this.putBoolean(EXPORT_WINDOW_INFORMATION, check);
+ }
+
public final double getDefaultMach() {
return Application.getPreferences().getChoice(Preferences.DEFAULT_MACH_NUMBER, 0.9, 0.3);
}
diff --git a/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java b/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java
index dd27bcc7c..c7a01a770 100644
--- a/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java
+++ b/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java
@@ -998,7 +998,7 @@ public class MassCalculatorTest extends BaseTestCase {
double expTotalMass = overrideMass;
assertEquals(" Booster Launch Mass is incorrect: ", expTotalMass, calcTotalMass, EPSILON);
- double expCMx = 6.484;
+ double expCMx = 5.92;
Coordinate expCM = new Coordinate(expCMx, 0, 0, expTotalMass);
assertEquals(" Booster Launch CM.x is incorrect: ", expCM.x, boosterSetCM.x, EPSILON);
assertEquals(" Booster Launch CM.y is incorrect: ", expCM.y, boosterSetCM.y, EPSILON);
@@ -1052,7 +1052,7 @@ public class MassCalculatorTest extends BaseTestCase {
double expTotalMass = 3.3565872;
assertEquals(" Booster Launch Mass is incorrect: ", expTotalMass, boosterData.getMass(), EPSILON);
- double expCMx = 0.847508988;
+ double expCMx = 0.2835089882645608;
Coordinate expCM = new Coordinate(expCMx, 0, 0, expTotalMass);
assertEquals(" Booster Launch CM.x is incorrect: ", expCM.x, boosterCM.x, EPSILON);
assertEquals(" Booster Launch CM.y is incorrect: ", expCM.y, boosterCM.y, EPSILON);
@@ -1099,7 +1099,7 @@ public class MassCalculatorTest extends BaseTestCase {
double calcTotalMass = structure.getMass();
assertEquals(" Booster Launch Mass is incorrect: ", expMass, calcTotalMass, EPSILON);
- final double expCMx = 1.1191303646438673;
+ final double expCMx = 0.5551303646438673;
Coordinate expCM = new Coordinate(expCMx, 0, 0, expMass);
assertEquals(" Booster Launch CM.x is incorrect: ", expCM.x, structure.getCM().x, EPSILON);
assertEquals(" Booster Launch CM.y is incorrect: ", expCM.y, structure.getCM().y, EPSILON);
diff --git a/core/test/net/sf/openrocket/rocketcomponent/MassObjectTest.java b/core/test/net/sf/openrocket/rocketcomponent/MassObjectTest.java
index 72458ce78..a4df5b6eb 100644
--- a/core/test/net/sf/openrocket/rocketcomponent/MassObjectTest.java
+++ b/core/test/net/sf/openrocket/rocketcomponent/MassObjectTest.java
@@ -21,20 +21,14 @@ public class MassObjectTest extends BaseTestCase {
mo.setLength(0.1);
Assert.assertEquals(String.format(" No auto %s incorrect radius", mo.getClass().getName()),
0.1, mo.getRadius(), MathUtil.EPSILON);
- Assert.assertEquals(String.format(" No auto %s incorrect no auto radius", mo.getClass().getName()),
- 0.1, mo.getRadiusNoAuto(),MathUtil.EPSILON);
Assert.assertEquals(String.format(" No auto %s incorrect length", mo.getClass().getName()),
0.1, mo.getLength(), MathUtil.EPSILON);
- Assert.assertEquals(String.format(" No auto %s incorrect no auto length", mo.getClass().getName()),
- 0.1, mo.getLengthNoAuto(), MathUtil.EPSILON);
Assert.assertEquals(String.format(" No auto %s incorrect CG", mo.getClass().getName()),
0.05, mo.getComponentCG().x, MathUtil.EPSILON);
- mo.setLengthNoAuto(0.1);
+ mo.setLength(0.1);
Assert.assertEquals(String.format(" No auto 2 %s incorrect length", mo.getClass().getName()),
0.1, mo.getLength(), MathUtil.EPSILON);
- Assert.assertEquals(String.format(" No auto 2 %s incorrect no auto length", mo.getClass().getName()),
- 0.1, mo.getLengthNoAuto(), MathUtil.EPSILON);
Assert.assertEquals(String.format(" No auto %s incorrect CG", mo.getClass().getName()),
0.05, mo.getComponentCG().x, MathUtil.EPSILON);
@@ -46,12 +40,8 @@ public class MassObjectTest extends BaseTestCase {
mo.setRadiusAutomatic(true);
Assert.assertEquals(String.format(" Auto 1 %s incorrect radius", mo.getClass().getName()),
0.05, mo.getRadius(), MathUtil.EPSILON);
- Assert.assertEquals(String.format(" Auto 1 %s incorrect no auto radius", mo.getClass().getName()),
- 0.1, mo.getRadiusNoAuto(), MathUtil.EPSILON);
Assert.assertEquals(String.format(" Auto 1 %s incorrect length", mo.getClass().getName()),
0.4, mo.getLength(), MathUtil.EPSILON);
- Assert.assertEquals(String.format(" Auto 1 %s incorrect no auto length", mo.getClass().getName()),
- 0.1, mo.getLengthNoAuto(), MathUtil.EPSILON);
Assert.assertEquals(String.format(" Auto 1 %s incorrect CG", mo.getClass().getName()),
0.2, mo.getComponentCG().x, MathUtil.EPSILON);
@@ -59,12 +49,8 @@ public class MassObjectTest extends BaseTestCase {
parent.setInnerRadius(0.1);
Assert.assertEquals(String.format(" Auto 2 %s incorrect radius", mo.getClass().getName()),
0.1, mo.getRadius(), MathUtil.EPSILON);
- Assert.assertEquals(String.format(" Auto 2 %s incorrect no auto radius", mo.getClass().getName()),
- 0.1, mo.getRadiusNoAuto(), MathUtil.EPSILON);
Assert.assertEquals(String.format(" Auto 2 %s incorrect length", mo.getClass().getName()),
0.1, mo.getLength(), MathUtil.EPSILON);
- Assert.assertEquals(String.format(" Auto 2 %s incorrect no auto length", mo.getClass().getName()),
- 0.1, mo.getLengthNoAuto(), MathUtil.EPSILON);
Assert.assertEquals(String.format(" Auto 2 %s incorrect CG", mo.getClass().getName()),
0.05, mo.getComponentCG().x, MathUtil.EPSILON);
@@ -73,26 +59,18 @@ public class MassObjectTest extends BaseTestCase {
mo.setLength(0.075);
Assert.assertEquals(String.format(" Auto 3 %s incorrect radius", mo.getClass().getName()),
0.075, mo.getRadius(), MathUtil.EPSILON);
- Assert.assertEquals(String.format(" Auto 3 %s incorrect no auto radius", mo.getClass().getName()),
- 0.1, mo.getRadiusNoAuto(), MathUtil.EPSILON);
Assert.assertEquals(String.format(" Auto 3 %s incorrect length", mo.getClass().getName()),
0.075, mo.getLength(), MathUtil.EPSILON);
- Assert.assertEquals(String.format(" Auto 3 %s incorrect no auto length", mo.getClass().getName()),
- 0.0422, mo.getLengthNoAuto(), 0.001);
Assert.assertEquals(String.format(" Auto 3 %s incorrect CG", mo.getClass().getName()),
0.0375, mo.getComponentCG().x, MathUtil.EPSILON);
- mo.setLengthNoAuto(0.05);
+ mo.setLength(0.05);
Assert.assertEquals(String.format(" Auto 4 %s incorrect radius", mo.getClass().getName()),
0.075, mo.getRadius(), MathUtil.EPSILON);
- Assert.assertEquals(String.format(" Auto 4 %s incorrect no auto radius", mo.getClass().getName()),
- 0.1, mo.getRadiusNoAuto(), MathUtil.EPSILON);
Assert.assertEquals(String.format(" Auto 4 %s incorrect length", mo.getClass().getName()),
- 0.0889, mo.getLength(), 0.001);
- Assert.assertEquals(String.format(" Auto 4 %s incorrect no auto length", mo.getClass().getName()),
- 0.05, mo.getLengthNoAuto(), MathUtil.EPSILON);
+ 0.05, mo.getLength(), 0.001);
Assert.assertEquals(String.format(" Auto 4 %s incorrect CG", mo.getClass().getName()),
- 0.044444444444, mo.getComponentCG().x, MathUtil.EPSILON);
+ 0.025, mo.getComponentCG().x, MathUtil.EPSILON);
}
}
}
diff --git a/core/test/net/sf/openrocket/rocketcomponent/NoseConeTest.java b/core/test/net/sf/openrocket/rocketcomponent/NoseConeTest.java
index f8076445b..4757e52b9 100644
--- a/core/test/net/sf/openrocket/rocketcomponent/NoseConeTest.java
+++ b/core/test/net/sf/openrocket/rocketcomponent/NoseConeTest.java
@@ -31,8 +31,8 @@ public class NoseConeTest extends BaseTestCase {
assertEquals(0.06, noseCone.getLength(), EPSILON);
assertEquals(0.1, noseCone.getAftRadius(), EPSILON);
assertEquals(0.1, noseCone.getBaseRadius(), EPSILON);
- assertEquals(0.1, noseCone.getAftRadiusNoAutomatic(), EPSILON);
- assertEquals(0.1, noseCone.getBaseRadiusNoAutomatic(), EPSILON);
+ assertEquals(0.1, noseCone.getAftRadius(), EPSILON);
+ assertEquals(0.1, noseCone.getBaseRadius(), EPSILON);
assertEquals(0.01, noseCone.getAftShoulderLength(), EPSILON);
assertEquals(0.01, noseCone.getShoulderLength(), EPSILON);
assertEquals(0.05, noseCone.getAftShoulderRadius(), EPSILON);
@@ -44,7 +44,7 @@ public class NoseConeTest extends BaseTestCase {
assertFalse(noseCone.isAftRadiusAutomatic());
assertEquals(0, noseCone.getForeRadius(), EPSILON);
- assertEquals(0, noseCone.getForeRadiusNoAutomatic(), EPSILON);
+ assertEquals(0, noseCone.getForeRadius(), EPSILON);
assertEquals(0, noseCone.getForeShoulderLength(), EPSILON);
assertEquals(0, noseCone.getForeShoulderRadius(), EPSILON);
assertEquals(0, noseCone.getForeShoulderThickness(), EPSILON);
@@ -60,8 +60,8 @@ public class NoseConeTest extends BaseTestCase {
assertEquals(0.2, noseCone.getAftRadius(), EPSILON);
assertEquals(0.2, noseCone.getBaseRadius(), EPSILON);
- assertEquals(0.2, noseCone.getAftRadiusNoAutomatic(), EPSILON);
- assertEquals(0.2, noseCone.getBaseRadiusNoAutomatic(), EPSILON);
+ assertEquals(0.2, noseCone.getAftRadius(), EPSILON);
+ assertEquals(0.2, noseCone.getBaseRadius(), EPSILON);
assertEquals(0.03, noseCone.getAftShoulderLength(), EPSILON);
assertEquals(0.03, noseCone.getShoulderLength(), EPSILON);
assertEquals(0.04, noseCone.getAftShoulderRadius(), EPSILON);
@@ -73,7 +73,7 @@ public class NoseConeTest extends BaseTestCase {
assertFalse(noseCone.isAftRadiusAutomatic());
assertEquals(0, noseCone.getForeRadius(), EPSILON);
- assertEquals(0, noseCone.getForeRadiusNoAutomatic(), EPSILON);
+ assertEquals(0, noseCone.getForeRadius(), EPSILON);
assertEquals(0, noseCone.getForeShoulderLength(), EPSILON);
assertEquals(0, noseCone.getForeShoulderRadius(), EPSILON);
assertEquals(0, noseCone.getForeShoulderThickness(), EPSILON);
@@ -99,8 +99,8 @@ public class NoseConeTest extends BaseTestCase {
assertEquals(0.06, noseCone.getLength(), EPSILON);
assertEquals(0.1, noseCone.getForeRadius(), EPSILON);
assertEquals(0.1, noseCone.getBaseRadius(), EPSILON);
- assertEquals(0.1, noseCone.getForeRadiusNoAutomatic(), EPSILON);
- assertEquals(0.1, noseCone.getBaseRadiusNoAutomatic(), EPSILON);
+ assertEquals(0.1, noseCone.getForeRadius(), EPSILON);
+ assertEquals(0.1, noseCone.getBaseRadius(), EPSILON);
assertEquals(0.01, noseCone.getForeShoulderLength(), EPSILON);
assertEquals(0.01, noseCone.getShoulderLength(), EPSILON);
assertEquals(0.05, noseCone.getForeShoulderRadius(), EPSILON);
@@ -112,7 +112,7 @@ public class NoseConeTest extends BaseTestCase {
assertFalse(noseCone.isForeRadiusAutomatic());
assertEquals(0, noseCone.getAftRadius(), EPSILON);
- assertEquals(0, noseCone.getAftRadiusNoAutomatic(), EPSILON);
+ assertEquals(0, noseCone.getAftRadius(), EPSILON);
assertEquals(0, noseCone.getAftShoulderLength(), EPSILON);
assertEquals(0, noseCone.getAftShoulderRadius(), EPSILON);
assertEquals(0, noseCone.getAftShoulderThickness(), EPSILON);
@@ -128,8 +128,8 @@ public class NoseConeTest extends BaseTestCase {
assertEquals(0.2, noseCone.getForeRadius(), EPSILON);
assertEquals(0.2, noseCone.getBaseRadius(), EPSILON);
- assertEquals(0.2, noseCone.getForeRadiusNoAutomatic(), EPSILON);
- assertEquals(0.2, noseCone.getBaseRadiusNoAutomatic(), EPSILON);
+ assertEquals(0.2, noseCone.getForeRadius(), EPSILON);
+ assertEquals(0.2, noseCone.getBaseRadius(), EPSILON);
assertEquals(0.03, noseCone.getForeShoulderLength(), EPSILON);
assertEquals(0.03, noseCone.getShoulderLength(), EPSILON);
assertEquals(0.04, noseCone.getForeShoulderRadius(), EPSILON);
@@ -141,7 +141,7 @@ public class NoseConeTest extends BaseTestCase {
assertFalse(noseCone.isForeRadiusAutomatic());
assertEquals(0, noseCone.getAftRadius(), EPSILON);
- assertEquals(0, noseCone.getAftRadiusNoAutomatic(), EPSILON);
+ assertEquals(0, noseCone.getAftRadius(), EPSILON);
assertEquals(0, noseCone.getAftShoulderLength(), EPSILON);
assertEquals(0, noseCone.getAftShoulderRadius(), EPSILON);
assertEquals(0, noseCone.getAftShoulderThickness(), EPSILON);
@@ -153,8 +153,8 @@ public class NoseConeTest extends BaseTestCase {
assertEquals(0.2, noseCone.getAftRadius(), EPSILON);
assertEquals(0.2, noseCone.getBaseRadius(), EPSILON);
- assertEquals(0.2, noseCone.getAftRadiusNoAutomatic(), EPSILON);
- assertEquals(0.2, noseCone.getBaseRadiusNoAutomatic(), EPSILON);
+ assertEquals(0.2, noseCone.getAftRadius(), EPSILON);
+ assertEquals(0.2, noseCone.getBaseRadius(), EPSILON);
assertEquals(0.03, noseCone.getAftShoulderLength(), EPSILON);
assertEquals(0.03, noseCone.getShoulderLength(), EPSILON);
assertEquals(0.04, noseCone.getAftShoulderRadius(), EPSILON);
@@ -166,7 +166,7 @@ public class NoseConeTest extends BaseTestCase {
assertFalse(noseCone.isAftRadiusAutomatic());
assertEquals(0, noseCone.getForeRadius(), EPSILON);
- assertEquals(0, noseCone.getForeRadiusNoAutomatic(), EPSILON);
+ assertEquals(0, noseCone.getForeRadius(), EPSILON);
assertEquals(0, noseCone.getForeShoulderLength(), EPSILON);
assertEquals(0, noseCone.getForeShoulderRadius(), EPSILON);
assertEquals(0, noseCone.getForeShoulderThickness(), EPSILON);
@@ -180,7 +180,7 @@ public class NoseConeTest extends BaseTestCase {
AxialStage stage = rocket.getStage(0);
NoseCone noseCone = new NoseCone(Transition.Shape.CONICAL, 0.06, 0.01);
- BodyTube tube1 = new BodyTube(0.06, 0.02);
+ BodyTube tube1 = new BodyTube(0.06, 0.023);
tube1.setOuterRadiusAutomatic(false);
BodyTube tube2 = new BodyTube(0.06, 0.03);
tube2.setOuterRadiusAutomatic(false);
@@ -197,11 +197,11 @@ public class NoseConeTest extends BaseTestCase {
assertFalse(noseCone.isAftRadiusAutomatic());
assertFalse(noseCone.isBaseRadiusAutomatic());
assertFalse(noseCone.isForeRadiusAutomatic());
- assertEquals(noseCone.getAftRadius(), noseCone.getAftRadiusNoAutomatic(), EPSILON);
- assertEquals(noseCone.getBaseRadius(), noseCone.getBaseRadiusNoAutomatic(), EPSILON);
- assertEquals(noseCone.getForeRadius(), noseCone.getForeRadiusNoAutomatic(), EPSILON);
- assertEquals(0.01, noseCone.getAftRadiusNoAutomatic(), EPSILON);
- assertEquals(0, noseCone.getForeRadiusNoAutomatic(), EPSILON);
+ assertEquals(noseCone.getAftRadius(), noseCone.getAftRadius(), EPSILON);
+ assertEquals(noseCone.getBaseRadius(), noseCone.getBaseRadius(), EPSILON);
+ assertEquals(noseCone.getForeRadius(), noseCone.getForeRadius(), EPSILON);
+ assertEquals(0.01, noseCone.getAftRadius(), EPSILON);
+ assertEquals(0, noseCone.getForeRadius(), EPSILON);
noseCone.setAftRadiusAutomatic(true, true);
assertFalse(noseCone.isAftRadiusAutomatic());
@@ -222,10 +222,10 @@ public class NoseConeTest extends BaseTestCase {
assertFalse(noseCone.isAftRadiusAutomatic());
assertFalse(noseCone.isBaseRadiusAutomatic());
assertFalse(noseCone.isForeRadiusAutomatic());
- assertEquals(noseCone.getAftRadius(), noseCone.getAftRadiusNoAutomatic(), EPSILON);
- assertEquals(noseCone.getBaseRadius(), noseCone.getBaseRadiusNoAutomatic(), EPSILON);
- assertEquals(noseCone.getForeRadius(), noseCone.getForeRadiusNoAutomatic(), EPSILON);
- assertEquals(0, noseCone.getForeRadiusNoAutomatic(), EPSILON);
+ assertEquals(noseCone.getAftRadius(), noseCone.getAftRadius(), EPSILON);
+ assertEquals(noseCone.getBaseRadius(), noseCone.getBaseRadius(), EPSILON);
+ assertEquals(noseCone.getForeRadius(), noseCone.getForeRadius(), EPSILON);
+ assertEquals(0, noseCone.getForeRadius(), EPSILON);
noseCone.setAftRadiusAutomatic(true, true);
assertFalse(noseCone.usesPreviousCompAutomatic());
@@ -238,24 +238,24 @@ public class NoseConeTest extends BaseTestCase {
assertTrue(noseCone.isBaseRadiusAutomatic());
assertFalse(noseCone.isForeRadiusAutomatic());
assertEquals(tube1.getForeRadius(), noseCone.getAftRadius(), EPSILON);
- assertEquals(0.01, noseCone.getAftRadiusNoAutomatic(), EPSILON);
+ assertEquals(0.023, noseCone.getAftRadius(), EPSILON);
assertEquals(tube1.getForeRadius(), noseCone.getBaseRadius(), EPSILON);
- assertEquals(0.01, noseCone.getBaseRadiusNoAutomatic(), EPSILON);
- assertEquals(noseCone.getForeRadius(), noseCone.getForeRadiusNoAutomatic(), EPSILON);
- assertEquals(0, noseCone.getForeRadiusNoAutomatic(), EPSILON);
+ assertEquals(0.023, noseCone.getBaseRadius(), EPSILON);
+ assertEquals(noseCone.getForeRadius(), noseCone.getForeRadius(), EPSILON);
+ assertEquals(0, noseCone.getForeRadius(), EPSILON);
noseCone.setAftRadiusAutomatic(false, true);
- assertEquals(noseCone.getAftRadius(), noseCone.getAftRadiusNoAutomatic(), EPSILON);
- assertEquals(noseCone.getBaseRadius(), noseCone.getBaseRadiusNoAutomatic(), EPSILON);
- assertEquals(noseCone.getForeRadius(), noseCone.getForeRadiusNoAutomatic(), EPSILON);
- assertEquals(0, noseCone.getForeRadiusNoAutomatic(), EPSILON);
+ assertEquals(noseCone.getAftRadius(), noseCone.getAftRadius(), EPSILON);
+ assertEquals(noseCone.getBaseRadius(), noseCone.getBaseRadius(), EPSILON);
+ assertEquals(noseCone.getForeRadius(), noseCone.getForeRadius(), EPSILON);
+ assertEquals(0, noseCone.getForeRadius(), EPSILON);
noseCone.setBaseRadiusAutomatic(true);
- assertEquals(0.01, noseCone.getAftRadiusNoAutomatic(), EPSILON);
+ assertEquals(0.023, noseCone.getAftRadius(), EPSILON);
assertEquals(tube1.getForeRadius(), noseCone.getBaseRadius(), EPSILON);
- assertEquals(0.01, noseCone.getBaseRadiusNoAutomatic(), EPSILON);
- assertEquals(noseCone.getForeRadius(), noseCone.getForeRadiusNoAutomatic(), EPSILON);
- assertEquals(0, noseCone.getForeRadiusNoAutomatic(), EPSILON);
+ assertEquals(0.023, noseCone.getBaseRadius(), EPSILON);
+ assertEquals(noseCone.getForeRadius(), noseCone.getForeRadius(), EPSILON);
+ assertEquals(0, noseCone.getForeRadius(), EPSILON);
noseCone.setForeRadiusAutomatic(true, true);
assertFalse(noseCone.isForeRadiusAutomatic());
@@ -274,11 +274,11 @@ public class NoseConeTest extends BaseTestCase {
assertTrue(noseCone.isBaseRadiusAutomatic());
assertFalse(noseCone.isForeRadiusAutomatic());
assertEquals(tube1.getForeRadius(), noseCone.getAftRadius(), EPSILON);
- assertEquals(0.01, noseCone.getAftRadiusNoAutomatic(), EPSILON);
+ assertEquals(0.023, noseCone.getAftRadius(), EPSILON);
assertEquals(tube1.getForeRadius(), noseCone.getBaseRadius(), EPSILON);
- assertEquals(0.01, noseCone.getBaseRadiusNoAutomatic(), EPSILON);
- assertEquals(noseCone.getForeRadius(), noseCone.getForeRadiusNoAutomatic(), EPSILON);
- assertEquals(0, noseCone.getForeRadiusNoAutomatic(), EPSILON);
+ assertEquals(0.023, noseCone.getBaseRadius(), EPSILON);
+ assertEquals(noseCone.getForeRadius(), noseCone.getForeRadius(), EPSILON);
+ assertEquals(0, noseCone.getForeRadius(), EPSILON);
// Do a flip
noseCone.setFlipped(true);
@@ -311,11 +311,11 @@ public class NoseConeTest extends BaseTestCase {
assertFalse(noseCone.isAftRadiusAutomatic());
assertFalse(noseCone.isBaseRadiusAutomatic());
assertFalse(noseCone.isForeRadiusAutomatic());
- assertEquals(noseCone.getAftRadius(), noseCone.getAftRadiusNoAutomatic(), EPSILON);
- assertEquals(noseCone.getBaseRadius(), noseCone.getBaseRadiusNoAutomatic(), EPSILON);
- assertEquals(noseCone.getForeRadius(), noseCone.getForeRadiusNoAutomatic(), EPSILON);
- assertEquals(0, noseCone.getAftRadiusNoAutomatic(), EPSILON);
- assertEquals(0.01, noseCone.getForeRadiusNoAutomatic(), EPSILON);
+ assertEquals(noseCone.getAftRadius(), noseCone.getAftRadius(), EPSILON);
+ assertEquals(noseCone.getBaseRadius(), noseCone.getBaseRadius(), EPSILON);
+ assertEquals(noseCone.getForeRadius(), noseCone.getForeRadius(), EPSILON);
+ assertEquals(0, noseCone.getAftRadius(), EPSILON);
+ assertEquals(0.01, noseCone.getForeRadius(), EPSILON);
noseCone.setAftRadiusAutomatic(true, true);
assertFalse(noseCone.isAftRadiusAutomatic());
@@ -336,10 +336,10 @@ public class NoseConeTest extends BaseTestCase {
assertFalse(noseCone.isAftRadiusAutomatic());
assertFalse(noseCone.isBaseRadiusAutomatic());
assertFalse(noseCone.isForeRadiusAutomatic());
- assertEquals(noseCone.getAftRadius(), noseCone.getAftRadiusNoAutomatic(), EPSILON);
- assertEquals(noseCone.getBaseRadius(), noseCone.getBaseRadiusNoAutomatic(), EPSILON);
- assertEquals(noseCone.getForeRadius(), noseCone.getForeRadiusNoAutomatic(), EPSILON);
- assertEquals(0, noseCone.getAftRadiusNoAutomatic(), EPSILON);
+ assertEquals(noseCone.getAftRadius(), noseCone.getAftRadius(), EPSILON);
+ assertEquals(noseCone.getBaseRadius(), noseCone.getBaseRadius(), EPSILON);
+ assertEquals(noseCone.getForeRadius(), noseCone.getForeRadius(), EPSILON);
+ assertEquals(0, noseCone.getAftRadius(), EPSILON);
noseCone.setBaseRadiusAutomatic(true);
assertTrue(noseCone.usesPreviousCompAutomatic());
@@ -352,25 +352,25 @@ public class NoseConeTest extends BaseTestCase {
assertTrue(noseCone.isBaseRadiusAutomatic());
assertTrue(noseCone.isForeRadiusAutomatic());
assertEquals(tube1.getAftRadius(), noseCone.getForeRadius(), EPSILON);
- assertEquals(0.01, noseCone.getForeRadiusNoAutomatic(), EPSILON);
+ assertEquals(0.02, noseCone.getForeRadius(), EPSILON);
assertEquals(tube1.getAftRadius(), noseCone.getBaseRadius(), EPSILON);
- assertEquals(0.01, noseCone.getBaseRadiusNoAutomatic(), EPSILON);
- assertEquals(noseCone.getAftRadius(), noseCone.getAftRadiusNoAutomatic(), EPSILON);
- assertEquals(0, noseCone.getAftRadiusNoAutomatic(), EPSILON);
+ assertEquals(0.02, noseCone.getBaseRadius(), EPSILON);
+ assertEquals(noseCone.getAftRadius(), noseCone.getAftRadius(), EPSILON);
+ assertEquals(0, noseCone.getAftRadius(), EPSILON);
noseCone.setForeRadiusAutomatic(false, true);
- assertEquals(noseCone.getAftRadius(), noseCone.getAftRadiusNoAutomatic(), EPSILON);
- assertEquals(noseCone.getBaseRadius(), noseCone.getBaseRadiusNoAutomatic(), EPSILON);
- assertEquals(noseCone.getForeRadius(), noseCone.getForeRadiusNoAutomatic(), EPSILON);
- assertEquals(0.01, noseCone.getForeRadiusNoAutomatic(), EPSILON);
+ assertEquals(noseCone.getAftRadius(), noseCone.getAftRadius(), EPSILON);
+ assertEquals(noseCone.getBaseRadius(), noseCone.getBaseRadius(), EPSILON);
+ assertEquals(noseCone.getForeRadius(), noseCone.getForeRadius(), EPSILON);
+ assertEquals(0.02, noseCone.getForeRadius(), EPSILON);
noseCone.setBaseRadiusAutomatic(true);
assertEquals(tube1.getAftRadius(), noseCone.getForeRadius(), EPSILON);
- assertEquals(0.01, noseCone.getForeRadiusNoAutomatic(), EPSILON);
+ assertEquals(0.02, noseCone.getForeRadius(), EPSILON);
assertEquals(tube1.getAftRadius(), noseCone.getBaseRadius(), EPSILON);
- assertEquals(0.01, noseCone.getBaseRadiusNoAutomatic(), EPSILON);
- assertEquals(noseCone.getAftRadius(), noseCone.getAftRadiusNoAutomatic(), EPSILON);
- assertEquals(0, noseCone.getAftRadiusNoAutomatic(), EPSILON);
+ assertEquals(0.02, noseCone.getBaseRadius(), EPSILON);
+ assertEquals(noseCone.getAftRadius(), noseCone.getAftRadius(), EPSILON);
+ assertEquals(0, noseCone.getAftRadius(), EPSILON);
assertTrue(noseCone.isForeRadiusAutomatic());
assertTrue(noseCone.isBaseRadiusAutomatic());
@@ -389,10 +389,10 @@ public class NoseConeTest extends BaseTestCase {
assertTrue(noseCone.isBaseRadiusAutomatic());
assertTrue(noseCone.isForeRadiusAutomatic());
assertEquals(tube1.getAftRadius(), noseCone.getForeRadius(), EPSILON);
- assertEquals(0.01, noseCone.getForeRadiusNoAutomatic(), EPSILON);
+ assertEquals(0.02, noseCone.getForeRadius(), EPSILON);
assertEquals(tube1.getForeRadius(), noseCone.getBaseRadius(), EPSILON);
- assertEquals(0.01, noseCone.getBaseRadiusNoAutomatic(), EPSILON);
- assertEquals(noseCone.getAftRadius(), noseCone.getAftRadiusNoAutomatic(), EPSILON);
+ assertEquals(0.02, noseCone.getBaseRadius(), EPSILON);
+ assertEquals(noseCone.getAftRadius(), noseCone.getAftRadius(), EPSILON);
assertEquals(0, noseCone.getAftRadius(), EPSILON);
}
}
diff --git a/core/test/net/sf/openrocket/simulation/DisableStageTest.java b/core/test/net/sf/openrocket/simulation/DisableStageTest.java
index 42b0f77b7..d0eb1e8d4 100644
--- a/core/test/net/sf/openrocket/simulation/DisableStageTest.java
+++ b/core/test/net/sf/openrocket/simulation/DisableStageTest.java
@@ -42,7 +42,7 @@ public class DisableStageTest extends BaseTestCase {
}
}
- //// Test re-enableing the stage.
+ //// Test re-enabling the stage.
Rocket rocketOriginal = TestRockets.makeEstesAlphaIII();
Simulation simOriginal = new Simulation(rocketOriginal);
@@ -81,7 +81,7 @@ public class DisableStageTest extends BaseTestCase {
compareSims(simRemoved, simDisabled, delta);
- //// Test re-enableing the stage.
+ //// Test re-enabling the stage.
Rocket rocketOriginal = TestRockets.makeBeta();
Simulation simOriginal = new Simulation(rocketOriginal);
simOriginal.setFlightConfigurationId(TestRockets.TEST_FCID_1);
@@ -173,7 +173,7 @@ public class DisableStageTest extends BaseTestCase {
compareSims(simRemoved, simDisabled, delta);
- //// Test re-enableing the stage.
+ //// Test re-enabling the stage.
Rocket rocketOriginal = TestRockets.makeFalcon9Heavy();
TestRockets.addCoreFins(rocketOriginal);
@@ -232,7 +232,7 @@ public class DisableStageTest extends BaseTestCase {
}
}
- //// Test re-enableing the stage.
+ //// Test re-enabling the stage.
Rocket rocketOriginal = TestRockets.makeFalcon9Heavy();
TestRockets.addCoreFins(rocketOriginal);
diff --git a/swing/src/net/sf/openrocket/database/MotorDatabaseLoader.java b/swing/src/net/sf/openrocket/database/MotorDatabaseLoader.java
index 279f0cf92..24fd7e4c4 100644
--- a/swing/src/net/sf/openrocket/database/MotorDatabaseLoader.java
+++ b/swing/src/net/sf/openrocket/database/MotorDatabaseLoader.java
@@ -27,6 +27,7 @@ import net.sf.openrocket.util.Pair;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
/**
* An asynchronous database loader that loads the internal thrust curves
@@ -140,21 +141,26 @@ public class MotorDatabaseLoader extends AsynchronousDatabaseLoader {
*/
private void loadFile(GeneralMotorLoader loader, Pair f) {
try {
- List motors = loader.load(f.getV(), f.getU().getName());
try {
+ List motors = loader.load(f.getV(), f.getU().getName());
addMotorsFromBuilders(motors);
}
- catch (IllegalArgumentException e) {
+ catch (IllegalArgumentException | IOException e) {
Translator trans = Application.getTranslator();
String fullPath = f.getU().getPath();
String message = "" + e.getMessage() +
".
" + MessageFormat.format( trans.get("MotorDbLoaderDlg.message1"), fullPath) +
"
" + trans.get("MotorDbLoaderDlg.message2") + "
";
- JOptionPane pane = new JOptionPane(message, JOptionPane.WARNING_MESSAGE);
- JDialog dialog = pane.createDialog(null, trans.get("MotorDbLoaderDlg.title"));
- dialog.setModalityType(Dialog.ModalityType.MODELESS);
- dialog.setAlwaysOnTop(true);
- dialog.setVisible(true);
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ JOptionPane pane = new JOptionPane(message, JOptionPane.WARNING_MESSAGE);
+ JDialog dialog = pane.createDialog(null, trans.get("MotorDbLoaderDlg.title"));
+ dialog.setModalityType(Dialog.ModalityType.MODELESS);
+ dialog.setAlwaysOnTop(true);
+ dialog.setVisible(true);
+ }
+ });
}
f.getV().close();
} catch (Exception e) {
diff --git a/swing/src/net/sf/openrocket/gui/components/PreferencesOptionPanel.java b/swing/src/net/sf/openrocket/gui/components/PreferencesOptionPanel.java
new file mode 100644
index 000000000..a87e7a19b
--- /dev/null
+++ b/swing/src/net/sf/openrocket/gui/components/PreferencesOptionPanel.java
@@ -0,0 +1,65 @@
+package net.sf.openrocket.gui.components;
+
+import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.l10n.Translator;
+import net.sf.openrocket.startup.Application;
+import net.sf.openrocket.startup.Preferences;
+
+import javax.swing.BorderFactory;
+import javax.swing.JCheckBox;
+import javax.swing.JPanel;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+
+/**
+ * A panel that adds storage options for exporting preferences.
+ */
+public class PreferencesOptionPanel extends JPanel {
+ private static final Translator trans = Application.getTranslator();
+ private static final Preferences prefs = Application.getPreferences();
+
+ private final JCheckBox exportUserDirectories;
+ private final JCheckBox exportWindowInfo;
+
+ public PreferencesOptionPanel() {
+ super(new MigLayout("fill, ins 0"));
+
+ JPanel panel = new JPanel(new MigLayout("fill, ins 4lp"));
+ panel.setBorder(BorderFactory.createTitledBorder(trans.get("PreferencesOptionPanel.title")));
+
+ // Export user directories
+ exportUserDirectories = new JCheckBox(trans.get("PreferencesOptionPanel.checkbox.userDirectories"));
+ exportUserDirectories.setToolTipText(trans.get("PreferencesOptionPanel.checkbox.userDirectories.ttip"));
+ exportUserDirectories.setSelected(prefs.getExportUserDirectories());
+ exportUserDirectories.addItemListener(new ItemListener() {
+ @Override
+ public void itemStateChanged(ItemEvent e) {
+ prefs.setExportUserDirectories(e.getStateChange() == ItemEvent.SELECTED);
+ }
+ });
+ panel.add(exportUserDirectories, "wrap");
+
+ // Export window information (position, size...)
+ exportWindowInfo = new JCheckBox(trans.get("PreferencesOptionPanel.checkbox.windowInfo"));
+ exportWindowInfo.setToolTipText(trans.get("PreferencesOptionPanel.checkbox.windowInfo.ttip"));
+ exportWindowInfo.setSelected(prefs.getExportWindowInformation());
+ exportWindowInfo.addItemListener(new ItemListener() {
+ @Override
+ public void itemStateChanged(ItemEvent e) {
+ prefs.setExportWindowInformation(e.getStateChange() == ItemEvent.SELECTED);
+ }
+ });
+ panel.add(exportWindowInfo, "wrap 10lp");
+
+
+ this.add(panel, "growx, north");
+ }
+
+ public boolean isIgnoreUserDirectories() {
+ return !exportUserDirectories.isSelected();
+ }
+
+ public boolean isIgnoreWindowInformation() {
+ return !exportWindowInfo.isSelected();
+ }
+}
diff --git a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java
index 9f898774c..cce14deba 100644
--- a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java
+++ b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java
@@ -303,6 +303,7 @@ public class FreeformFinSetConfig extends FinSetConfig {
}
FreeformFinSetConfig.writeCSVFile(table, selectedFile);
+ ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(chooser.getCurrentDirectory());
}
}
});
@@ -400,6 +401,7 @@ public class FreeformFinSetConfig extends FinSetConfig {
trans.get("CustomFinImport.error.title"), JOptionPane.ERROR_MESSAGE);
} finally {
document.stopUndo();
+ ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(chooser.getCurrentDirectory());
}
}
}
diff --git a/swing/src/net/sf/openrocket/gui/customexpression/CustomExpressionPanel.java b/swing/src/net/sf/openrocket/gui/customexpression/CustomExpressionPanel.java
index 272044bc4..394daec86 100644
--- a/swing/src/net/sf/openrocket/gui/customexpression/CustomExpressionPanel.java
+++ b/swing/src/net/sf/openrocket/gui/customexpression/CustomExpressionPanel.java
@@ -18,6 +18,7 @@ import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileNameExtensionFilter;
+import net.sf.openrocket.gui.util.SwingPreferences;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -110,6 +111,7 @@ public class CustomExpressionPanel extends JPanel {
log.info(Markers.USER_MARKER, "Error opening document to import expressions from.");
}
updateExpressions();
+ ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(fc.getCurrentDirectory());
}
}
});
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/DecalNotFoundDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/DecalNotFoundDialog.java
index e14c05a15..e726e0015 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/DecalNotFoundDialog.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/DecalNotFoundDialog.java
@@ -43,6 +43,7 @@ public abstract class DecalNotFoundDialog {
if (resultFileChooser == JFileChooser.APPROVE_OPTION) {
File file = chooser.getSelectedFile();
decex.getDecal().setDecalFile(file);
+ ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(chooser.getCurrentDirectory());
}
}
return (resultYesNo == JOptionPane.YES_OPTION) && (resultFileChooser == JFileChooser.APPROVE_OPTION);
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/EditDecalDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/EditDecalDialog.java
index 8ed3bf885..678e0edbc 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/EditDecalDialog.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/EditDecalDialog.java
@@ -20,6 +20,7 @@ import javax.swing.event.ChangeListener;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.gui.util.GUIUtil;
+import net.sf.openrocket.gui.util.SwingPreferences;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.gui.widgets.SelectColorButton;
@@ -75,6 +76,7 @@ public class EditDecalDialog extends JDialog {
int action = fc.showOpenDialog(owner);
if (action == JFileChooser.APPROVE_OPTION) {
commandText.setText(fc.getSelectedFile().getAbsolutePath());
+ ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(fc.getCurrentDirectory());
}
}
@@ -109,6 +111,7 @@ public class EditDecalDialog extends JDialog {
int action = fc.showOpenDialog(owner);
if (action == JFileChooser.APPROVE_OPTION) {
commandText.setText(fc.getSelectedFile().getAbsolutePath());
+ ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(fc.getCurrentDirectory());
}
}
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/ScaleDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/ScaleDialog.java
index 111f16378..d1a95308d 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/ScaleDialog.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/ScaleDialog.java
@@ -136,10 +136,8 @@ public class ScaleDialog extends JDialog {
SCALERS_NO_OFFSET.put(FreeformFinSet.class, list);
// MassObject
- list = new ArrayList<>(1);
- list.add(new MassObjectScaler());
- SCALERS_NO_OFFSET.put(MassObject.class, list);
addScaler(MassObject.class, "Radius", "isRadiusAutomatic", SCALERS_NO_OFFSET);
+ addScaler(MassObject.class, "Length", SCALERS_NO_OFFSET);
addScaler(MassObject.class, "RadialPosition", SCALERS_OFFSET);
// MassComponent
@@ -720,25 +718,6 @@ public class ScaleDialog extends JDialog {
}
}
-
- private static class MassObjectScaler implements Scaler {
- @Override
- public void scale(RocketComponent component, double multiplier, boolean scaleMass) {
- if (scaleMass) {
- MassObject c = (MassObject) component;
- if (c.isRadiusAutomatic()) {
- double volume = Math.PI * Math.pow(c.getRadiusNoAuto(), 2) * c.getLengthNoAuto();
- double scaledVolume = volume * MathUtil.pow3(multiplier);
- c.setRadius(c.getRadiusNoAuto() * multiplier);
- c.setLengthNoAuto(scaledVolume / (Math.PI * Math.pow(c.getRadiusNoAuto(), 2)));
- c.setRadiusAutomatic(true);
- } else {
- c.setLength(c.getLength() * multiplier);
- }
- }
- }
-
- }
private static class FreeformFinSetScaler implements Scaler {
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/MotorMountConfigurationPanel.java b/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/MotorMountConfigurationPanel.java
index 322794979..fc944e220 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/MotorMountConfigurationPanel.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/MotorMountConfigurationPanel.java
@@ -25,7 +25,7 @@ public class MotorMountConfigurationPanel extends JPanel {
table.setShowVerticalLines(false);
table.setRowSelectionAllowed(false);
table.setColumnSelectionAllowed(false);
- table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
+ table.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
table.setAutoCreateColumnsFromModel(false);
TableColumnModel columnModel = table.getColumnModel();
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preferences/GeneralPreferencesPanel.java b/swing/src/net/sf/openrocket/gui/dialogs/preferences/GeneralPreferencesPanel.java
index f4893800a..7cfba5735 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/preferences/GeneralPreferencesPanel.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/preferences/GeneralPreferencesPanel.java
@@ -19,6 +19,7 @@ import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
@@ -35,8 +36,11 @@ import net.sf.openrocket.gui.dialogs.UpdateInfoDialog;
import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.gui.util.SimpleFileFilter;
import net.sf.openrocket.gui.util.SwingPreferences;
+import net.sf.openrocket.gui.util.PreferencesExporter;
+import net.sf.openrocket.gui.util.PreferencesImporter;
import net.sf.openrocket.l10n.L10N;
import net.sf.openrocket.logging.Markers;
+import net.sf.openrocket.startup.Application;
import net.sf.openrocket.startup.Preferences;
import net.sf.openrocket.util.BuildProperties;
import net.sf.openrocket.util.Named;
@@ -46,7 +50,7 @@ import net.sf.openrocket.gui.widgets.SelectColorButton;
@SuppressWarnings("serial")
public class GeneralPreferencesPanel extends PreferencesPanel {
- public GeneralPreferencesPanel(JDialog parent) {
+ public GeneralPreferencesPanel(PreferencesDialog parent) {
super(parent, new MigLayout("fillx, ins 30lp n n n"));
@@ -251,21 +255,69 @@ public class GeneralPreferencesPanel extends PreferencesPanel {
});
this.add(rocksimWarningDialogBox,"spanx, wrap");
- //// Clear cached preferences
- final JButton clearCachedPreferences = new SelectColorButton(trans.get("pref.dlg.but.clearCachedPreferences"));
- clearCachedPreferences.setToolTipText(trans.get("pref.dlg.but.clearCachedPreferences.ttip"));
- clearCachedPreferences.addActionListener(new ActionListener() {
+ // Preference buttons
+ JPanel buttonPanel = new JPanel(new MigLayout("fillx, ins 0"));
+
+ //// Export preferences
+ final JButton exportPreferences = new SelectColorButton(trans.get("pref.dlg.but.exportPreferences"));
+ exportPreferences.setToolTipText(trans.get("pref.dlg.but.exportPreferences.ttip"));
+ exportPreferences.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ PreferencesExporter.exportPreferences(parent, preferences.getPreferences());
+ }
+ });
+ buttonPanel.add(exportPreferences);
+
+ //// Import preferences
+ final JButton importPreferences = new SelectColorButton(trans.get("pref.dlg.but.importPreferences"));
+ importPreferences.setToolTipText(trans.get("pref.dlg.but.importPreferences.ttip"));
+ importPreferences.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ boolean imported = PreferencesImporter.importPreferences(parent);
+ if (imported) {
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ JOptionPane.showMessageDialog(parent,
+ trans.get("generalprefs.ImportWarning.msg"),
+ trans.get("generalprefs.ImportWarning.title"),
+ JOptionPane.WARNING_MESSAGE);
+ PreferencesDialog.showPreferences(parent.getParentFrame()); // Refresh the preferences dialog
+ }
+ });
+ }
+ }
+ });
+ buttonPanel.add(importPreferences);
+
+ //// Reset all preferences
+ final JButton resetAllPreferences = new SelectColorButton(trans.get("pref.dlg.but.resetAllPreferences"));
+ resetAllPreferences.setToolTipText(trans.get("pref.dlg.but.resetAllPreferences.ttip"));
+ resetAllPreferences.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int resultYesNo = JOptionPane.showConfirmDialog(parent, trans.get("pref.dlg.clearCachedPreferences.message"),
trans.get("pref.dlg.clearCachedPreferences.title"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
if (resultYesNo == JOptionPane.YES_OPTION) {
preferences.clearPreferences();
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ JOptionPane.showMessageDialog(parent,
+ trans.get("generalprefs.ImportWarning.msg"),
+ trans.get("generalprefs.ImportWarning.title"),
+ JOptionPane.WARNING_MESSAGE);
+ PreferencesDialog.showPreferences(parent.getParentFrame()); // Refresh the preferences dialog
+ }
+ });
}
}
});
- this.add(clearCachedPreferences, "spanx, pushy, bottom, wrap");
+ buttonPanel.add(resetAllPreferences, "pushx, right, wrap");
+ this.add(buttonPanel, "spanx, growx, pushy, bottom, wrap");
}
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preferences/GraphicsPreferencesPanel.java b/swing/src/net/sf/openrocket/gui/dialogs/preferences/GraphicsPreferencesPanel.java
index b0b241b9b..3f16a27d3 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/preferences/GraphicsPreferencesPanel.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/preferences/GraphicsPreferencesPanel.java
@@ -29,6 +29,8 @@ import net.sf.openrocket.gui.adaptors.BooleanModel;
import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.gui.components.StyledLabel.Style;
import net.sf.openrocket.gui.util.GUIUtil;
+import net.sf.openrocket.gui.util.SwingPreferences;
+import net.sf.openrocket.startup.Application;
import net.sf.openrocket.startup.Preferences;
import net.sf.openrocket.gui.widgets.SelectColorButton;
@@ -119,6 +121,7 @@ public class GraphicsPreferencesPanel extends PreferencesPanel {
String commandLine = fc.getSelectedFile().getAbsolutePath();
commandText.setText(commandLine);
preferences.setDecalEditorPreference(false, commandLine);
+ ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(fc.getCurrentDirectory());
}
}
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preferences/PreferencesDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/preferences/PreferencesDialog.java
index 54de016bb..4dadc0f14 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/preferences/PreferencesDialog.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/preferences/PreferencesDialog.java
@@ -33,11 +33,15 @@ public class PreferencesDialog extends JDialog {
private final SwingPreferences preferences = (SwingPreferences) Application
.getPreferences();
+ private BasicFrame parentFrame;
+
private PreferencesDialog(BasicFrame parent) {
// // Preferences
super(parent, trans.get("pref.dlg.title.Preferences"),
Dialog.ModalityType.APPLICATION_MODAL);
+ this.parentFrame = parent;
+
JPanel panel = new JPanel(new MigLayout("fill, gap unrel", "[grow]",
"[grow][]"));
@@ -105,6 +109,10 @@ public class PreferencesDialog extends JDialog {
GUIUtil.setDisposableDialogOptions(this, close);
}
+ public BasicFrame getParentFrame() {
+ return parentFrame;
+ }
+
// ////// Singleton implementation ////////
private static PreferencesDialog dialog = null;
diff --git a/swing/src/net/sf/openrocket/gui/main/ExportDecalDialog.java b/swing/src/net/sf/openrocket/gui/main/ExportDecalDialog.java
index 18e3c4ba1..932547cc5 100644
--- a/swing/src/net/sf/openrocket/gui/main/ExportDecalDialog.java
+++ b/swing/src/net/sf/openrocket/gui/main/ExportDecalDialog.java
@@ -90,6 +90,7 @@ public class ExportDecalDialog extends JDialog {
export(selectedDecal, selectedFile);
// If the user doesn't confirm over write, then leave this dialog open.
ExportDecalDialog.this.dispose();
+ ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(chooser.getCurrentDirectory());
}
}
}
diff --git a/swing/src/net/sf/openrocket/gui/main/OpenRocketClipboard.java b/swing/src/net/sf/openrocket/gui/main/OpenRocketClipboard.java
index cda0a7e85..7302d8b43 100644
--- a/swing/src/net/sf/openrocket/gui/main/OpenRocketClipboard.java
+++ b/swing/src/net/sf/openrocket/gui/main/OpenRocketClipboard.java
@@ -101,7 +101,7 @@ public final class OpenRocketClipboard {
if (someChildrenSelected) {
for (RocketComponent child : component.getChildren()) {
if (!clipboardComponents.contains(child)) {
- component.removeChild(child);
+ component.removeChild(child, false);
} else {
clipboardComponents.remove(child);
filterClipboardComponents(child.getChildren());
diff --git a/swing/src/net/sf/openrocket/gui/main/RocketActions.java b/swing/src/net/sf/openrocket/gui/main/RocketActions.java
index 200244723..2bfa6e9a6 100644
--- a/swing/src/net/sf/openrocket/gui/main/RocketActions.java
+++ b/swing/src/net/sf/openrocket/gui/main/RocketActions.java
@@ -337,7 +337,7 @@ public class RocketActions {
RocketComponent originalParent = components.get(i).getParent();
int originalParentIdx = components.indexOf(originalParent);
- result.get(originalParentIdx).addChild(result.get(i));
+ result.get(originalParentIdx).addChild(result.get(i), false);
} else if (RocketComponent.listContainsParent(components, components.get(i))){
RocketComponent originalParent = components.get(i);
while (originalParent != components.get(i)) {
@@ -346,7 +346,7 @@ public class RocketActions {
}
}
int originalParentIdx = components.indexOf(originalParent);
- result.get(originalParentIdx).addChild(result.get(i));
+ result.get(originalParentIdx).addChild(result.get(i), false);
}
}
diff --git a/swing/src/net/sf/openrocket/gui/preset/PresetEditorDialog.java b/swing/src/net/sf/openrocket/gui/preset/PresetEditorDialog.java
index 3818ebe17..b10331a21 100644
--- a/swing/src/net/sf/openrocket/gui/preset/PresetEditorDialog.java
+++ b/swing/src/net/sf/openrocket/gui/preset/PresetEditorDialog.java
@@ -44,6 +44,7 @@ import javax.swing.border.EmptyBorder;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.text.JTextComponent;
+import net.sf.openrocket.gui.util.SwingPreferences;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -389,6 +390,7 @@ public class PresetEditorDialog extends JDialog implements ItemListener {
File file = imageChooser.getSelectedFile();
ncImage = scaleImage(new ImageIcon(file.getAbsolutePath()).getImage(), 155);
ncImageBtn.setIcon(ncImage);
+ ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(imageChooser.getCurrentDirectory());
}
}
});
@@ -1318,6 +1320,7 @@ public class PresetEditorDialog extends JDialog implements ItemListener {
*/
private JFileChooser createImageChooser() {
final JFileChooser chooser = new JFileChooser();
+ chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory());
ImagePreviewPanel preview = new ImagePreviewPanel();
chooser.setAccessory(preview);
chooser.addPropertyChangeListener(preview);
diff --git a/swing/src/net/sf/openrocket/gui/util/DummyFrameMenuOSX.java b/swing/src/net/sf/openrocket/gui/util/DummyFrameMenuOSX.java
index 9a4a84a33..4b9f1537b 100644
--- a/swing/src/net/sf/openrocket/gui/util/DummyFrameMenuOSX.java
+++ b/swing/src/net/sf/openrocket/gui/util/DummyFrameMenuOSX.java
@@ -1,11 +1,6 @@
package net.sf.openrocket.gui.util;
-import net.sf.openrocket.gui.dialogs.AboutDialog;
-import net.sf.openrocket.gui.dialogs.LicenseDialog;
-import net.sf.openrocket.gui.help.tours.GuidedTourSelectionDialog;
import net.sf.openrocket.gui.main.BasicFrame;
-import net.sf.openrocket.gui.main.ExampleDesignFileAction;
-import net.sf.openrocket.gui.main.MRUDesignFileAction;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.logging.Markers;
import net.sf.openrocket.startup.Application;
diff --git a/swing/src/net/sf/openrocket/gui/util/FileHelper.java b/swing/src/net/sf/openrocket/gui/util/FileHelper.java
index 9ea309450..c2bc914d6 100644
--- a/swing/src/net/sf/openrocket/gui/util/FileHelper.java
+++ b/swing/src/net/sf/openrocket/gui/util/FileHelper.java
@@ -64,6 +64,10 @@ public final class FileHelper {
public static final FileFilter PNG_FILTER =
new SimpleFileFilter(trans.get("FileHelper.PNG_FILTER"), ".png");
+ /** File filter for XML files (*.xml) */
+ public static final FileFilter XML_FILTER =
+ new SimpleFileFilter(trans.get("FileHelper.XML_FILTER"), ".xml");
+
diff --git a/swing/src/net/sf/openrocket/gui/util/PreferencesExporter.java b/swing/src/net/sf/openrocket/gui/util/PreferencesExporter.java
new file mode 100644
index 000000000..ab40b7114
--- /dev/null
+++ b/swing/src/net/sf/openrocket/gui/util/PreferencesExporter.java
@@ -0,0 +1,202 @@
+package net.sf.openrocket.gui.util;
+
+import net.sf.openrocket.arch.SystemInfo;
+import net.sf.openrocket.gui.components.PreferencesOptionPanel;
+import net.sf.openrocket.gui.main.MRUDesignFile;
+import net.sf.openrocket.gui.widgets.SaveFileChooser;
+import net.sf.openrocket.l10n.Translator;
+import net.sf.openrocket.startup.Application;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import javax.swing.JFileChooser;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import java.awt.Dimension;
+import java.awt.Window;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.prefs.BackingStoreException;
+import java.util.prefs.Preferences;
+
+public abstract class PreferencesExporter {
+ private static final Translator trans = Application.getTranslator();
+ private static final Logger log = LoggerFactory.getLogger(PreferencesExporter.class);
+ private static final net.sf.openrocket.startup.Preferences prefs = Application.getPreferences();
+
+ private static final List keysToIgnore = new ArrayList<>(); // Preference keys to ignore when exporting user directories (= keys that export user directories)
+ private static final List prefixKeysToIgnore = new ArrayList<>(); // Preference keys to ignore when exporting user directories (= keys that start with these prefixes), e.g.
+ private static final List nodesToIgnore = new ArrayList<>(); // Preferences nodes that should not be exported
+
+ public static boolean exportPreferences(Window parent, Preferences preferences) {
+ JFileChooser chooser = new SaveFileChooser();
+ chooser.setDialogTitle(trans.get("PreferencesExporter.chooser.title"));
+ chooser.setAcceptAllFileFilterUsed(false);
+ chooser.setFileFilter(FileHelper.XML_FILTER);
+ chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory());
+ PreferencesOptionPanel options = new PreferencesOptionPanel();
+ chooser.setAccessory(options);
+
+ // TODO: update this dynamically instead of hard-coded values
+ // The macOS file chooser has an issue where it does not update its size when the accessory is added.
+ if (SystemInfo.getPlatform() == SystemInfo.Platform.MAC_OS) {
+ Dimension currentSize = chooser.getPreferredSize();
+ Dimension newSize = new Dimension((int) (1.35 * currentSize.width), (int) (1.2 * currentSize.height));
+ chooser.setPreferredSize(newSize);
+ }
+
+ //// Ensures No Problems When Choosing File
+ if (chooser.showSaveDialog(parent) != JFileChooser.APPROVE_OPTION) {
+ log.info("Cancelled export of preferences.");
+ return false;
+ }
+
+ ((SwingPreferences) prefs).setDefaultDirectory(chooser.getCurrentDirectory());
+
+ File file = chooser.getSelectedFile();
+ if (file == null) {
+ log.info("No file selected to export preferences to.");
+ return false;
+ }
+
+ final File newFile = FileHelper.forceExtension(file, "xml");
+ if (!FileHelper.confirmWrite(newFile, parent)) {
+ log.info("Cancelled export of preferences.");
+ return false;
+ }
+
+ // Decide which keys/nodes to ignore
+ boolean ignoreUserDirectories = options.isIgnoreUserDirectories();
+ boolean ignoreWindowInformation = options.isIgnoreWindowInformation();
+ fillIgnoreKeys(ignoreUserDirectories, ignoreWindowInformation);
+
+ try (FileOutputStream fos = new FileOutputStream(newFile)) {
+ if (keysToIgnore.isEmpty() && nodesToIgnore.isEmpty() && prefixKeysToIgnore.isEmpty()) {
+ // Export all preferences
+ preferences.exportSubtree(fos);
+ } else {
+ // Export all preferences except user directories
+ exportFilteredPreferences(preferences, fos);
+ }
+ log.info("Preferences exported successfully.");
+ } catch (IOException | BackingStoreException e) {
+ log.warn("Error while importing preferences: " + e.getMessage());
+ }
+
+ return true;
+ }
+
+ private static void fillIgnoreKeys(boolean ignoreUserDirectories, boolean ignoreWindowInformation) {
+ keysToIgnore.clear();
+ prefixKeysToIgnore.clear();
+ nodesToIgnore.clear();
+
+ if (ignoreUserDirectories) {
+ keysToIgnore.add(net.sf.openrocket.startup.Preferences.USER_THRUST_CURVES_KEY);
+ keysToIgnore.add(net.sf.openrocket.startup.Preferences.DEFAULT_DIRECTORY);
+ prefixKeysToIgnore.add(MRUDesignFile.MRU_FILE_LIST_PROPERTY);
+ }
+
+ if (ignoreWindowInformation) {
+ nodesToIgnore.add(SwingPreferences.NODE_WINDOWS);
+ nodesToIgnore.add(SwingPreferences.NODE_TABLES);
+ }
+
+ keysToIgnore.add(SwingPreferences.UPDATE_PLATFORM); // Don't export platform-specific settings
+ }
+
+ private static void exportFilteredPreferences(Preferences preferences, FileOutputStream fos) throws BackingStoreException, IOException {
+ // Filter out user directories
+ Preferences root = Preferences.userRoot();
+ String originalNodeName = ((SwingPreferences) prefs).getNodename();
+ String nodeName = originalNodeName + "-temp";
+ if (root.nodeExists(nodeName)) {
+ root.node(nodeName).removeNode();
+ }
+ Preferences tempPrefs = root.node(nodeName);
+
+ // Fill in all parameters to the temporary preferences, except for user directories
+ copyFilteredPreferences(preferences, tempPrefs, nodesToIgnore, keysToIgnore, prefixKeysToIgnore);
+
+ // Export the filtered preferences
+ try {
+ // Export the filtered preferences to a temporary file
+ Path tempFile = Files.createTempFile("ORprefs_" + System.currentTimeMillis(), ".xml");
+ try (FileOutputStream tempFos = new FileOutputStream(tempFile.toFile())) {
+ tempPrefs.exportSubtree(tempFos);
+ }
+
+ // Read and parse the temporary file
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ Document doc;
+ try (FileInputStream tempFis = new FileInputStream(tempFile.toFile())) {
+ doc = factory.newDocumentBuilder().parse(tempFis);
+ }
+
+ // Find and rename the node
+ NodeList nodeList = doc.getElementsByTagName("node");
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ Element element = (Element) nodeList.item(i);
+ if (element.getAttribute("name").equals(nodeName)) {
+ element.setAttribute("name", ((SwingPreferences) prefs).getNodename());
+ break;
+ }
+ }
+
+ // Create a transformer to write the XML document to the FileOutputStream
+ Transformer transformer = TransformerFactory.newInstance().newTransformer();
+
+ // Set output properties to include the correct DOCTYPE declaration
+ transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "http://java.sun.com/dtd/preferences.dtd");
+ transformer.setOutputProperty(OutputKeys.STANDALONE, "no");
+
+ // Write the XML document to the FileOutputStream
+ DOMSource source = new DOMSource(doc);
+ StreamResult result = new StreamResult(fos);
+ transformer.transform(source, result);
+
+ // Clean up the temporary file
+ Files.deleteIfExists(tempFile);
+ } catch (ParserConfigurationException | TransformerException | SAXException e) {
+ e.printStackTrace();
+ } finally {
+ root.node(nodeName).removeNode();
+ }
+ }
+
+ private static void copyFilteredPreferences(Preferences src, Preferences dest,
+ List nodesToIgnore, List keysToIgnore, List prefixKeysToIgnore) throws BackingStoreException {
+ for (String key : src.keys()) {
+ if (keysToIgnore.contains(key)
+ || prefixKeysToIgnore.stream().anyMatch(key::startsWith)) {
+ continue;
+ }
+ dest.put(key, src.get(key, null));
+ }
+
+ for (String childNodeName : src.childrenNames()) {
+ if (nodesToIgnore.contains(childNodeName)) {
+ continue;
+ }
+ Preferences srcChild = src.node(childNodeName);
+ Preferences destChild = dest.node(childNodeName);
+ copyFilteredPreferences(srcChild, destChild, nodesToIgnore, keysToIgnore, prefixKeysToIgnore);
+ }
+ }
+}
diff --git a/swing/src/net/sf/openrocket/gui/util/PreferencesImporter.java b/swing/src/net/sf/openrocket/gui/util/PreferencesImporter.java
new file mode 100644
index 000000000..32a4044db
--- /dev/null
+++ b/swing/src/net/sf/openrocket/gui/util/PreferencesImporter.java
@@ -0,0 +1,45 @@
+package net.sf.openrocket.gui.util;
+
+import net.sf.openrocket.l10n.Translator;
+import net.sf.openrocket.startup.Application;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.swing.JFileChooser;
+import java.awt.Window;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.prefs.InvalidPreferencesFormatException;
+import java.util.prefs.Preferences;
+
+public abstract class PreferencesImporter {
+ private static final Translator trans = Application.getTranslator();
+ private static final Logger log = LoggerFactory.getLogger(PreferencesImporter.class);
+
+ public static boolean importPreferences(Window parent) {
+ final JFileChooser chooser = new JFileChooser();
+ chooser.setDialogTitle(trans.get("PreferencesImporter.chooser.title"));
+ chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory());
+ chooser.setFileFilter(FileHelper.XML_FILTER);
+ chooser.setAcceptAllFileFilterUsed(false);
+
+ int returnVal = chooser.showOpenDialog(parent);
+ if (returnVal != JFileChooser.APPROVE_OPTION) {
+ log.info("Cancelled import of preferences.");
+ return false;
+ }
+
+ ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(chooser.getCurrentDirectory());
+
+ File importFile = chooser.getSelectedFile();
+ try (FileInputStream fis = new FileInputStream(importFile)) {
+ Preferences.importPreferences(fis);
+ log.info("Preferences imported successfully.");
+ } catch (IOException | InvalidPreferencesFormatException e) {
+ log.warn("Error while importing preferences: " + e.getMessage());
+ }
+
+ return true;
+ }
+}
diff --git a/swing/src/net/sf/openrocket/gui/util/SwingPreferences.java b/swing/src/net/sf/openrocket/gui/util/SwingPreferences.java
index bf30f6c39..51be68159 100644
--- a/swing/src/net/sf/openrocket/gui/util/SwingPreferences.java
+++ b/swing/src/net/sf/openrocket/gui/util/SwingPreferences.java
@@ -2,7 +2,11 @@ package net.sf.openrocket.gui.util;
import java.awt.Color;
import java.awt.Dimension;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
import java.awt.Point;
+import java.awt.Rectangle;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
@@ -38,7 +42,11 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
private static final Logger log = LoggerFactory.getLogger(SwingPreferences.class);
private static final String SPLIT_CHARACTER = "|";
-
+
+
+ public static final String NODE_WINDOWS = "windows";
+ public static final String NODE_TABLES = "tables";
+ public static final String UPDATE_PLATFORM = "UpdatePlatform";
private static final List SUPPORTED_LOCALES;
static {
@@ -88,10 +96,16 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
PREFNODE = root.node(NODENAME);
}
-
+ public String getNodename() {
+ return NODENAME;
+ }
//////////////////////
+
+ public Preferences getPreferences() {
+ return PREFNODE;
+ }
public void clearPreferences() {
try {
@@ -114,7 +128,22 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
private void storeVersion() {
PREFNODE.put("OpenRocketVersion", BuildProperties.getVersion());
}
-
+
+ /**
+ * Checks if a certain key exists in the node
+ * @param node node to check the keys of.
+ * @param key key to check
+ * @return true if the key is stored in the preferences, false otherwise
+ */
+ private boolean keyExists(Preferences node, String key) {
+ try {
+ return Arrays.asList(node.keys()).contains(key);
+ } catch (BackingStoreException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
/**
* Return a string preference.
*
@@ -124,12 +153,28 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
*/
@Override
public String getString(String key, String def) {
+ if (!keyExists(PREFNODE, key) && key != null && def != null) {
+ PREFNODE.put(key, def);
+ try {
+ PREFNODE.flush();
+ } catch (BackingStoreException e) {
+ e.printStackTrace();
+ }
+ }
return PREFNODE.get(key, def);
}
@Override
public String getString(String directory, String key, String defaultValue) {
Preferences p = PREFNODE.node(directory);
+ if (!keyExists(p, key) && key != null && defaultValue != null) {
+ p.put(key, defaultValue);
+ try {
+ p.flush();
+ } catch (BackingStoreException e) {
+ e.printStackTrace();
+ }
+ }
return p.get(key, defaultValue);
}
@@ -169,6 +214,16 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
*/
@Override
public boolean getBoolean(String key, boolean def) {
+ // Check if the key exists
+ if (!keyExists(PREFNODE, key) && key != null) {
+ // Save the default value
+ PREFNODE.putBoolean(key, def);
+ try {
+ PREFNODE.flush();
+ } catch (BackingStoreException e) {
+ e.printStackTrace();
+ }
+ }
return PREFNODE.getBoolean(key, def);
}
@@ -183,9 +238,17 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
PREFNODE.putBoolean(key, value);
storeVersion();
}
-
+
@Override
public int getInt(String key, int defaultValue) {
+ if (!keyExists(PREFNODE, key) && key != null) {
+ PREFNODE.putInt(key, defaultValue);
+ try {
+ PREFNODE.flush();
+ } catch (BackingStoreException e) {
+ e.printStackTrace();
+ }
+ }
return PREFNODE.getInt(key, defaultValue);
}
@@ -194,9 +257,17 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
PREFNODE.putInt(key, value);
storeVersion();
}
-
+
@Override
public double getDouble(String key, double defaultValue) {
+ if (!keyExists(PREFNODE, key) && key != null) {
+ PREFNODE.putDouble(key, defaultValue);
+ try {
+ PREFNODE.flush();
+ } catch (BackingStoreException e) {
+ e.printStackTrace();
+ }
+ }
return PREFNODE.getDouble(key, defaultValue);
}
@@ -228,7 +299,7 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
}
public File getDefaultDirectory() {
- String file = getString("defaultDirectory", null);
+ String file = getString(net.sf.openrocket.startup.Preferences.DEFAULT_DIRECTORY, null);
if (file == null)
return null;
return new File(file);
@@ -241,7 +312,7 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
} else {
d = dir.getAbsolutePath();
}
- putString("defaultDirectory", d);
+ putString(net.sf.openrocket.startup.Preferences.DEFAULT_DIRECTORY, d);
storeVersion();
}
@@ -262,13 +333,22 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
return compdir;
}
+ /**
+ * Set the operating system that the software updater will use to redirect you to an installer download link.
+ * @param platform the operating system to use
+ */
public void setUpdatePlatform(UpdatePlatform platform) {
if (platform == null) return;
- putString("UpdatePlatform", platform.name());
+ putString(UPDATE_PLATFORM, platform.name());
}
+ /**
+ * Get the operating system that will be selected when asking for a software update.
+ * E.g. "Windows" will cause the software updater to default to letting you download a Windows installer.
+ * @return the operating system that is used
+ */
public UpdatePlatform getUpdatePlatform() {
- String p = getString("UpdatePlatform", SystemInfo.getPlatform().name());
+ String p = getString(UPDATE_PLATFORM, SystemInfo.getPlatform().name());
if (p == null) return null;
return UpdatePlatform.valueOf(p);
}
@@ -372,7 +452,7 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
public Point getWindowPosition(Class> c) {
int x, y;
- String pref = PREFNODE.node("windows").get("position." + c.getCanonicalName(), null);
+ String pref = PREFNODE.node(NODE_WINDOWS).get("position." + c.getCanonicalName(), null);
if (pref == null)
return null;
@@ -386,20 +466,46 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
} catch (NumberFormatException e) {
return null;
}
- return new Point(x, y);
+
+ // If position was on a screen that is not available anymore
+ Point p = new Point(x, y);
+ if (!isPointOnScreen(p)) {
+ return null;
+ }
+
+ return p;
}
public void setWindowPosition(Class> c, Point p) {
- PREFNODE.node("windows").put("position." + c.getCanonicalName(), "" + p.x + "," + p.y);
+ PREFNODE.node(NODE_WINDOWS).put("position." + c.getCanonicalName(), "" + p.x + "," + p.y);
storeVersion();
}
-
-
+
+ /**
+ * Checks whether the point is present on any of the current monitor screens.
+ * Can return false if point was e.g. referenced on a secondary monitor that doesn't exist anymore.
+ * @param p point to check
+ * @return true if point is present on any of the current screens, false otherwise
+ */
+ private boolean isPointOnScreen(Point p) {
+ GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+ GraphicsDevice[] screens = ge.getScreenDevices();
+
+ for (GraphicsDevice screen : screens) {
+ GraphicsConfiguration gc = screen.getDefaultConfiguration();
+ Rectangle bounds = gc.getBounds();
+ if (bounds.contains(p)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public Dimension getWindowSize(Class> c) {
int x, y;
- String pref = PREFNODE.node("windows").get("size." + c.getCanonicalName(), null);
+ String pref = PREFNODE.node(NODE_WINDOWS).get("size." + c.getCanonicalName(), null);
if (pref == null)
return null;
@@ -418,22 +524,22 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
public boolean isWindowMaximized(Class> c) {
- String pref = PREFNODE.node("windows").get("size." + c.getCanonicalName(), null);
+ String pref = PREFNODE.node(NODE_WINDOWS).get("size." + c.getCanonicalName(), null);
return "max".equals(pref);
}
public void setWindowSize(Class> c, Dimension d) {
- PREFNODE.node("windows").put("size." + c.getCanonicalName(), "" + d.width + "," + d.height);
+ PREFNODE.node(NODE_WINDOWS).put("size." + c.getCanonicalName(), "" + d.width + "," + d.height);
storeVersion();
}
public void setWindowMaximized(Class> c) {
- PREFNODE.node("windows").put("size." + c.getCanonicalName(), "max");
+ PREFNODE.node(NODE_WINDOWS).put("size." + c.getCanonicalName(), "max");
storeVersion();
}
public Integer getTableColumnWidth(String keyName, int columnIdx) {
- String pref = PREFNODE.node("tables").get(
+ String pref = PREFNODE.node(NODE_TABLES).get(
"cw." + keyName + "." + columnIdx, null);
if (pref == null)
return null;
@@ -451,7 +557,7 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
}
public void setTableColumnWidth(String keyName, int columnIdx, Integer width) {
- PREFNODE.node("tables").put(
+ PREFNODE.node(NODE_TABLES).put(
"cw." + keyName + "." + columnIdx, width.toString());
storeVersion();
}
diff --git a/swing/src/net/sf/openrocket/utils/ComponentPresetEditor.java b/swing/src/net/sf/openrocket/utils/ComponentPresetEditor.java
index a70cfc878..a1baf53f3 100644
--- a/swing/src/net/sf/openrocket/utils/ComponentPresetEditor.java
+++ b/swing/src/net/sf/openrocket/utils/ComponentPresetEditor.java
@@ -342,6 +342,8 @@ public class ComponentPresetEditor extends JPanel implements PresetResultListene
log.info(Markers.USER_MARKER, "User decided not to open, option=" + option);
return false;
}
+
+ ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(chooser.getCurrentDirectory());
File file = chooser.getSelectedFile();
try {