extensionList = Arrays.asList(EXTENSIONS);
+ names = Collections.unmodifiableList(nameList);
+ mimeTypes = Collections.unmodifiableList(mimeTypeList);
+ extensions = Collections.unmodifiableList(extensionList);
+ }
+}
diff --git a/core/src/net/sf/openrocket/scripting/ScriptEngineManagerRedux.java b/core/src/net/sf/openrocket/scripting/ScriptEngineManagerRedux.java
new file mode 100644
index 000000000..42566c8a1
--- /dev/null
+++ b/core/src/net/sf/openrocket/scripting/ScriptEngineManagerRedux.java
@@ -0,0 +1,300 @@
+/*
+ * This is a replacement for the ScriptEngineManager which gets around and issue with the sun.misc.ServiceConfigurationError
+ * which has been removed in Java 9+. If using the ScriptEngineManager from the script-api*.jar then the sun.misc throws
+ * a ClassNotFoundException.
+ */
+
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package net.sf.openrocket.scripting;
+
+import javax.script.*;
+import java.util.*;
+import java.security.*;
+import java.util.ServiceLoader;
+import java.util.ServiceConfigurationError;
+
+/**
+ * The ScriptEngineManager implements a discovery and instantiation
+ * mechanism for ScriptEngine classes and also maintains a
+ * collection of key/value pairs storing state shared by all engines created
+ * by the Manager. This class uses the service provider mechanism described in the
+ * {@link java.util.ServiceLoader} class to enumerate all the
+ * implementations of ScriptEngineFactory.
+ * The ScriptEngineManager provides a method to return a list of all these factories
+ * as well as utility methods which look up factories on the basis of language name, file extension
+ * and mime type.
+ *
+ * The Bindings of key/value pairs, referred to as the "Global Scope" maintained
+ * by the manager is available to all instances of ScriptEngine created
+ * by the ScriptEngineManager. The values in the Bindings are
+ * generally exposed in all scripts.
+ *
+ * @author Mike Grogan
+ * @author A. Sundararajan
+ * @since 1.6
+ */
+public class ScriptEngineManagerRedux {
+ private static final boolean DEBUG = false;
+ /**
+ * The effect of calling this constructor is the same as calling
+ * ScriptEngineManager(Thread.currentThread().getContextClassLoader()).
+ *
+ * @see java.lang.Thread#getContextClassLoader
+ */
+ public ScriptEngineManagerRedux() {
+ init(Thread.currentThread().getContextClassLoader());
+ }
+
+ /**
+ * This constructor loads the implementations of
+ * ScriptEngineFactory visible to the given
+ * ClassLoader using the service provider mechanism.
+ * If loader is null, the script engine factories that are
+ * bundled with the platform are loaded.
+ *
+ * @param loader ClassLoader used to discover script engine factories.
+ */
+ public ScriptEngineManagerRedux(ClassLoader loader) {
+ init(loader);
+ }
+
+ /**
+ * Gets the value for the specified key in the Global Scope
+ * @param key The key whose value is to be returned.
+ * @return The value for the specified key.
+ */
+ public Object get(String key) {
+ return _globalScope.get(key);
+ }
+
+ /**
+ * getBindings returns the value of the globalScope field.
+ * ScriptEngineManager sets this Bindings as global bindings for
+ * ScriptEngine objects created by it.
+ *
+ * @return The globalScope field.
+ */
+ public Bindings getBindings() {
+ return _globalScope;
+ }
+
+ /**
+ * Looks up and creates a ScriptEngine for a given name.
+ * The algorithm first searches for a ScriptEngineFactory that has been
+ * registered as a handler for the specified name using the registerEngineName
+ * method.
+ *
If one is not found, it searches the set of ScriptEngineFactory instances
+ * stored by the constructor for one with the specified name. If a ScriptEngineFactory
+ * is found by either method, it is used to create instance of ScriptEngine.
+ * @param shortName The short name of the ScriptEngine implementation.
+ * returned by the getNames method of its ScriptEngineFactory.
+ * @return A ScriptEngine created by the factory located in the search. Returns null
+ * if no such factory was found. The ScriptEngineManager sets its own globalScope
+ * Bindings as the GLOBAL_SCOPE Bindings of the newly
+ * created ScriptEngine.
+ * @throws NullPointerException if shortName is null.
+ */
+ private Map _factoriesByName = new HashMap<>();
+ public synchronized ScriptEngine getEngineByName(String shortName) {
+ if (shortName == null) {
+ throw new NullPointerException();
+ }
+
+ String key = shortName.toLowerCase();
+ if (_factoriesByName.containsKey(key)) {
+ return getEngineByFactory(_factoriesByName.get(key));
+ }
+
+ // Look for registered name first
+ ScriptEngineFactory factoryNamed;
+ if (null != (factoryNamed = _nameAssociations.get(key))) {
+ try {
+ _factoriesByName.put(key, factoryNamed);
+ return getEngineByFactory(factoryNamed);
+ } catch (Exception exp) {
+ if (DEBUG) {
+ exp.printStackTrace();
+ }
+ }
+ }
+
+ Optional factoryName;
+ List names;
+ for (ScriptEngineFactory factory : _scriptEngineFactories) {
+ try {
+ factoryName = factory.getNames().stream().filter(l -> l.equalsIgnoreCase(shortName)).findFirst();
+ if (factoryName.isPresent()) {
+ _factoriesByName.put(key, factory);
+ return getEngineByFactory(factory);
+ }
+ } catch (Exception exp) {
+ if (DEBUG) {
+ exp.printStackTrace();
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns a list whose elements are instances of all the ScriptEngineFactory classes
+ * found by the discovery mechanism.
+ * @return List of all discovered ScriptEngineFactorys.
+ */
+ public synchronized List getEngineFactories() {
+ return List.copyOf(_scriptEngineFactories);
+ }
+
+ /**
+ * Sets the specified key/value pair in the Global Scope.
+ * @param key Key to set
+ * @param value Value to set.
+ * @throws NullPointerException if key is null.
+ * @throws IllegalArgumentException if key is empty string.
+ */
+ public void put(String key, Object value) {
+ _globalScope.put(key, value);
+ }
+
+ /**
+ * Registers a ScriptEngineFactory to handle a language
+ * name. Overrides any such association found using the Discovery mechanism.
+ * @param name The name to be associated with the ScriptEngineFactory.
+ * @param factory The class to associate with the given name.
+ * @throws NullPointerException if any of the parameters is null.
+ */
+ public void registerEngineName(String name, ScriptEngineFactory factory) {
+ if (name == null || factory == null) {
+ throw new NullPointerException();
+ }
+
+ _nameAssociations.put(name.toLowerCase(), factory);
+ }
+
+ /**
+ * setBindings stores the specified Bindings
+ * in the globalScope field. ScriptEngineManager sets this
+ * Bindings as global bindings for ScriptEngine
+ * objects created by it.
+ *
+ * @param bindings The specified Bindings
+ * @throws IllegalArgumentException if bindings is null.
+ */
+ public void setBindings(Bindings bindings) {
+ if (bindings == null) {
+ throw new IllegalArgumentException("Global scope cannot be null.");
+ }
+
+ _globalScope = bindings;
+ }
+
+ private ScriptEngine getEngineByFactory(ScriptEngineFactory factory) {
+ if (factory == null) {
+ return null;
+ }
+
+ ScriptEngine engine = factory.getScriptEngine();
+ engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE);
+ return engine;
+ }
+
+ private ServiceLoader getServiceLoader(final ClassLoader loader) {
+ if (loader != null) {
+ return ServiceLoader.load(ScriptEngineFactory.class, loader);
+ } else {
+ return ServiceLoader.loadInstalled(ScriptEngineFactory.class);
+ }
+ }
+
+ private void init(final ClassLoader loader) {
+ _scriptEngineFactories = new TreeSet<>(Comparator.comparing(
+ ScriptEngineFactory::getEngineName,
+ Comparator.nullsLast(Comparator.naturalOrder()))
+ );
+ initEngines(loader);
+ }
+
+ private void initEngines(final ClassLoader loader) {
+ Iterator itr;
+ try {
+ ServiceLoader loaders = AccessController.doPrivileged(
+ new PrivilegedAction>() {
+ @Override
+ public ServiceLoader run() {
+ return getServiceLoader(loader);
+ }
+ });
+
+ itr = loaders.iterator();
+ } catch (ServiceConfigurationError err) {
+ // } catch (Exception err) {
+ System.err.println("Can't find ScriptEngineFactory providers: " + err.getMessage());
+ if (DEBUG) {
+ err.printStackTrace();
+ }
+ // do not throw any exception here. user may want to
+ // manage his/her own factories using this manager
+ // by explicit registration (by registerXXX) methods.
+ return;
+ }
+
+ try {
+ while (itr.hasNext()) {
+ try {
+ ScriptEngineFactory factory = itr.next();
+ _scriptEngineFactories.add(factory);
+ } catch (ServiceConfigurationError err) {
+ // } catch (Exception err) {
+ System.err.println("ScriptEngineManager providers.next(): " + err.getMessage());
+ if (DEBUG) {
+ err.printStackTrace();
+ }
+ // one factory failed, but check other factories...
+ }
+ }
+ } catch (ServiceConfigurationError err) {
+ // } catch (Exception err) {
+ System.err.println("ScriptEngineManager providers.hasNext(): " + err.getMessage());
+ if (DEBUG) {
+ err.printStackTrace();
+ }
+ // do not throw any exception here. user may want to
+ // manage his/her own factories using this manager
+ // by explicit registratation (by registerXXX) methods.
+ }
+ }
+
+ /** Set of script engine factories discovered. */
+ private TreeSet _scriptEngineFactories;
+
+ /** Map of engine name to script engine factory. */
+ private HashMap _nameAssociations = new HashMap<>();
+
+ /** Global bindings associated with script engines created by this manager. */
+ private Bindings _globalScope = new SimpleBindings();
+}
diff --git a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java
index 1d5b62e76..0ba86fb4c 100644
--- a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java
+++ b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java
@@ -130,8 +130,8 @@ public class BasicEventSimulationEngine implements SimulationEngine {
double oldAlt = currentStatus.getRocketPosition().z;
if (SimulationListenerHelper.firePreStep(currentStatus)) {
- // Step at most to the next event
- double maxStepTime = Double.MAX_VALUE;
+ // Step at most to the next event. If there is no next event, don't step time
+ double maxStepTime = 0.0;
FlightEvent nextEvent = currentStatus.getEventQueue().peek();
if (nextEvent != null) {
maxStepTime = MathUtil.max(nextEvent.getTime() - currentStatus.getSimulationTime(), 0.001);
diff --git a/core/src/net/sf/openrocket/simulation/GroundStepper.java b/core/src/net/sf/openrocket/simulation/GroundStepper.java
index 49c8b5b4f..b13cfde8c 100644
--- a/core/src/net/sf/openrocket/simulation/GroundStepper.java
+++ b/core/src/net/sf/openrocket/simulation/GroundStepper.java
@@ -20,5 +20,6 @@ public class GroundStepper extends AbstractSimulationStepper {
@Override
public void step(SimulationStatus status, double timeStep) throws SimulationException {
log.trace("step: position=" + status.getRocketPosition() + ", velocity=" + status.getRocketVelocity());
+ status.setSimulationTime(status.getSimulationTime() + timeStep);
}
}
diff --git a/core/src/net/sf/openrocket/simulation/extension/impl/ScriptingExtension.java b/core/src/net/sf/openrocket/simulation/extension/impl/ScriptingExtension.java
index 3569d2aae..953543442 100644
--- a/core/src/net/sf/openrocket/simulation/extension/impl/ScriptingExtension.java
+++ b/core/src/net/sf/openrocket/simulation/extension/impl/ScriptingExtension.java
@@ -2,7 +2,6 @@ package net.sf.openrocket.simulation.extension.impl;
import javax.script.Invocable;
import javax.script.ScriptEngine;
-import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import net.sf.openrocket.aerodynamics.Warning;
@@ -91,8 +90,7 @@ public class ScriptingExtension extends AbstractSimulationExtension {
SimulationListener getListener() throws SimulationException {
- ScriptEngineManager manager = new ScriptEngineManager();
- ScriptEngine engine = manager.getEngineByName(getLanguage());
+ ScriptEngine engine = util.getEngineByName(getLanguage());
if (engine == null) {
throw new SimulationException("Your JRE does not support the scripting language '" + getLanguage() + "'");
}
diff --git a/core/src/net/sf/openrocket/simulation/extension/impl/ScriptingUtil.java b/core/src/net/sf/openrocket/simulation/extension/impl/ScriptingUtil.java
index 7259a9787..ef215ae3e 100644
--- a/core/src/net/sf/openrocket/simulation/extension/impl/ScriptingUtil.java
+++ b/core/src/net/sf/openrocket/simulation/extension/impl/ScriptingUtil.java
@@ -4,14 +4,14 @@ import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
-import java.util.Arrays;
import java.util.List;
import java.util.prefs.BackingStoreException;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
-import javax.script.ScriptEngineManager;
+import net.sf.openrocket.scripting.ScriptEngineManagerRedux;
+import net.sf.openrocket.scripting.GraalJSScriptEngineFactory;
import net.sf.openrocket.startup.Preferences;
import net.sf.openrocket.util.ArrayList;
import net.sf.openrocket.util.BugException;
@@ -22,22 +22,34 @@ import com.google.inject.Inject;
* Utility class used by the scripting extension and its configurator.
*/
public class ScriptingUtil {
-
static final String NODE_ID = ScriptingExtension.class.getCanonicalName();
- private static final List DEFAULT_TRUSTED_HASHES = Arrays.asList(
+ private static final List DEFAULT_TRUSTED_HASHES = List.of(
// Roll control script in roll control example file:
"SHA-256:9bf364ce4d4a75f09b29178bf9d6872b232084f73dae20dc7b5b073e54e95a42"
- );
+ );
/** The name to be chosen from a list of alternatives. If not found, will use the default name. */
- private static final List PREFERRED_LANGUAGE_NAMES = Arrays.asList("JavaScript");
+ private static final List PREFERRED_LANGUAGE_NAMES = List.of("JavaScript");
+
+ private static ScriptEngineManagerRedux manager;
@Inject
Preferences prefs;
-
-
-
+
+ public ScriptingUtil() {
+ if (manager == null) {
+ // using the ScriptEngineManger from javax.script package pulls in the sun.misc.ServiceConfigurationError
+ // which is removed in Java 9+ which causes a ClassNotFoundException to be thrown.
+ manager = new ScriptEngineManagerRedux();
+
+ manager.registerEngineName("Javascript", new GraalJSScriptEngineFactory());
+ }
+ }
+
+ public ScriptEngine getEngineByName(String shortName) {
+ return manager.getEngineByName(shortName);
+ }
/**
* Return the preferred internal language name based on a script language name.
@@ -48,38 +60,23 @@ public class ScriptingUtil {
if (language == null) {
return null;
}
-
- ScriptEngineManager manager = new ScriptEngineManager();
+
ScriptEngine engine = manager.getEngineByName(language);
if (engine == null) {
return null;
}
- return getLanguage(engine.getFactory());
+
+ return getLanguageByFactory(engine.getFactory());
}
-
-
+
public List getLanguages() {
- List langs = new ArrayList();
- ScriptEngineManager manager = new ScriptEngineManager();
+ List languages = new ArrayList<>();
for (ScriptEngineFactory factory : manager.getEngineFactories()) {
- langs.add(getLanguage(factory));
+ languages.add(getLanguageByFactory(factory));
}
- return langs;
+ return languages;
}
-
- private String getLanguage(ScriptEngineFactory factory) {
- for (String name : factory.getNames()) {
- if (PREFERRED_LANGUAGE_NAMES.contains(name)) {
- return name;
- }
- }
-
- return factory.getLanguageName();
- }
-
-
-
/**
* Test whether the user has indicated this script to be trusted,
* or if it is an internally trusted script.
@@ -122,7 +119,16 @@ public class ScriptingUtil {
throw new BugException(e);
}
}
-
+
+ private String getLanguageByFactory(ScriptEngineFactory factory) {
+ for (String name : factory.getNames()) {
+ if (PREFERRED_LANGUAGE_NAMES.contains(name)) {
+ return name;
+ }
+ }
+
+ return factory.getLanguageName();
+ }
static String normalize(String script) {
return script.replaceAll("\r", "").trim();
@@ -132,10 +138,8 @@ public class ScriptingUtil {
/*
* NOTE: Hash length must be max 80 chars, the max length of a key in a Properties object.
*/
-
String output;
MessageDigest digest;
-
try {
digest = MessageDigest.getInstance("SHA-256");
digest.update(language.getBytes(StandardCharsets.UTF_8));
@@ -152,5 +156,4 @@ public class ScriptingUtil {
return digest.getAlgorithm() + ":" + output;
}
-
}
diff --git a/core/src/net/sf/openrocket/simulation/listeners/example/RollControlListener.java b/core/src/net/sf/openrocket/simulation/listeners/example/RollControlListener.java
index 55a557d70..5493c4a0d 100644
--- a/core/src/net/sf/openrocket/simulation/listeners/example/RollControlListener.java
+++ b/core/src/net/sf/openrocket/simulation/listeners/example/RollControlListener.java
@@ -35,8 +35,7 @@ public class RollControlListener extends AbstractSimulationListener {
// Maximum control fin angle (rad)
private static final double MAX_ANGLE = 15 * Math.PI / 180;
-
-
+
/*
* PID parameters
*
@@ -47,29 +46,22 @@ public class RollControlListener extends AbstractSimulationListener {
private static final double KP = 0.007;
private static final double KI = 0.2;
-
-
-
- private double rollrate;
+ private double rollRate;
private double prevTime = 0;
private double intState = 0;
private double finPosition = 0;
-
-
-
+
@Override
public FlightConditions postFlightConditions(SimulationStatus status, FlightConditions flightConditions) {
// Store the current roll rate for later use
- rollrate = flightConditions.getRollRate();
+ rollRate = flightConditions.getRollRate();
return null;
}
-
-
+
@Override
public void postStep(SimulationStatus status) throws SimulationException {
-
// Activate PID controller only after a specific time
if (status.getSimulationTime() < START_TIME) {
prevTime = status.getSimulationTime();
@@ -87,23 +79,20 @@ public class RollControlListener extends AbstractSimulationListener {
if (finset == null) {
throw new SimulationException("A fin set with name '" + CONTROL_FIN_NAME + "' was not found");
}
-
-
+
// Determine time step
double deltaT = status.getSimulationTime() - prevTime;
prevTime = status.getSimulationTime();
-
-
+
// PID controller
- double error = SETPOINT - rollrate;
+ double error = SETPOINT - rollRate;
double p = KP * error;
intState += error * deltaT;
double i = KI * intState;
double value = p + i;
-
-
+
// Clamp the fin angle between -MAX_ANGLE and MAX_ANGLE
if (Math.abs(value) > MAX_ANGLE) {
System.err.printf("Attempting to set angle %.1f at t=%.3f, clamping.\n",
@@ -111,7 +100,6 @@ public class RollControlListener extends AbstractSimulationListener {
value = MathUtil.clamp(value, -MAX_ANGLE, MAX_ANGLE);
}
-
// Limit the fin turn rate
if (finPosition < value) {
finPosition = Math.min(finPosition + TURNRATE * deltaT, value);
@@ -122,6 +110,5 @@ public class RollControlListener extends AbstractSimulationListener {
// Set the control fin cant and store the data
finset.setCantAngle(finPosition);
status.getFlightData().setValue(FIN_CANT_TYPE, finPosition);
-
}
}
diff --git a/core/src/net/sf/openrocket/startup/Preferences.java b/core/src/net/sf/openrocket/startup/Preferences.java
index 64f1de5a0..90da3c320 100644
--- a/core/src/net/sf/openrocket/startup/Preferences.java
+++ b/core/src/net/sf/openrocket/startup/Preferences.java
@@ -63,11 +63,15 @@ public abstract class Preferences implements ChangeSource {
public static final String MOTOR_DIAMETER_FILTER = "MotorDiameterMatch";
public static final String MOTOR_HIDE_SIMILAR = "MotorHideSimilar";
public static final String MOTOR_HIDE_UNAVAILABLE = "MotorHideUnavailable";
+
+ public static final String MATCH_FORE_DIAMETER = "MatchForeDiameter";
+ 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";
+ private static final String SHOW_MARKERS = "SHOW_MARKERS";
private static final String SHOW_ROCKSIM_FORMAT_WARNING = "SHOW_ROCKSIM_FORMAT_WARNING";
//Preferences related to 3D graphics
@@ -469,7 +473,64 @@ public abstract class Preferences implements ChangeSource {
public final boolean isAlwaysOpenLeftmostTab() {
return this.getBoolean(OPEN_LEFTMOST_DESIGN_TAB, false);
}
-
+
+ /**
+ * Set whether pod set/booster markers should only be displayed when the pod set/booster is selected.
+ * @param enabled true if pod set/booster markers should only be displayed when the pod set/booster is selected,
+ * false if they should be displayed permanently.
+ */
+ public final void setShowMarkers(boolean enabled) {
+ this.putBoolean(SHOW_MARKERS, enabled);
+ }
+
+ /**
+ * Answer if pod set/booster markers should only be displayed when the pod set/booster is selected
+ *
+ * @return true if pod set/booster markers should only be displayed when the pod set/booster is selected,
+ * false if they should be displayed permanently.
+ */
+ public final boolean isShowMarkers() {
+ return this.getBoolean(SHOW_MARKERS, false);
+ }
+
+ /**
+ * Set whether the component preset chooser dialog should filter by fore diameter when the window is opened.
+ * @param enabled true if the fore diameter filter should be enabled,
+ * false if it should be disabled.
+ */
+ public final void setMatchForeDiameter(boolean enabled) {
+ this.putBoolean(MATCH_FORE_DIAMETER, enabled);
+ }
+
+ /**
+ * Answer if the component preset chooser dialog should filter by fore diameter when the window is opened.
+ *
+ * @return true if the fore diameter filter should be enabled,
+ * false if it should be disabled.
+ */
+ public final boolean isMatchForeDiameter() {
+ return this.getBoolean(MATCH_FORE_DIAMETER, true);
+ }
+
+ /**
+ * Set whether the component preset chooser dialog should filter by aft diameter when the window is opened.
+ * @param enabled true if the aft diameter filter should be enabled,
+ * false if it should be disabled.
+ */
+ public final void setMatchAftDiameter(boolean enabled) {
+ this.putBoolean(MATCH_AFT_DIAMETER, enabled);
+ }
+
+ /**
+ * Answer if the component preset chooser dialog should filter by aft diameter when the window is opened.
+ *
+ * @return true if the aft diameter filter should be enabled,
+ * false if it should be disabled.
+ */
+ public final boolean isMatchAftDiameter() {
+ return this.getBoolean(MATCH_AFT_DIAMETER, true);
+ }
+
/**
* Return the OpenRocket unique ID.
*
diff --git a/core/test/net/sf/openrocket/simulation/DisableStageTest.java b/core/test/net/sf/openrocket/simulation/DisableStageTest.java
index 9b4b0f7df..d4b7d5a1d 100644
--- a/core/test/net/sf/openrocket/simulation/DisableStageTest.java
+++ b/core/test/net/sf/openrocket/simulation/DisableStageTest.java
@@ -19,6 +19,8 @@ import org.junit.Test;
* @author Sibo Van Gool
*/
public class DisableStageTest extends BaseTestCase {
+ private final double delta = 0.08; // 8 % error margin (simulations are not exact)
+
/**
* Tests that the simulation results are correct when a single stage is deactivated and re-activated.
*/
@@ -54,7 +56,6 @@ public class DisableStageTest extends BaseTestCase {
simDisabled.getActiveConfiguration().setAllStages(); // Re-enable all stages.
- double delta = 0.05; // 5 % error margin (simulations are not exact)
compareSims(simOriginal, simDisabled, simulationListener, delta);
}
@@ -84,7 +85,6 @@ public class DisableStageTest extends BaseTestCase {
SimulationListener simulationListener = new AbstractSimulationListener();
- double delta = 0.05; // 5 % error margin (simulations are not exact)
compareSims(simRemoved, simDisabled, simulationListener, delta);
//// Test re-enableing the stage.
@@ -175,7 +175,6 @@ public class DisableStageTest extends BaseTestCase {
SimulationListener simulationListener = new AbstractSimulationListener();
- double delta = 0.05; // 5 % error margin (simulations are not exact)
compareSims(simRemoved, simDisabled, simulationListener, delta);
//// Test re-enableing the stage.
@@ -243,7 +242,6 @@ public class DisableStageTest extends BaseTestCase {
simDisabled.getActiveConfiguration().setAllStages();
- double delta = 0.05; // 5 % error margin (simulations are not exact)
compareSims(simOriginal, simDisabled, simulationListener, delta);
}
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index aca598150..af6fe1e98 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -1,6 +1,6 @@
name: openrocket
adopt-info: openrocket
-grade: devel
+grade: stable
summary: A free, fully featured model rocket simulator.
description: |
OpenRocket is a free, fully featured model rocket simulator that allows you
@@ -77,6 +77,7 @@ parts:
prime:
- -usr/lib/jvm/java-*/lib/security/cacerts
- -usr/lib/jvm/java-*/jre/lib/security/cacerts
+ - -usr/lib/jvm/java-*/lib/security/blacklisted.certs
launcher:
plugin: dump
diff --git a/swing/.classpath b/swing/.classpath
index 7daea48ea..6878da13d 100644
--- a/swing/.classpath
+++ b/swing/.classpath
@@ -19,6 +19,7 @@
+
diff --git a/swing/OpenRocket Swing.iml b/swing/OpenRocket Swing.iml
index a4dcbfdb1..c9df46ca4 100644
--- a/swing/OpenRocket Swing.iml
+++ b/swing/OpenRocket Swing.iml
@@ -245,5 +245,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/swing/build.xml b/swing/build.xml
index dada6c98c..578f0994c 100644
--- a/swing/build.xml
+++ b/swing/build.xml
@@ -99,6 +99,11 @@
+
+
+
+
+
@@ -149,7 +154,7 @@
depends="build">
-
+
+
+
+ 0.1
+
+
+
+
+ Delrin
+ 1420
+ BULK
+
+
+
+ Nylon
+ 1150
+ BULK
+
+
+
+
+
+
+
+ Binder Design-Rail Button Supply House
+ Std 1010 RB
+ Standard 1010 Rail Button, Countersunk 8-32 Screw, and T-Nut
+ Delrin
+ Polished
+ 0.4375
+ 0.2285
+ 0.2975
+ 0.0730
+ 0.0730
+ 0.0000
+
+ 0.445
+ 1.395
+ 1.025
+
+
+
+ Binder Design-Rail Button Supply House
+ Std 1515 RB
+ Standard 1515 Rail Button, Countersunk 10-32 Screw, and T-Nut
+ Delrin
+ Polished
+ 0.6200
+ 0.2995
+ .4495
+ 0.125
+ 0.125
+ 0.0000
+
+ 1.465
+ 3.365
+ 2.675
+
+
+
+ Rail-Buttons.com
+ RB-Micro
+ 2 Piece Micro Rail Button with 2-56 Screw (10mm Rail)
+ Nylon
+ Polished
+ .1650
+ .1195
+ 0.1595
+ 0.041
+ 0.0000
+ 0.0465
+
+ 0.01
+ 0.04
+
+
+
+
+ Rail-Buttons.com
+ 1PMB
+ 1 Piece Mini Rail Button with Countersunk 6-32 Screw
+ Delrin
+ Polished
+ .249
+ .193
+ 0.205
+ 0.0380
+ 0.0380
+ 0.0000
+
+ 0.090
+ 0.415
+
+
+
+
+ Rail-Buttons.com
+ RB-10-D
+ 3 Piece 1010 Rail Button with 8-32 Screw
+ Delrin
+ Polished
+ .278
+ .154
+ 0.270
+ 0.060
+ 0.060
+ 0.115
+
+ 0.305
+ 1.715
+
+
+
+
+ Rail-Buttons.com
+ 1P1010DLX
+ 1 Piece 1010 Rail Button with Countersunk 8-32 Screw
+ Delrin
+ Polished
+ .3725
+ .2480
+ 0.305
+ 0.078
+ 0.078
+ 0.0000
+
+ 0.320
+ 1.235
+
+
+
+
+ Rail-Buttons.com
+ RB1515S
+ 1 Piece 1515 Rail Button, Countersunk 10-32 Screw, and T-Nut
+ Delrin
+ Polished
+ 0.49
+ 0.29
+ 0.56
+ 0.1875
+ 0.1875
+ 0.0000
+
+ 1.355
+ 2.720
+
+
+
+
+
+
diff --git a/swing/resources/datafiles/examples/Simulation extensions and scripting.ork b/swing/resources/datafiles/examples/Simulation extensions and scripting.ork
index a1592fc11..e84fc0d9d 100644
Binary files a/swing/resources/datafiles/examples/Simulation extensions and scripting.ork and b/swing/resources/datafiles/examples/Simulation extensions and scripting.ork differ
diff --git a/swing/src/net/sf/openrocket/gui/adaptors/PresetModel.java b/swing/src/net/sf/openrocket/gui/adaptors/PresetModel.java
index e62eac2d5..2fcec306e 100644
--- a/swing/src/net/sf/openrocket/gui/adaptors/PresetModel.java
+++ b/swing/src/net/sf/openrocket/gui/adaptors/PresetModel.java
@@ -5,17 +5,14 @@ import java.util.List;
import javax.swing.AbstractListModel;
import javax.swing.ComboBoxModel;
-import javax.swing.SwingUtilities;
-import net.sf.openrocket.database.ComponentPresetDatabase;
-import net.sf.openrocket.gui.configdialog.RocketComponentConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import net.sf.openrocket.gui.configdialog.RocketComponentConfig;
import net.sf.openrocket.database.Database;
import net.sf.openrocket.database.DatabaseListener;
import net.sf.openrocket.document.OpenRocketDocument;
-import net.sf.openrocket.gui.dialogs.preset.ComponentPresetChooserDialog;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.logging.Markers;
import net.sf.openrocket.preset.ComponentPreset;
@@ -30,8 +27,7 @@ public class PresetModel extends AbstractListModel implements ComboBoxModel, Com
private static final Logger log = LoggerFactory.getLogger(PresetModel.class);
private static final Translator trans = Application.getTranslator();
- private static final String NONE_SELECTED = trans.get("lbl.select");
- private static final String SELECT_DATABASE = trans.get("lbl.database");
+ private static final String NONE_SELECTED = String.format("%s", trans.get("PresetModel.lbl.custompreset"));
private final Component parent;
private final RocketComponent component;
@@ -51,7 +47,7 @@ public class PresetModel extends AbstractListModel implements ComboBoxModel, Com
@Override
public int getSize() {
- return presets.size() + 2;
+ return presets.size() + 1;
}
@Override
@@ -59,9 +55,6 @@ public class PresetModel extends AbstractListModel implements ComboBoxModel, Com
if (index == 0) {
return NONE_SELECTED;
}
- if (index == getSize() - 1) {
- return SELECT_DATABASE;
- }
return presets.get(index - 1);
}
@@ -73,21 +66,6 @@ public class PresetModel extends AbstractListModel implements ComboBoxModel, Com
throw new BugException("item is null");
} else if (item.equals(NONE_SELECTED)) {
component.clearPreset();
- } else if (item.equals(SELECT_DATABASE)) {
- SwingUtilities.invokeLater(new Runnable() {
- @Override
- public void run() {
- ((ComponentPresetDatabase) Application.getComponentPresetDao()).addDatabaseListener(PresetModel.this);
- ComponentPresetChooserDialog dialog =
- new ComponentPresetChooserDialog(SwingUtilities.getWindowAncestor(parent), component);
- dialog.setVisible(true);
- ComponentPreset preset = dialog.getSelectedComponentPreset();
- if (preset != null) {
- setSelectedItem(preset);
- }
- ((ComponentPresetDatabase) Application.getComponentPresetDao()).removeChangeListener(PresetModel.this);
- }
- });
} else {
document.addUndoPosition("Use Preset " + component.getComponentName());
component.loadPreset((ComponentPreset) item);
diff --git a/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java b/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java
index 3566cf3e9..bd15b1b90 100644
--- a/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java
+++ b/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java
@@ -1,18 +1,24 @@
package net.sf.openrocket.gui.configdialog;
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
+import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
import java.util.List;
import javax.swing.JDialog;
import net.sf.openrocket.document.OpenRocketDocument;
+import net.sf.openrocket.gui.main.BasicFrame;
import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.gui.util.SwingPreferences;
+import net.sf.openrocket.gui.util.WindowLocationUtil;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.rocketcomponent.AxialStage;
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
@@ -230,8 +236,9 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
* @param document the document to configure.
* @param component the component to configure.
* @param rememberPreviousTab if true, the previous tab will be remembered and used for the new dialog
+ * @param includeUndoModify if true, include a 'Modify component' undo action
*/
- public static void showDialog(Window parent, OpenRocketDocument document, RocketComponent component, boolean rememberPreviousTab) {
+ public static void showDialog(Window parent, OpenRocketDocument document, RocketComponent component, boolean rememberPreviousTab, boolean includeUndoModify) {
if (dialog != null) {
// Don't remember the previous tab for rockets or stages, because this will leave you in the override tab for
// the next component, which is generally not what you want.
@@ -256,15 +263,32 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
dialog = new ComponentConfigDialog(parent, document, component);
dialog.setVisible(true);
+ if (parent instanceof BasicFrame && BasicFrame.getStartupFrame() == parent) {
+ WindowLocationUtil.moveIfOutsideOfParentMonitor(dialog, parent);
+ }
////Modify
- if (component.getConfigListeners().size() == 0) {
- document.addUndoPosition(trans.get("ComponentCfgDlg.Modify") + " " + component.getComponentName());
- } else {
- document.addUndoPosition(trans.get("ComponentCfgDlg.ModifyComponents"));
+ if (includeUndoModify) {
+ if (component.getConfigListeners().size() == 0) {
+ document.addUndoPosition(trans.get("ComponentCfgDlg.Modify") + " " + component.getComponentName());
+ } else {
+ document.addUndoPosition(trans.get("ComponentCfgDlg.ModifyComponents"));
+ }
}
}
+ /**
+ * A singleton configuration dialog. Will create and show a new dialog if one has not
+ * previously been used, or update the dialog and show it if a previous one exists.
+ *
+ * @param document the document to configure.
+ * @param component the component to configure.
+ * @param rememberPreviousTab if true, the previous tab will be remembered and used for the new dialog
+ */
+ public static void showDialog(Window parent, OpenRocketDocument document, RocketComponent component, boolean rememberPreviousTab) {
+ ComponentConfigDialog.showDialog(parent, document, component, rememberPreviousTab, true);
+ }
+
/**
* A singleton configuration dialog. Will create and show a new dialog if one has not
* previously been used, or update the dialog and show it if a previous one exists.
diff --git a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java
index ba6b077da..b695e5dbf 100644
--- a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java
+++ b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java
@@ -81,6 +81,7 @@ public class FreeformFinSetConfig extends FinSetConfig {
private Point dragPoint = null;
private FinPointFigure figure = null;
+ private ScaleScrollPane figurePane = null;
private ScaleSelector selector;
public FreeformFinSetConfig(OpenRocketDocument d, RocketComponent component, JDialog parent) {
@@ -219,7 +220,7 @@ public class FreeformFinSetConfig extends FinSetConfig {
// Create the figure
figure = new FinPointFigure(finset);
- ScaleScrollPane figurePane = new FinPointScrollPane( figure);
+ figurePane = new FinPointScrollPane( figure);
// Create the table
tableModel = new FinPointTableModel();
@@ -392,9 +393,10 @@ public class FreeformFinSetConfig extends FinSetConfig {
}
figure.updateFigure();
}
-
- revalidate();
- repaint();
+
+ if (figurePane != null) {
+ figurePane.revalidate();
+ }
}
private class FinPointScrollPane extends ScaleScrollPane {
@@ -408,8 +410,6 @@ public class FreeformFinSetConfig extends FinSetConfig {
@Override
public void mousePressed(MouseEvent event) {
- int mods = event.getModifiersEx();
-
final FreeformFinSet finset = (FreeformFinSet) component;
final int pressIndex = getPoint(event);
diff --git a/swing/src/net/sf/openrocket/gui/configdialog/RailButtonConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/RailButtonConfig.java
index b5e005a4a..be4803c42 100644
--- a/swing/src/net/sf/openrocket/gui/configdialog/RailButtonConfig.java
+++ b/swing/src/net/sf/openrocket/gui/configdialog/RailButtonConfig.java
@@ -29,13 +29,6 @@ public class RailButtonConfig extends RocketComponentConfig {
public RailButtonConfig( OpenRocketDocument document, RocketComponent component, JDialog parent) {
super(document, component, parent);
-
- // For DEBUG purposes
-// if( component instanceof AxialStage ){
-// System.err.println(" Dumping AxialStage tree info for devel / debugging.");
-// System.err.println(component.toDebugTree());
-// }
-
//// General and General properties
tabbedPane.insertTab( trans.get("RailBtnCfg.tab.General"), null, buttonTab( (RailButton)component ), trans.get("RailBtnCfg.tab.GeneralProp"), 0);
@@ -59,6 +52,33 @@ public class RailButtonConfig extends RocketComponentConfig {
panel.add(new UnitSelector(ODModel), "growx");
panel.add(new BasicSlider(ODModel.getSliderModel(0, 0.001, 0.02)), "w 100lp, wrap");
}
+ { //// Inner Diameter
+ panel.add(new JLabel(trans.get("RailBtnCfg.lbl.InnerDiam")));
+ DoubleModel IDModel = new DoubleModel(component, "InnerDiameter", UnitGroup.UNITS_LENGTH, 0);
+ JSpinner IDSpinner = new JSpinner(IDModel.getSpinnerModel());
+ IDSpinner.setEditor(new SpinnerEditor(IDSpinner));
+ panel.add(IDSpinner, "growx");
+ panel.add(new UnitSelector(IDModel), "growx");
+ panel.add(new BasicSlider(IDModel.getSliderModel(0, 0.001, 0.02)), "w 100lp, wrap para");
+ }
+ { //// Base Height
+ panel.add(new JLabel(trans.get("RailBtnCfg.lbl.BaseHeight")));
+ DoubleModel heightModel = new DoubleModel(component, "BaseHeight", UnitGroup.UNITS_LENGTH, 0);
+ JSpinner heightSpinner = new JSpinner(heightModel.getSpinnerModel());
+ heightSpinner.setEditor(new SpinnerEditor(heightSpinner));
+ panel.add(heightSpinner, "growx");
+ panel.add(new UnitSelector(heightModel), "growx");
+ panel.add(new BasicSlider(heightModel.getSliderModel(0, 0.001, 0.02)), "w 100lp, wrap");
+ }
+ { //// Flange Height
+ panel.add(new JLabel(trans.get("RailBtnCfg.lbl.FlangeHeight")));
+ DoubleModel heightModel = new DoubleModel(component, "FlangeHeight", UnitGroup.UNITS_LENGTH, 0);
+ JSpinner heightSpinner = new JSpinner(heightModel.getSpinnerModel());
+ heightSpinner.setEditor(new SpinnerEditor(heightSpinner));
+ panel.add(heightSpinner, "growx");
+ panel.add(new UnitSelector(heightModel), "growx");
+ panel.add(new BasicSlider(heightModel.getSliderModel(0, 0.001, 0.02)), "w 100lp, wrap");
+ }
{ //// Height
panel.add(new JLabel(trans.get("RailBtnCfg.lbl.TotalHeight")));
DoubleModel heightModel = new DoubleModel(component, "TotalHeight", UnitGroup.UNITS_LENGTH, 0);
@@ -66,7 +86,7 @@ public class RailButtonConfig extends RocketComponentConfig {
heightSpinner.setEditor(new SpinnerEditor(heightSpinner));
panel.add(heightSpinner, "growx");
panel.add(new UnitSelector(heightModel), "growx");
- panel.add(new BasicSlider(heightModel.getSliderModel(0, 0.001, 0.02)), "w 100lp, wrap");
+ panel.add(new BasicSlider(heightModel.getSliderModel(0, 0.001, 0.02)), "w 100lp, wrap para");
}
{ //// Angular Position:
@@ -79,14 +99,17 @@ public class RailButtonConfig extends RocketComponentConfig {
panel.add(new BasicSlider( angleModel.getSliderModel(-Math.PI, Math.PI)), "w 100lp, wrap");
}
+ primary.add(panel, "grow, gapright 201p");
+ panel = new JPanel(new MigLayout("gap rel unrel", "[][65lp::][30lp::][]", ""));
+
{ //// Position relative to:
panel.add(new JLabel(trans.get("RailBtnCfg.lbl.PosRelativeTo")));
-
+
final EnumModel methodModel = new EnumModel(component, "AxialMethod", AxialMethod.axialOffsetMethods );
JComboBox relToCombo = new JComboBox( methodModel );
panel.add( relToCombo, "spanx, growx, wrap");
}
-
+
{ //// plus
panel.add(new JLabel(trans.get("RailBtnCfg.lbl.Plus")), "right");
DoubleModel offsetModel = new DoubleModel(component, "AxialOffset", UnitGroup.UNITS_LENGTH);
@@ -96,17 +119,13 @@ public class RailButtonConfig extends RocketComponentConfig {
panel.add(offsetSpinner, "growx");
panel.add(new UnitSelector(offsetModel), "growx");
panel.add(new BasicSlider(offsetModel.getSliderModel(
- new DoubleModel(component.getParent(), "Length", -1.0, UnitGroup.UNITS_NONE),
- new DoubleModel(component.getParent(), "Length"))),
+ new DoubleModel(component.getParent(), "Length", -1.0, UnitGroup.UNITS_NONE),
+ new DoubleModel(component.getParent(), "Length"))),
"w 100lp, wrap para");
-
- }
- primary.add(panel, "grow, gapright 201p");
- panel = new JPanel(new MigLayout("gap rel unrel", "[][65lp::][30lp::][]", ""));
-
+ }
//// Instance count
- panel.add( instanceablePanel(rbc), "span, wrap");
+ panel.add(instanceablePanel(rbc), "span, wrap");
//// Material
panel.add(materialPanel(Material.Type.BULK),"span, wrap");
diff --git a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java
index dd27f02c3..57e7568d2 100644
--- a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java
+++ b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java
@@ -25,6 +25,7 @@ import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.database.ComponentPresetDatabase;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.gui.SpinnerEditor;
import net.sf.openrocket.gui.adaptors.BooleanModel;
@@ -37,6 +38,7 @@ import net.sf.openrocket.gui.components.BasicSlider;
import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.gui.components.StyledLabel.Style;
import net.sf.openrocket.gui.components.UnitSelector;
+import net.sf.openrocket.gui.dialogs.preset.ComponentPresetChooserDialog;
import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.gui.widgets.SelectColorButton;
import net.sf.openrocket.l10n.Translator;
@@ -117,8 +119,20 @@ public class RocketComponentConfig extends JPanel {
// If the component supports a preset, show the preset selection box.
presetModel = new PresetModel(this, document, component);
presetComboBox = new JComboBox(presetModel);
+ presetComboBox.setMaximumRowCount(25);
presetComboBox.setEditable(false);
- this.add(presetComboBox, "");
+ presetComboBox.setToolTipText(trans.get("PresetModel.combo.ttip"));
+ this.add(presetComboBox, "growx 110");
+
+ final JButton selectPreset = new SelectColorButton(trans.get("PresetModel.lbl.partsLib"));
+ selectPreset.setToolTipText(trans.get("PresetModel.lbl.partsLib.ttip"));
+ selectPreset.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ selectPreset();
+ }
+ });
+ this.add(selectPreset);
}
tabbedPane = new JTabbedPane();
@@ -243,6 +257,21 @@ public class RocketComponentConfig extends JPanel {
}
}
+ private void selectPreset() {
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ if (presetComboBox == null || presetModel == null) return;
+ ((ComponentPresetDatabase) Application.getComponentPresetDao()).addDatabaseListener(presetModel);
+ ComponentPresetChooserDialog dialog =
+ new ComponentPresetChooserDialog(SwingUtilities.getWindowAncestor(RocketComponentConfig.this),
+ component, presetModel);
+ dialog.setVisible(true);
+ ((ComponentPresetDatabase) Application.getComponentPresetDao()).removeChangeListener(presetModel);
+ }
+ });
+ }
+
public void clearConfigListeners() {
if (appearancePanel != null) {
appearancePanel.clearConfigListeners();
@@ -353,7 +382,7 @@ public class RocketComponentConfig extends JPanel {
}
protected JPanel instanceablePanel( Instanceable inst ){
- JPanel panel = new JPanel( new MigLayout("fill"));
+ JPanel panel = new JPanel( new MigLayout("fill, insets 0") );
{ // Instance Count
panel.add(new JLabel(trans.get("RocketCompCfg.lbl.InstanceCount")));
IntegerModel countModel = new IntegerModel(component, "InstanceCount", 1);
@@ -369,7 +398,11 @@ public class RocketComponentConfig extends JPanel {
separationSpinner.setEditor(new SpinnerEditor(separationSpinner));
panel.add(separationSpinner, "growx");
panel.add(new UnitSelector(separationModel), "growx");
- panel.add(new BasicSlider(separationModel.getSliderModel(0, 0.001, 0.02)), "w 100lp, wrap para");
+ double maxSeparationDistance = 0.1;
+ if (component.getParent() != null && component.getParent().getLength() > 0) {
+ maxSeparationDistance = component.getParent().getLength();
+ }
+ panel.add(new BasicSlider(separationModel.getSliderModel(0, 0.001, maxSeparationDistance)), "w 100lp, wrap para");
}
return panel;
}
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/ScaleDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/ScaleDialog.java
index ad53576e6..3a1db6af8 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/ScaleDialog.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/ScaleDialog.java
@@ -346,7 +346,7 @@ public class ScaleDialog extends JDialog {
panel.add(selectionOption, "growx, wrap para*2");
// Select the 'scale component / scale selection and all subcomponents' if a component is selected
- if (selection != null && selection.size() > 0) {
+ if (options.size() > 1 && selection != null && selection.size() > 0) {
boolean entireRocket = false; // Flag to scale entire rocket
for (RocketComponent component : selection) {
if (component instanceof Rocket || (component instanceof AxialStage && !(component instanceof ParallelStage))) {
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/motor/MotorChooserDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/motor/MotorChooserDialog.java
index b53e4a696..396cd7e5a 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/motor/MotorChooserDialog.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/motor/MotorChooserDialog.java
@@ -91,7 +91,7 @@ public class MotorChooserDialog extends JDialog implements CloseableDialog {
// Set the closeable dialog after all initialization
selectionPanel.setCloseableDialog(this);
- GUIUtil.setDisposableDialogOptions(this, cancelButton);
+ GUIUtil.setWindowIcons(this);
}
public void setMotorMountAndConfig( FlightConfigurationId _fcid, MotorMount _mount ) {
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preferences/DesignPreferencesPanel.java b/swing/src/net/sf/openrocket/gui/dialogs/preferences/DesignPreferencesPanel.java
index 08ef4acb1..3a98bd616 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/preferences/DesignPreferencesPanel.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/preferences/DesignPreferencesPanel.java
@@ -11,6 +11,7 @@ import javax.swing.JSpinner;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.gui.SpinnerEditor;
import net.sf.openrocket.gui.adaptors.DoubleModel;
+import net.sf.openrocket.gui.main.BasicFrame;
import net.sf.openrocket.startup.Preferences;
import net.sf.openrocket.unit.UnitGroup;
@@ -93,7 +94,6 @@ public class DesignPreferencesPanel extends PreferencesPanel {
// // Always open leftmost tab when opening a component edit dialog
final JCheckBox alwaysOpenLeftmostTab = new JCheckBox(
trans.get("pref.dlg.checkbox.AlwaysOpenLeftmost"));
-
alwaysOpenLeftmostTab.setSelected(preferences.isAlwaysOpenLeftmostTab());
alwaysOpenLeftmostTab.setToolTipText(trans.get("pref.dlg.checkbox.AlwaysOpenLeftmost.ttip"));
alwaysOpenLeftmostTab.addActionListener(new ActionListener() {
@@ -103,7 +103,7 @@ public class DesignPreferencesPanel extends PreferencesPanel {
.isSelected());
}
});
- this.add(alwaysOpenLeftmostTab, "wrap, growx, span 2");
+ this.add(alwaysOpenLeftmostTab, "wrap, growx, spanx");
// // Update flight estimates in the design window
final JCheckBox updateEstimates = new JCheckBox(
@@ -117,5 +117,23 @@ public class DesignPreferencesPanel extends PreferencesPanel {
}
});
this.add(updateEstimates, "wrap, growx, sg combos ");
+
+ // // Only show pod set/booster markers when they are selected
+ final JCheckBox showMarkers = new JCheckBox(
+ trans.get("pref.dlg.checkbox.Markers"));
+ showMarkers.setToolTipText(trans.get("pref.dlg.checkbox.Markers.ttip"));
+ showMarkers.setSelected(preferences.isShowMarkers());
+ showMarkers.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ preferences.setShowMarkers(showMarkers
+ .isSelected());
+ // Update all BasicFrame rocket panel figures because it can change due to the preference change
+ for (BasicFrame frame : BasicFrame.getAllFrames()) {
+ frame.getRocketPanel().updateFigures();
+ }
+ }
+ });
+ this.add(showMarkers, "wrap, growx, spanx");
}
}
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetChooserDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetChooserDialog.java
index 3061d6d62..eaf0afa87 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetChooserDialog.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetChooserDialog.java
@@ -7,6 +7,8 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -25,7 +27,10 @@ import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.gui.adaptors.PresetModel;
+import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.gui.util.GUIUtil;
+import net.sf.openrocket.gui.util.SwingPreferences;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.preset.ComponentPreset;
import net.sf.openrocket.preset.TypedKey;
@@ -43,10 +48,12 @@ public class ComponentPresetChooserDialog extends JDialog {
private static final Translator trans = Application.getTranslator();
+ private final SwingPreferences preferences = (SwingPreferences) Application.getPreferences();
+
private final RocketComponent component;
- private ComponentPresetTable componentSelectionTable;
- private JTextField filterText;
+ private final ComponentPresetTable componentSelectionTable;
+ private final JTextField filterText;
private JCheckBox foreDiameterFilterCheckBox;
private JCheckBox aftDiameterFilterCheckBox;
private JCheckBox showLegacyCheckBox;
@@ -66,12 +73,14 @@ public class ComponentPresetChooserDialog extends JDialog {
private List presets;
private ComponentPreset.Type presetType;
+ private PresetModel presetModel;
- public ComponentPresetChooserDialog(Window owner, RocketComponent component) {
+ public ComponentPresetChooserDialog(Window owner, RocketComponent component, PresetModel presetModel) {
super(owner, trans.get("title"), Dialog.ModalityType.APPLICATION_MODAL);
this.component = component;
this.presetType = component.getPresetType();
+ this.presetModel = presetModel;
this.presets = Application.getComponentPresetDao().listForType(component.getPresetType());
List> displayedColumnKeys = Arrays.asList(component.getPresetType().getDisplayedColumns());
@@ -149,8 +158,18 @@ public class ComponentPresetChooserDialog extends JDialog {
scrollpane.setViewportView(componentSelectionTable);
panel.add(scrollpane, "grow, width 700lp, height 300lp, pushy, spanx, wrap rel");
- panel.add(new JLabel(Chars.UP_ARROW + " " + trans.get("lbl.favorites")), "spanx, gapleft 5px, wrap para");
-
+ panel.add(new StyledLabel(String.format("%s %s", Chars.UP_ARROW, trans.get("lbl.favorites")), -1), "spanx, gapleft 5px, wrap para");
+
+ // When double-clicking a preset row, apply the preset and close this dialog
+ componentSelectionTable.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ // Don't do anything when double-clicking the first column
+ if (e.getClickCount() == 2 && componentSelectionTable.getSelectedColumn() > 0 && applySelectedPreset()) {
+ ComponentPresetChooserDialog.this.setVisible(false);
+ }
+ }
+ });
// Close buttons
JButton closeButton = new SelectColorButton(trans.get("dlg.but.close"));
@@ -158,6 +177,7 @@ public class ComponentPresetChooserDialog extends JDialog {
@Override
public void actionPerformed(ActionEvent e) {
ComponentPresetChooserDialog.this.setVisible(false);
+ applySelectedPreset();
}
});
panel.add(closeButton, "spanx, right, tag close");
@@ -169,6 +189,21 @@ public class ComponentPresetChooserDialog extends JDialog {
updateFilters();
}
+
+ /**
+ * Applies the currently selected preset to presetModel.
+ *
+ * @return true if the preset was applied, false if otherwise.
+ */
+ private boolean applySelectedPreset() {
+ if (presetModel == null) return false;
+ ComponentPreset preset = getSelectedComponentPreset();
+ if (preset != null) {
+ presetModel.setSelectedItem(preset);
+ return true;
+ }
+ return false;
+ }
private JPanel getFilterCheckboxes(XTableColumnModel tm, int legacyColumnIndex) {
@@ -222,12 +257,14 @@ public class ComponentPresetChooserDialog extends JDialog {
foreDiameterFilterCheckBox = new JCheckBox(trans.get("ComponentPresetChooserDialog.checkbox.filterForeDiameter"));
final SymmetricComponent prevSym = curSym.getPreviousSymmetricComponent();
if (prevSym != null && foreDiameterColumnIndex >= 0) {
+ foreDiameterFilterCheckBox.setSelected(preferences.isMatchForeDiameter());
foreDiameterFilter = new ComponentPresetRowFilter(prevSym.getAftRadius() * 2.0, foreDiameterColumnIndex);
panel.add(foreDiameterFilterCheckBox, "wrap");
foreDiameterFilterCheckBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
updateFilters();
+ preferences.setMatchForeDiameter(foreDiameterFilterCheckBox.isSelected());
}
});
}
@@ -238,12 +275,14 @@ public class ComponentPresetChooserDialog extends JDialog {
aftDiameterFilterCheckBox = new JCheckBox(trans.get("ComponentPresetChooserDialog.checkbox.filterAftDiameter"));
final SymmetricComponent nextSym = curSym.getNextSymmetricComponent();
if (nextSym != null && aftDiameterColumnIndex >= 0) {
+ aftDiameterFilterCheckBox.setSelected(preferences.isMatchAftDiameter());
aftDiameterFilter = new ComponentPresetRowFilter(nextSym.getForeRadius() * 2.0, aftDiameterColumnIndex);
panel.add(aftDiameterFilterCheckBox, "wrap");
aftDiameterFilterCheckBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
updateFilters();
+ preferences.setMatchAftDiameter(aftDiameterFilterCheckBox.isSelected());
}
});
}
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTable.java b/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTable.java
index c361baebf..62c1bb31b 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTable.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTable.java
@@ -77,9 +77,11 @@ public class ComponentPresetTable extends JTable {
if ( columnIndex != 0 ) {
return;
}
+ int selectedRow = ComponentPresetTable.this.getSelectedRow();
ComponentPreset preset = ComponentPresetTable.this.presets.get(rowIndex);
Application.getComponentPresetDao().setFavorite(preset, presetType, (Boolean) aValue);
ComponentPresetTable.this.updateFavorites();
+ ComponentPresetTable.this.setRowSelectionInterval(selectedRow, selectedRow);
}
@Override
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTableColumn.java b/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTableColumn.java
index 6bf137b28..04821f75d 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTableColumn.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTableColumn.java
@@ -33,7 +33,7 @@ public abstract class ComponentPresetTableColumn extends TableColumn {
@Override
public Object getValueFromPreset( Set favorites, ComponentPreset preset ) {
- return Boolean.valueOf(favorites.contains(preset.preferenceKey()));
+ return favorites.contains(preset.preferenceKey());
}
}
diff --git a/swing/src/net/sf/openrocket/gui/figure3d/geometry/ComponentRenderer.java b/swing/src/net/sf/openrocket/gui/figure3d/geometry/ComponentRenderer.java
index 5c2d39c1e..84c57a1ec 100644
--- a/swing/src/net/sf/openrocket/gui/figure3d/geometry/ComponentRenderer.java
+++ b/swing/src/net/sf/openrocket/gui/figure3d/geometry/ComponentRenderer.java
@@ -295,26 +295,37 @@ public class ComponentRenderer {
final double ir = r.getInnerDiameter() / 2.0;
gl.glRotated(r.getAngleOffset()*180/Math.PI -90 , 1, 0, 0);
- //Inner Diameter
- glu.gluCylinder(q, ir, ir, r.getTotalHeight(), LOD, 1);
+ // Base Cylinder
+ if (r.getBaseHeight() > 0) {
+ glu.gluCylinder(q, or, or, r.getBaseHeight(), LOD, 1);
+ glu.gluQuadricOrientation(q, GLU.GLU_INSIDE);
+ glu.gluDisk(q, 0, or, LOD, 2);
+ glu.gluQuadricOrientation(q, GLU.GLU_OUTSIDE);
+ gl.glTranslated(0, 0, r.getBaseHeight());
+ glu.gluDisk(q, 0, or, LOD, 2);
+ } else { // Draw a closing cap if there is no base
+ glu.gluQuadricOrientation(q, GLU.GLU_INSIDE);
+ glu.gluDisk(q, 0, ir, LOD, 2);
+ glu.gluQuadricOrientation(q, GLU.GLU_OUTSIDE);
+ gl.glTranslated(0, 0, r.getBaseHeight());
+ }
+
+ // Inner Cylinder
+ glu.gluCylinder(q, ir, ir, r.getInnerHeight(), LOD, 1);
- //Bottom Disc
- glu.gluCylinder(q, or, or, r.getBaseHeight(), LOD, 1);
- glu.gluQuadricOrientation(q, GLU.GLU_INSIDE);
- glu.gluDisk(q, 0, or, LOD, 2);
- glu.gluQuadricOrientation(q, GLU.GLU_OUTSIDE);
- gl.glTranslated(0,0,r.getBaseHeight());
- glu.gluDisk(q, 0, or, LOD, 2);
-
-
- //Upper Disc
- gl.glTranslated(0,0,r.getTotalHeight() - r.getFlangeHeight() * 2.0);
- glu.gluCylinder(q, or, or, r.getFlangeHeight(), LOD, 1);
- glu.gluQuadricOrientation(q, GLU.GLU_INSIDE);
- glu.gluDisk(q, 0, or, LOD, 2);
- glu.gluQuadricOrientation(q, GLU.GLU_OUTSIDE);
- gl.glTranslated(0,0,r.getFlangeHeight());
- glu.gluDisk(q, 0, or, LOD, 2);
+ // Flange Cylinder
+ if (r.getFlangeHeight() > 0) {
+ gl.glTranslated(0, 0, r.getInnerHeight());
+ glu.gluCylinder(q, or, or, r.getFlangeHeight(), LOD, 1);
+ glu.gluQuadricOrientation(q, GLU.GLU_INSIDE);
+ glu.gluDisk(q, 0, or, LOD, 2);
+ glu.gluQuadricOrientation(q, GLU.GLU_OUTSIDE);
+ gl.glTranslated(0, 0, r.getFlangeHeight());
+ glu.gluDisk(q, 0, or, LOD, 2);
+ } else { // Draw a closing cap if there is no flange
+ gl.glTranslated(0, 0, r.getInnerHeight());
+ glu.gluDisk(q, 0, ir, LOD, 2);
+ }
}
}
diff --git a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java
index 2030dba2b..3ce2cd3cc 100644
--- a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java
+++ b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java
@@ -108,7 +108,7 @@ public class PhotoFrame extends JFrame {
settings = new JDialog(this, trans.get("PhotoSettingsConfig.title")) {
{
- setContentPane(new PhotoSettingsConfig(p));
+ setContentPane(new PhotoSettingsConfig(p, document));
pack();
this.setLocationByPlatform(true);
GUIUtil.rememberWindowSize(this);
diff --git a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoSettings.java b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoSettings.java
index eca18a495..23599f7f3 100644
--- a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoSettings.java
+++ b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoSettings.java
@@ -36,7 +36,7 @@ public class PhotoSettings extends AbstractChangeSource implements FlameSettings
private double exhaustScale = 1.0;
private double flameAspectRatio = 1.0;
- private double sparkConcentration = 0;
+ private double sparkConcentration = 0.2;
private double sparkWeight = 0;
private Sky sky = Mountains.instance;
@@ -278,5 +278,6 @@ public class PhotoSettings extends AbstractChangeSource implements FlameSettings
public void setSmokeOpacity(double smokeOpacity) {
this.smokeOpacity = smokeOpacity;
+ setSmokeAlpha(smokeOpacity);
}
}
\ No newline at end of file
diff --git a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoSettingsConfig.java b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoSettingsConfig.java
index 6294a6e96..ef27b0dc4 100644
--- a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoSettingsConfig.java
+++ b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoSettingsConfig.java
@@ -25,8 +25,10 @@ import javax.swing.event.ChangeListener;
import com.jogamp.opengl.GL2;
import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.gui.adaptors.BooleanModel;
import net.sf.openrocket.gui.adaptors.DoubleModel;
+import net.sf.openrocket.gui.components.BasicSlider;
import net.sf.openrocket.gui.components.ColorIcon;
import net.sf.openrocket.gui.components.EditableSpinner;
import net.sf.openrocket.gui.components.StyledLabel;
@@ -125,7 +127,7 @@ public class PhotoSettingsConfig extends JTabbedPane {
}
}
- public PhotoSettingsConfig(PhotoSettings p) {
+ public PhotoSettingsConfig(PhotoSettings p, OpenRocketDocument document) {
super();
setPreferredSize(new Dimension(240, 320));
@@ -170,25 +172,29 @@ public class PhotoSettingsConfig extends JTabbedPane {
add(new JLabel(trans.get("PhotoSettingsConfig.lbl.pitch")));
DoubleModel pitchModel = new DoubleModel(p, "Pitch", UnitGroup.UNITS_ANGLE);
add(new EditableSpinner(pitchModel.getSpinnerModel()), "growx");
- add(new UnitSelector(pitchModel), "pushx, left, wrap");
+ add(new UnitSelector(pitchModel), "growx");
+ add(new BasicSlider(pitchModel.getSliderModel(0, 2 * Math.PI)), "pushx, left, wrap");
/// Yaw
add(new JLabel(trans.get("PhotoSettingsConfig.lbl.yaw")));
DoubleModel yawModel = new DoubleModel(p, "Yaw", UnitGroup.UNITS_ANGLE);
add(new EditableSpinner(yawModel.getSpinnerModel()), "growx");
- add(new UnitSelector(yawModel), "wrap");
+ add(new UnitSelector(yawModel), "growx");
+ add(new BasicSlider(yawModel.getSliderModel(0, 2 * Math.PI)), "wrap");
/// Roll
add(new JLabel(trans.get("PhotoSettingsConfig.lbl.roll")));
DoubleModel rollModel = new DoubleModel(p, "Roll", UnitGroup.UNITS_ANGLE);
add(new EditableSpinner(rollModel.getSpinnerModel()), "growx");
- add(new UnitSelector(rollModel), "wrap");
+ add(new UnitSelector(rollModel), "growx");
+ add(new BasicSlider(rollModel.getSliderModel(0, 2 * Math.PI)), "wrap");
/// Advance
add(new JLabel(trans.get("PhotoSettingsConfig.lbl.advance")));
DoubleModel advanceModel = new DoubleModel(p, "Advance", UnitGroup.UNITS_LENGTH);
add(new EditableSpinner(advanceModel.getSpinnerModel()), "growx");
- add(new UnitSelector(advanceModel), "wrap");
+ add(new UnitSelector(advanceModel), "growx");
+ add(new BasicSlider(advanceModel.getSliderModel(-document.getRocket().getLength(), document.getRocket().getLength())), "wrap");
// Camera
add(new StyledLabel(trans.get("PhotoSettingsConfig.lbl.camera"), Style.BOLD), "split, gapright para, span");
@@ -198,25 +204,29 @@ public class PhotoSettingsConfig extends JTabbedPane {
add(new JLabel(trans.get("PhotoSettingsConfig.lbl.vAz")));
DoubleModel viewAzModel = new DoubleModel(p, "ViewAz", UnitGroup.UNITS_ANGLE);
add(new EditableSpinner(viewAzModel.getSpinnerModel()), "growx");
- add(new UnitSelector(viewAzModel), "wrap");
+ add(new UnitSelector(viewAzModel), "growx");
+ add(new BasicSlider(viewAzModel.getSliderModel(0, 2 * Math.PI)), "wrap");
/// View altitude
add(new JLabel(trans.get("PhotoSettingsConfig.lbl.vAlt")));
- DoubleModel viewAltModle = new DoubleModel(p, "ViewAlt", UnitGroup.UNITS_ANGLE);
+ DoubleModel viewAltModle = new DoubleModel(p, "ViewAlt", UnitGroup.UNITS_ANGLE, -Math.PI / 2, Math.PI / 2);
add(new EditableSpinner(viewAltModle.getSpinnerModel()), "growx");
- add(new UnitSelector(viewAltModle), "wrap");
+ add(new UnitSelector(viewAltModle), "growx");
+ add(new BasicSlider(viewAltModle.getSliderModel(-Math.PI / 2, Math.PI / 2)), "wrap");
/// View distance
add(new JLabel(trans.get("PhotoSettingsConfig.lbl.vDist")));
DoubleModel viewDistanceModel = new DoubleModel(p, "ViewDistance", UnitGroup.UNITS_LENGTH);
add(new EditableSpinner(viewDistanceModel.getSpinnerModel()), "growx");
- add(new UnitSelector(viewDistanceModel), "wrap");
+ add(new UnitSelector(viewDistanceModel), "growx");
+ add(new BasicSlider(viewDistanceModel.getSliderModel(0, 2 * document.getRocket().getLength())), "wrap");
/// FoV
add(new JLabel(trans.get("PhotoSettingsConfig.lbl.fov")));
- DoubleModel fovModel = new DoubleModel(p, "Fov", UnitGroup.UNITS_ANGLE);
+ DoubleModel fovModel = new DoubleModel(p, "Fov", UnitGroup.UNITS_ANGLE, Math.PI * 57.3/180, Math.PI * 160/180);
add(new EditableSpinner(fovModel.getSpinnerModel()), "growx");
- add(new UnitSelector(fovModel), "wrap");
+ add(new UnitSelector(fovModel), "growx");
+ add(new BasicSlider(fovModel.getSliderModel(Math.PI * 57.3/180, Math.PI * 160/180)), "wrap");
}
});
@@ -232,19 +242,24 @@ public class PhotoSettingsConfig extends JTabbedPane {
/// Ambiance
add(new JLabel(trans.get("PhotoSettingsConfig.lbl.amb")));
- DoubleModel ambianceModel = new DoubleModel(p, "Ambiance", 100, UnitGroup.UNITS_NONE, 0, 100);
- add(new EditableSpinner(ambianceModel.getSpinnerModel()), "wrap");
+ DoubleModel ambianceModel = new DoubleModel(p, "Ambiance", UnitGroup.UNITS_RELATIVE, 0, 1);
+ add(new EditableSpinner(ambianceModel.getSpinnerModel()), "growx, split 2");
+ add(new UnitSelector(ambianceModel));
+ add(new BasicSlider(ambianceModel.getSliderModel(0, 1)), "pushx, left, wrap");
/// Light azimuth
add(new JLabel(trans.get("PhotoSettingsConfig.lbl.lightAz")));
DoubleModel lightAzModel = new DoubleModel(p, "LightAz", UnitGroup.UNITS_ANGLE);
- add(new EditableSpinner(lightAzModel.getSpinnerModel()), "split 2");
- add(new UnitSelector(lightAzModel), "pushx, left, wrap");
+ add(new EditableSpinner(lightAzModel.getSpinnerModel()), "growx, split 2");
+ add(new UnitSelector(lightAzModel));
+ add(new BasicSlider(lightAzModel.getSliderModel(-Math.PI, Math.PI)), "wrap");
/// Light altitude
add(new JLabel(trans.get("PhotoSettingsConfig.lbl.lightAlt")));
- DoubleModel lightAltModle = new DoubleModel(p, "LightAlt", UnitGroup.UNITS_ANGLE);
- add(new EditableSpinner(lightAltModle.getSpinnerModel()), "wrap");
+ DoubleModel lightAltModle = new DoubleModel(p, "LightAlt", UnitGroup.UNITS_ANGLE, -Math.PI / 2, Math.PI / 2);
+ add(new EditableSpinner(lightAltModle.getSpinnerModel()), "growx, split 2");
+ add(new UnitSelector(lightAltModle));
+ add(new BasicSlider(lightAltModle.getSliderModel(-Math.PI / 2, Math.PI / 2)), "wrap");
// Sky
add(new StyledLabel(trans.get("PhotoSettingsConfig.lbl.sky"), Style.BOLD), "split, span, gapright para");
@@ -292,7 +307,7 @@ public class PhotoSettingsConfig extends JTabbedPane {
setSelectedItem(noSky);
}
}
- }, "wrap");
+ }, "spanx, wrap");
/// Image credit
final JLabel creditLabel = new JLabel(trans.get("PhotoSettingsConfig.lbl.skyCredit"));
@@ -304,7 +319,7 @@ public class PhotoSettingsConfig extends JTabbedPane {
credit.setOpaque(false);
credit.setFocusable(false);
credit.setFont(creditLabel.getFont());
- add(credit);
+ add(credit, "spanx");
final StateChangeListener skyChange = new StateChangeListener() {
@Override
@@ -332,22 +347,28 @@ public class PhotoSettingsConfig extends JTabbedPane {
/// Smoke
add(new JLabel(trans.get("PhotoSettingsConfig.lbl.smoke")));
BooleanModel smokeModel = new BooleanModel(p, "Smoke");
- add(new JCheckBox(smokeModel), "split 2, w 15");
+ add(new JCheckBox(smokeModel), "split 2, spanx");
- add(smokeColorButton, "pushx, left, wrap");
+ add(smokeColorButton, "wrap");
smokeModel.addEnableComponent(smokeColorButton);
/// Smoke opacity
add(new JLabel(trans.get("PhotoSettingsConfig.lbl.smokeOpacity")));
- DoubleModel smokeOpacityModel = new DoubleModel(p, "SmokeOpacity", 100, UnitGroup.UNITS_NONE, 0, 100);
+ DoubleModel smokeOpacityModel = new DoubleModel(p, "SmokeOpacity", UnitGroup.UNITS_RELATIVE, 0, 1);
EditableSpinner opacitySpinner = new EditableSpinner(smokeOpacityModel.getSpinnerModel());
- add(opacitySpinner, "wrap");
+ UnitSelector opacitySelector = new UnitSelector(smokeOpacityModel);
+ BasicSlider opacitySlider = new BasicSlider(smokeOpacityModel.getSliderModel(0, 1));
+ add(opacitySpinner, "growx");
+ add(opacitySelector);
+ add(opacitySlider, "wrap");
smokeModel.addEnableComponent(opacitySpinner);
+ smokeModel.addEnableComponent(opacitySelector);
+ smokeModel.addEnableComponent(opacitySlider);
/// Flame
add(new JLabel(trans.get("PhotoSettingsConfig.lbl.flame")));
BooleanModel fireModel = new BooleanModel(p, "Flame");
- add(new JCheckBox(fireModel), "split 2, w 15");
+ add(new JCheckBox(fireModel), "split 2, spanx");
add(flameColorButton, "wrap");
fireModel.addEnableComponent(flameColorButton);
@@ -357,8 +378,11 @@ public class PhotoSettingsConfig extends JTabbedPane {
DoubleModel flameAspectModel = new DoubleModel(p, "FlameAspectRatio", 100, UnitGroup.UNITS_NONE, 25,
250);
EditableSpinner flameAspectSpinner = new EditableSpinner(flameAspectModel.getSpinnerModel());
- add(flameAspectSpinner, "wrap");
+ BasicSlider flameAspectSlider = new BasicSlider(flameAspectModel.getSliderModel(25, 250));
+ add(flameAspectSpinner, "growx");
+ add(flameAspectSlider, "skip 1, wrap");
fireModel.addEnableComponent(flameAspectSpinner);
+ fireModel.addEnableComponent(flameAspectSlider);
/// Sparks
add(new JLabel(trans.get("PhotoSettingsConfig.lbl.sparks")));
@@ -369,23 +393,36 @@ public class PhotoSettingsConfig extends JTabbedPane {
/// Sparks concentration
add(new JLabel(trans.get("PhotoSettingsConfig.lbl.sparkConcentration")));
- DoubleModel sparkConcentrationModel = new DoubleModel(p, "SparkConcentration", 100,
- UnitGroup.UNITS_NONE, 0, 100);
+ DoubleModel sparkConcentrationModel = new DoubleModel(p, "SparkConcentration",
+ UnitGroup.UNITS_RELATIVE, 0, 1);
EditableSpinner sparkConcentrationSpinner = new EditableSpinner(sparkConcentrationModel.getSpinnerModel());
- add(sparkConcentrationSpinner, "wrap");
+ UnitSelector sparkConcentrationSelector = new UnitSelector(sparkConcentrationModel);
+ BasicSlider sparkConcentrationSlider = new BasicSlider(sparkConcentrationModel.getSliderModel(0, 1));
+ add(sparkConcentrationSpinner, "growx");
+ add(sparkConcentrationSelector);
+ add(sparkConcentrationSlider, "wrap");
sparksModel.addEnableComponent(sparkConcentrationSpinner);
+ sparksModel.addEnableComponent(sparkConcentrationSelector);
+ sparksModel.addEnableComponent(sparkConcentrationSlider);
/// Spark weight
add(new JLabel(trans.get("PhotoSettingsConfig.lbl.sparkWeight")));
- DoubleModel sparkWeightModel = new DoubleModel(p, "SparkWeight", 100, UnitGroup.UNITS_NONE, 0, 100);
+ DoubleModel sparkWeightModel = new DoubleModel(p, "SparkWeight", UnitGroup.UNITS_RELATIVE, 0, 1);
EditableSpinner sparkWeightSpinner = new EditableSpinner(sparkWeightModel.getSpinnerModel());
- add(sparkWeightSpinner, "wrap");
+ UnitSelector sparkWeightSelector = new UnitSelector(sparkWeightModel);
+ BasicSlider sparkWeightSlider = new BasicSlider(sparkWeightModel.getSliderModel(0, 1));
+ add(sparkWeightSpinner, "growx");
+ add(sparkWeightSelector);
+ add(sparkWeightSlider, "wrap");
sparksModel.addEnableComponent(sparkWeightSpinner);
+ sparksModel.addEnableComponent(sparkWeightSelector);
+ sparksModel.addEnableComponent(sparkWeightSlider);
/// Exhaust scale
add(new JLabel(trans.get("PhotoSettingsConfig.lbl.exhaustScale")));
DoubleModel exhaustScaleModel = new DoubleModel(p, "ExhaustScale", 100, UnitGroup.UNITS_NONE, 0, 1000);
- add(new EditableSpinner(exhaustScaleModel.getSpinnerModel()), "wrap");
+ add(new EditableSpinner(exhaustScaleModel.getSpinnerModel()), "growx");
+ add(new BasicSlider(exhaustScaleModel.getSliderModel(0, 1000)), "skip 1, wrap");
// Effects
add(new StyledLabel(trans.get("PhotoSettingsConfig.lbl.effects"), Style.BOLD), "split, span, gapright para");
diff --git a/swing/src/net/sf/openrocket/gui/figureelements/RocketInfo.java b/swing/src/net/sf/openrocket/gui/figureelements/RocketInfo.java
index 28bce901e..d3876b181 100644
--- a/swing/src/net/sf/openrocket/gui/figureelements/RocketInfo.java
+++ b/swing/src/net/sf/openrocket/gui/figureelements/RocketInfo.java
@@ -41,7 +41,7 @@ public class RocketInfo implements FigureElement {
private final Caret cpCaret = new CPCaret(0,0);
private final Caret cgCaret = new CGCaret(0,0);
- private final UnitGroup stabilityUnits;
+ private UnitGroup stabilityUnits;
private FlightConfiguration configuration;
private double cg = 0, cp = 0;
@@ -459,5 +459,6 @@ public class RocketInfo implements FigureElement {
public void setCurrentConfig(FlightConfiguration newConfig) {
this.configuration = newConfig;
+ this.stabilityUnits = UnitGroup.stabilityUnits(newConfig);
}
}
diff --git a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java
index b5304eba3..e9c10880e 100644
--- a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java
+++ b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java
@@ -117,7 +117,8 @@ public class BasicFrame extends JFrame {
* List of currently open frames. When the list goes empty
* it is time to exit the application.
*/
- private static final ArrayList frames = new ArrayList();
+ private static final List frames = new ArrayList();
+ private static BasicFrame startupFrame = null; // the frame that was created at startup
/**
@@ -417,18 +418,18 @@ public class BasicFrame extends JFrame {
button = new SelectColorButton(actions.getMoveDownAction());
panel.add(button, "sizegroup buttons, aligny 0%");
- button = new SelectColorButton(actions.getEditAction());
- button.setIcon(null);
+ button = new SelectColorButton();
+ RocketActions.tieActionToButtonNoIcon(button, actions.getEditAction());
button.setMnemonic(0);
panel.add(button, "sizegroup buttons, gaptop 20%");
- button = new SelectColorButton(actions.getDuplicateAction());
- button.setIcon(null);
+ button = new SelectColorButton();
+ RocketActions.tieActionToButtonNoIcon(button, actions.getDuplicateAction());
button.setMnemonic(0);
panel.add(button, "sizegroup buttons");
- button = new SelectColorButton(actions.getDeleteAction());
- button.setIcon(null);
+ button = new SelectColorButton();
+ RocketActions.tieActionToButtonNoIcon(button, actions.getDeleteAction());
button.setMnemonic(0);
panel.add(button, "sizegroup buttons");
@@ -489,6 +490,10 @@ public class BasicFrame extends JFrame {
return result;
}
+ public RocketPanel getRocketPanel() {
+ return rocketpanel;
+ }
+
/**
* Creates the menu for the window.
*/
@@ -836,19 +841,15 @@ public class BasicFrame extends JFrame {
}
- //// Analyze
- menu = new JMenu(trans.get("main.menu.analyze"));
- menu.setMnemonic(KeyEvent.VK_A);
-
- //// Analyzing the rocket
- menu.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.analyze.desc"));
+ // Tools
+ menu = new JMenu(trans.get("main.menu.tools"));
menubar.add(menu);
//// Component analysis
- item = new JMenuItem(trans.get("main.menu.analyze.componentAnalysis"), KeyEvent.VK_C);
+ item = new JMenuItem(trans.get("main.menu.tools.componentAnalysis"), KeyEvent.VK_C);
//// Analyze the rocket components separately
- item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.analyze.componentAnalysis.desc"));
+ item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.tools.componentAnalysis.desc"));
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
@@ -859,8 +860,8 @@ public class BasicFrame extends JFrame {
menu.add(item);
//// Optimize
- item = new JMenuItem(trans.get("main.menu.analyze.optimization"), KeyEvent.VK_O);
- item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.analyze.optimization.desc"));
+ item = new JMenuItem(trans.get("main.menu.tools.optimization"), KeyEvent.VK_O);
+ item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.tools.optimization.desc"));
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
@@ -875,8 +876,8 @@ public class BasicFrame extends JFrame {
menu.add(item);
//// Custom expressions
- item = new JMenuItem(trans.get("main.menu.analyze.customExpressions"), KeyEvent.VK_E);
- item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.analyze.customExpressions.desc"));
+ item = new JMenuItem(trans.get("main.menu.tools.customExpressions"), KeyEvent.VK_E);
+ item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.tools.customExpressions.desc"));
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
@@ -1231,6 +1232,19 @@ public class BasicFrame extends JFrame {
return menu;
}
+ /**
+ * Return the frame that was created at the application's startup.
+ */
+ public static BasicFrame getStartupFrame() {
+ return startupFrame;
+ }
+
+ /**
+ * Set the frame that is created at the application's startup.
+ */
+ public static void setStartupFrame(BasicFrame startupFrame) {
+ BasicFrame.startupFrame = startupFrame;
+ }
/**
* Select the tab on the main pane.
@@ -1273,7 +1287,7 @@ public class BasicFrame extends JFrame {
for (File file : files) {
log.info("Opening file: " + file);
- if (open(file, parent)) {
+ if (open(file, parent) != null) {
MRUDesignFile opts = MRUDesignFile.getInstance();
opts.addFile(file.getAbsolutePath());
}
@@ -1303,7 +1317,7 @@ public class BasicFrame extends JFrame {
for (File file : files) {
log.info("Opening file: " + file);
- if (open(file, this)) {
+ if (open(file, this) != null) {
MRUDesignFile opts = MRUDesignFile.getInstance();
opts.addFile(file.getAbsolutePath());
}
@@ -1373,9 +1387,9 @@ public class BasicFrame extends JFrame {
*
* @param file the file to open.
* @param parent the parent component for which a progress dialog is opened.
- * @return whether the file was successfully loaded and opened.
+ * @return the BasicFrame that was created, or null if not created successfully.
*/
- public static boolean open(File file, Window parent) {
+ public static BasicFrame open(File file, Window parent) {
OpenFileWorker worker = new OpenFileWorker(file);
return open(worker, file.getName(), parent, false);
}
@@ -1388,15 +1402,15 @@ public class BasicFrame extends JFrame {
* @param displayName the file name to display in dialogs.
* @param parent
* @param openRocketConfigDialog if true, will open the configuration dialog of the rocket. This is useful for examples.
- * @return
+ * @return the BasicFrame that was created, or null if not created successfully.
*/
- private static boolean open(OpenFileWorker worker, String displayName, Window parent, boolean openRocketConfigDialog) {
+ private static BasicFrame open(OpenFileWorker worker, String displayName, Window parent, boolean openRocketConfigDialog) {
//// Open the file in a Swing worker thread
log.info("Starting OpenFileWorker");
if (!SwingWorkerDialog.runWorker(parent, "Opening file", "Reading " + displayName + "...", worker)) {
// // User cancelled the operation
log.info("User cancelled the OpenFileWorker");
- return false;
+ return null;
}
//// Handle the document
@@ -1415,7 +1429,7 @@ public class BasicFrame extends JFrame {
JOptionPane.showMessageDialog(parent,
"File not found: " + displayName,
"Error opening file", JOptionPane.ERROR_MESSAGE);
- return false;
+ return null;
} else if (cause instanceof RocketLoadException) {
@@ -1424,7 +1438,7 @@ public class BasicFrame extends JFrame {
"Unable to open file '" + displayName + "': "
+ cause.getMessage(),
"Error opening file", JOptionPane.ERROR_MESSAGE);
- return false;
+ return null;
} else {
@@ -1468,7 +1482,7 @@ public class BasicFrame extends JFrame {
ComponentConfigDialog.showDialog(frame, doc, doc.getRocket());
}
- return true;
+ return frame;
}
@@ -1774,24 +1788,27 @@ public class BasicFrame extends JFrame {
/**
* Opens a new design file or the last design file, if set in the preferences.
* Can be used for reopening the application or opening it the first time.
+ * @return the BasicFrame that was created
*/
- public static void reopen() {
+ public static BasicFrame reopen() {
if (!Application.getPreferences().isAutoOpenLastDesignOnStartupEnabled()) {
- BasicFrame.newAction();
+ return BasicFrame.newAction();
} else {
String lastFile = MRUDesignFile.getInstance().getLastEditedDesignFile();
if (lastFile != null) {
log.info("Opening last design file: " + lastFile);
- if (!BasicFrame.open(new File(lastFile), null)) {
+ BasicFrame frame = BasicFrame.open(new File(lastFile), null);
+ if (frame == null) {
MRUDesignFile.getInstance().removeFile(lastFile);
- BasicFrame.newAction();
+ return BasicFrame.newAction();
}
else {
MRUDesignFile.getInstance().addFile(lastFile);
+ return frame;
}
}
else {
- BasicFrame.newAction();
+ return BasicFrame.newAction();
}
}
}
@@ -1799,8 +1816,9 @@ public class BasicFrame extends JFrame {
/**
* Open a new design window with a basic rocket+stage.
+ * @return the BasicFrame that was created
*/
- public static void newAction() {
+ public static BasicFrame newAction() {
log.info("New action initiated");
OpenRocketDocument doc = OpenRocketDocumentFactory.createNewRocket();
@@ -1808,6 +1826,7 @@ public class BasicFrame extends JFrame {
BasicFrame frame = new BasicFrame(doc);
frame.replaceable = true;
frame.setVisible(true);
+ return frame;
}
@@ -1871,6 +1890,13 @@ public class BasicFrame extends JFrame {
return null;
}
+ /**
+ * Return all BasicFrame instances
+ */
+ public static List getAllFrames() {
+ return frames;
+ }
+
/**
* Checks whether all the BasicFrames are closed.
* @return true if all the BasicFrames are closed, false if not
diff --git a/swing/src/net/sf/openrocket/gui/main/ComponentAddButtons.java b/swing/src/net/sf/openrocket/gui/main/ComponentAddButtons.java
index 8ed2338c9..0ff03e261 100644
--- a/swing/src/net/sf/openrocket/gui/main/ComponentAddButtons.java
+++ b/swing/src/net/sf/openrocket/gui/main/ComponentAddButtons.java
@@ -488,7 +488,7 @@ public class ComponentAddButtons extends JPanel implements Scrollable {
}
}
- ComponentConfigDialog.showDialog(parent, document, component, false);
+ ComponentConfigDialog.showDialog(parent, document, component, false, false);
}
}
diff --git a/swing/src/net/sf/openrocket/gui/main/MRUDesignFileAction.java b/swing/src/net/sf/openrocket/gui/main/MRUDesignFileAction.java
index 5582e4fca..3ff152d82 100644
--- a/swing/src/net/sf/openrocket/gui/main/MRUDesignFileAction.java
+++ b/swing/src/net/sf/openrocket/gui/main/MRUDesignFileAction.java
@@ -73,7 +73,7 @@ public final class MRUDesignFileAction extends JMenu {
@Override
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
- if (BasicFrame.open(new File(command), parent)) {
+ if (BasicFrame.open(new File(command), parent) != null) {
MRUDesignFile.getInstance().addFile(command);
}
else {
diff --git a/swing/src/net/sf/openrocket/gui/main/RocketActions.java b/swing/src/net/sf/openrocket/gui/main/RocketActions.java
index 86a3ea6bf..15ed1d597 100644
--- a/swing/src/net/sf/openrocket/gui/main/RocketActions.java
+++ b/swing/src/net/sf/openrocket/gui/main/RocketActions.java
@@ -12,6 +12,7 @@ import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Action;
+import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
@@ -189,6 +190,35 @@ public class RocketActions {
return moveDownAction;
}
+ /**
+ * Tie an action to a JButton, without using the icon or text of the action for the button.
+ *
+ * For any smartass that wants to know why you don't just initialize the JButton with the action and then set the
+ * icon to null and set the button text: this causes a bug where the text of the icon becomes much smaller than is intended.
+ *
+ * @param button button to tie the action to
+ * @param action action to tie to the button
+ * @param text text to display on the button
+ */
+ public static void tieActionToButtonNoIcon(JButton button, Action action, String text) {
+ button.setAction(action);
+ button.setIcon(null);
+ button.setText(text);
+ }
+
+ /**
+ * Tie an action to a JButton, without using the icon of the action for the button.
+ *
+ * For any smartass that wants to know why you don't just initialize the JButton with the action and then set the
+ * icon to null: this causes a bug where the text of the icon becomes much smaller than is intended.
+ *
+ * @param button button to tie the action to
+ * @param action action to tie to the button
+ */
+ public static void tieActionToButtonNoIcon(JButton button, Action action) {
+ button.setAction(action);
+ button.setIcon(null);
+ }
//////// Helper methods for the actions
diff --git a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java
index f8ac84ec5..cd46a4880 100644
--- a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java
+++ b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java
@@ -11,6 +11,7 @@ import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
@@ -19,6 +20,8 @@ import java.util.Arrays;
import java.util.Comparator;
import javax.swing.AbstractAction;
+import javax.swing.ActionMap;
+import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
@@ -98,366 +101,91 @@ public class SimulationPanel extends JPanel {
private final JButton plotButton;
private final JPopupMenu pm;
+ private final SimulationAction editSimulationAction;
+ private final SimulationAction runSimulationAction;
+ private final SimulationAction plotSimulationAction;
+ private final SimulationAction duplicateSimulationAction;
+ private final SimulationAction deleteSimulationAction;
+
public SimulationPanel(OpenRocketDocument doc) {
super(new MigLayout("fill", "[grow][][][][][][grow]"));
this.document = doc;
+ // Simulation actions
+ SimulationAction newSimulationAction = new NewSimulationAction();
+ editSimulationAction = new EditSimulationAction();
+ runSimulationAction = new RunSimulationAction();
+ plotSimulationAction = new PlotSimulationAction();
+ duplicateSimulationAction = new DuplicateSimulationAction();
+ deleteSimulationAction = new DeleteSimulationAction();
- //////// The simulation action buttons
+ //////// The simulation action buttons ////////
//// New simulation button
- {
- JButton button = new SelectColorButton(trans.get("simpanel.but.newsimulation"));
- //// Add a new simulation
- button.setToolTipText(trans.get("simpanel.but.ttip.newsimulation"));
- button.addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- Simulation sim = new Simulation(document.getRocket());
- sim.setName(document.getNextSimulationName());
-
- int n = document.getSimulationCount();
- document.addSimulation(sim);
- simulationTableModel.fireTableDataChanged();
- simulationTable.clearSelection();
- simulationTable.addRowSelectionInterval(n, n);
-
- openDialog(false, sim);
- }
- });
- this.add(button, "skip 1, gapright para");
- }
+ JButton newButton = new SelectColorButton();
+ RocketActions.tieActionToButtonNoIcon(newButton, newSimulationAction, trans.get("simpanel.but.newsimulation"));
+ newButton.setToolTipText(trans.get("simpanel.but.ttip.newsimulation"));
+ this.add(newButton, "skip 1, gapright para");
//// Edit simulation button
- editButton = new SelectColorButton(trans.get("simpanel.but.editsimulation"));
- //// Edit the selected simulation
+ editButton = new SelectColorButton();
+ RocketActions.tieActionToButtonNoIcon(editButton, editSimulationAction, trans.get("simpanel.but.editsimulation"));
editButton.setToolTipText(trans.get("simpanel.but.ttip.editsim"));
- editButton.addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- editSimulation();
- }
- });
this.add(editButton, "gapright para");
//// Run simulations
- runButton = new SelectColorButton(trans.get("simpanel.but.runsimulations"));
- //// Re-run the selected simulations
+ runButton = new SelectColorButton();
+ RocketActions.tieActionToButtonNoIcon(runButton, runSimulationAction, trans.get("simpanel.but.runsimulations"));
runButton.setToolTipText(trans.get("simpanel.but.ttip.runsimu"));
- runButton.addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- runSimulation();
- }
- });
this.add(runButton, "gapright para");
//// Delete simulations button
- deleteButton = new SelectColorButton(trans.get("simpanel.but.deletesimulations"));
- //// Delete the selected simulations
+ deleteButton = new SelectColorButton();
+ RocketActions.tieActionToButtonNoIcon(deleteButton, deleteSimulationAction, trans.get("simpanel.but.deletesimulations"));
deleteButton.setToolTipText(trans.get("simpanel.but.ttip.deletesim"));
- deleteButton.addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- deleteSimulation();
- }
- });
this.add(deleteButton, "gapright para");
//// Plot / export button
- plotButton = new SelectColorButton(trans.get("simpanel.but.plotexport"));
- // button = new SelectColorButton("Plot flight");
- plotButton.addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- plotSimulation();
- }
- });
+ plotButton = new SelectColorButton();
+ RocketActions.tieActionToButtonNoIcon(plotButton, plotSimulationAction, trans.get("simpanel.but.plotexport"));
this.add(plotButton, "wrap para");
//////// The simulation table
+ simulationTableModel = new SimulationTableModel();
- simulationTableModel = new ColumnTableModel(
-
- //// Status and warning column
- new Column("") {
- private JLabel label = null;
-
- @Override
- public Object getValueAt(int row) {
- if (row < 0 || row >= document.getSimulationCount())
- return null;
-
- // Initialize the label
- if (label == null) {
- label = new StyledLabel(2f);
- label.setIconTextGap(1);
- // label.setFont(label.getFont().deriveFont(Font.BOLD));
- }
-
- // Set simulation status icon
- Simulation.Status status = document.getSimulation(row).getStatus();
- label.setIcon(Icons.SIMULATION_STATUS_ICON_MAP.get(status));
-
-
- // Set warning marker
- if (status == Simulation.Status.NOT_SIMULATED ||
- status == Simulation.Status.EXTERNAL) {
-
- label.setText("");
-
- } else {
-
- WarningSet w = document.getSimulation(row).getSimulatedWarnings();
- if (w == null) {
- label.setText("");
- } else if (w.isEmpty()) {
- label.setForeground(OK_COLOR);
- label.setText(OK_TEXT);
- } else {
- label.setForeground(WARNING_COLOR);
- label.setText(WARNING_TEXT);
- }
- }
-
- return label;
- }
-
- @Override
- public int getExactWidth() {
- return 36;
- }
-
- @Override
- public Class> getColumnClass() {
- return JLabel.class;
- }
- },
-
- //// Simulation name
- //// Name
- new Column(trans.get("simpanel.col.Name")) {
- @Override
- public Object getValueAt(int row) {
- if (row < 0 || row >= document.getSimulationCount())
- return null;
- return document.getSimulation(row).getName();
- }
-
- @Override
- public int getDefaultWidth() {
- return 125;
- }
-
- @Override
- public Comparator getComparator() {
- return new AlphanumComparator();
- }
- },
-
- //// Simulation configuration
- new Column(trans.get("simpanel.col.Configuration")) {
- @Override
- public Object getValueAt(int row) {
- if (row < 0 || row >= document.getSimulationCount()){
- return null;
- }
-
- Rocket rkt = document.getRocket();
- FlightConfigurationId fcid = document.getSimulation(row).getId();
- return descriptor.format( rkt, fcid);
- }
-
- @Override
- public int getDefaultWidth() {
- return 125;
- }
- },
-
- //// Launch rod velocity
- new ValueColumn(trans.get("simpanel.col.Velocityoffrod"), UnitGroup.UNITS_VELOCITY) {
- @Override
- public Double valueAt(int row) {
- if (row < 0 || row >= document.getSimulationCount())
- return null;
-
- FlightData data = document.getSimulation(row).getSimulatedData();
- if (data == null)
- return null;
-
- return data.getLaunchRodVelocity();
- }
-
- },
-
- //// Apogee
- new ValueColumn(trans.get("simpanel.col.Apogee"), UnitGroup.UNITS_DISTANCE) {
- @Override
- public Double valueAt(int row) {
- if (row < 0 || row >= document.getSimulationCount())
- return null;
-
- FlightData data = document.getSimulation(row).getSimulatedData();
- if (data == null)
- return null;
-
- return data.getMaxAltitude();
- }
- },
-
- //// Velocity at deployment
- new ValueColumn(trans.get("simpanel.col.Velocityatdeploy"), UnitGroup.UNITS_VELOCITY) {
- @Override
- public Double valueAt(int row) {
- if (row < 0 || row >= document.getSimulationCount())
- return null;
-
- FlightData data = document.getSimulation(row).getSimulatedData();
- if (data == null)
- return null;
-
- return data.getDeploymentVelocity();
- }
- },
-
- //// Deployment Time from Apogee
- new ValueColumn(trans.get("simpanel.col.OptimumCoastTime"),
- trans.get("simpanel.col.OptimumCoastTime.ttip"),
- UnitGroup.UNITS_SHORT_TIME) {
- @Override
- public Double valueAt(int row) {
- if (row < 0 || row >= document.getSimulationCount())
- return null;
-
- FlightData data = document.getSimulation(row).getSimulatedData();
- if (data == null || data.getBranchCount() == 0)
- return null;
-
- double val = data.getBranch(0).getOptimumDelay();
- if ( Double.isNaN(val) ) {
- return null;
- }
- return val;
- }
- },
-
- //// Maximum velocity
- new ValueColumn(trans.get("simpanel.col.Maxvelocity"), UnitGroup.UNITS_VELOCITY) {
- @Override
- public Double valueAt(int row) {
- if (row < 0 || row >= document.getSimulationCount())
- return null;
-
- FlightData data = document.getSimulation(row).getSimulatedData();
- if (data == null)
- return null;
-
- return data.getMaxVelocity();
- }
- },
-
- //// Maximum acceleration
- new ValueColumn(trans.get("simpanel.col.Maxacceleration"), UnitGroup.UNITS_ACCELERATION) {
- @Override
- public Double valueAt(int row) {
- if (row < 0 || row >= document.getSimulationCount())
- return null;
-
- FlightData data = document.getSimulation(row).getSimulatedData();
- if (data == null)
- return null;
-
- return data.getMaxAcceleration();
- }
- },
-
- //// Time to apogee
- new ValueColumn(trans.get("simpanel.col.Timetoapogee"), UnitGroup.UNITS_FLIGHT_TIME) {
- @Override
- public Double valueAt(int row) {
- if (row < 0 || row >= document.getSimulationCount())
- return null;
-
- FlightData data = document.getSimulation(row).getSimulatedData();
- if (data == null)
- return null;
-
- return data.getTimeToApogee();
- }
- },
-
- //// Flight time
- new ValueColumn(trans.get("simpanel.col.Flighttime"), UnitGroup.UNITS_FLIGHT_TIME) {
- @Override
- public Double valueAt(int row) {
- if (row < 0 || row >= document.getSimulationCount())
- return null;
-
- FlightData data = document.getSimulation(row).getSimulatedData();
- if (data == null)
- return null;
-
- return data.getFlightTime();
- }
- },
-
- //// Ground hit velocity
- new ValueColumn(trans.get("simpanel.col.Groundhitvelocity"), UnitGroup.UNITS_VELOCITY) {
- @Override
- public Double valueAt(int row) {
- if (row < 0 || row >= document.getSimulationCount())
- return null;
-
- FlightData data = document.getSimulation(row).getSimulatedData();
- if (data == null)
- return null;
-
- return data.getGroundHitVelocity();
- }
- }
-
- ) {
-
- private static final long serialVersionUID = 8686456963492628476L;
-
- @Override
- public int getRowCount() {
- return document.getSimulationCount();
- }
- };
-
- // Override processKeyBinding so that the JTable does not catch
- // key bindings used in menu accelerators
simulationTable = new ColumnTable(simulationTableModel) {
-
private static final long serialVersionUID = -5799340181229735630L;
-
- @Override
- protected boolean processKeyBinding(KeyStroke ks,
- KeyEvent e,
- int condition,
- boolean pressed) {
- return false;
- }
};
ColumnTableRowSorter simulationTableSorter = new ColumnTableRowSorter(simulationTableModel);
simulationTable.setRowSorter(simulationTableSorter);
simulationTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
simulationTable.setDefaultRenderer(Object.class, new JLabelRenderer());
simulationTableModel.setColumnWidths(simulationTable.getColumnModel());
-
- pm = new JPopupMenu();
- pm.add(new EditSimulationAction());
- pm.add(new DuplicateSimulationAction());
- pm.add(new DeleteSimulationAction());
- pm.addSeparator();
- pm.add(new RunSimulationAction());
- pm.add(new PlotSimulationAction());
+ // Context menu
+ pm = new JPopupMenu();
+ pm.add(editSimulationAction);
+ pm.add(duplicateSimulationAction);
+ pm.add(deleteSimulationAction);
+ pm.addSeparator();
+ pm.add(runSimulationAction);
+ pm.add(plotSimulationAction);
+
+ // The normal left/right and tab/shift-tab key action traverses each cell/column of the table instead of going to the next row.
+ InputMap im = simulationTable.getInputMap();
+ im.put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), "Action.NextRowCycle");
+ im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "Action.NextRow");
+ im.put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_DOWN_MASK), "Action.PreviousRowCycle");
+ im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "Action.PreviousRow");
+ ActionMap am = simulationTable.getActionMap();
+ am.put("Action.NextRow", new NextRowAction(simulationTable, false));
+ am.put("Action.NextRowCycle", new NextRowAction(simulationTable, true));
+ am.put("Action.PreviousRow", new PreviousRowAction(simulationTable, false));
+ am.put("Action.PreviousRowCycle", new PreviousRowAction(simulationTable, true));
// Mouse listener to act on double-clicks
simulationTable.addMouseListener(new MouseAdapter() {
@@ -496,11 +224,14 @@ public class SimulationPanel extends JPanel {
});
simulationTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
- private int previousRow = -1;
+ private int previousSelectedRow = -1;
+ private int previousSelectedRowCount = 0;
public void valueChanged(ListSelectionEvent event) {
- if (simulationTable.getSelectedRow() != previousRow) {
+ if ((simulationTable.getSelectedRow() != previousSelectedRow) ||
+ (simulationTable.getSelectedRowCount() != previousSelectedRowCount)) {
updateButtonStates();
- previousRow = simulationTable.getSelectedRow();
+ previousSelectedRow = simulationTable.getSelectedRow();
+ previousSelectedRowCount = simulationTable.getSelectedRowCount();
}
}
});
@@ -532,6 +263,19 @@ public class SimulationPanel extends JPanel {
updateButtonStates();
}
+ private void newSimulation() {
+ Simulation sim = new Simulation(document.getRocket());
+ sim.setName(document.getNextSimulationName());
+
+ int n = document.getSimulationCount();
+ document.addSimulation(sim);
+ simulationTableModel.fireTableDataChanged();
+ simulationTable.clearSelection();
+ simulationTable.addRowSelectionInterval(n, n);
+
+ openDialog(false, sim);
+ }
+
private void plotSimulation() {
int selected = simulationTable.getSelectedRow();
if (selected < 0) {
@@ -690,23 +434,11 @@ public class SimulationPanel extends JPanel {
}
private void updateButtonStates() {
- int[] selection = simulationTable.getSelectedRows();
- if (selection.length == 0) {
- editButton.setEnabled(false);
- runButton.setEnabled(false);
- deleteButton.setEnabled(false);
- plotButton.setEnabled(false);
- } else {
- if (selection.length > 1) {
- plotButton.setEnabled(false);
- } else {
- plotButton.setEnabled(true);
- }
- editButton.setEnabled(true);
- runButton.setEnabled(true);
- deleteButton.setEnabled(true);
- }
-
+ editSimulationAction.updateEnabledState();
+ deleteSimulationAction.updateEnabledState();
+ runSimulationAction.updateEnabledState();
+ plotSimulationAction.updateEnabledState();
+ duplicateSimulationAction.updateEnabledState();
}
/// when the simulation tab is selected this run outdated simulated if appropriate.
@@ -778,7 +510,31 @@ public class SimulationPanel extends JPanel {
}
}
- class EditSimulationAction extends AbstractAction {
+ private abstract static class SimulationAction extends AbstractAction {
+ private static final long serialVersionUID = 1L;
+
+ public abstract void updateEnabledState();
+ }
+
+ class NewSimulationAction extends SimulationAction {
+ public NewSimulationAction() {
+ putValue(NAME, trans.get("simpanel.but.newsimulation"));
+ this.putValue(MNEMONIC_KEY, KeyEvent.VK_N);
+ this.putValue(SMALL_ICON, Icons.FILE_NEW);
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ newSimulation();
+ }
+
+ @Override
+ public void updateEnabledState() {
+ setEnabled(true);
+ }
+ }
+
+ class EditSimulationAction extends SimulationAction {
public EditSimulationAction() {
putValue(NAME, trans.get("simpanel.pop.edit"));
this.putValue(MNEMONIC_KEY, KeyEvent.VK_E);
@@ -790,9 +546,14 @@ public class SimulationPanel extends JPanel {
public void actionPerformed(ActionEvent e) {
editSimulation();
}
+
+ @Override
+ public void updateEnabledState() {
+ setEnabled(simulationTable.getSelectedRowCount() == 1);
+ }
}
- class RunSimulationAction extends AbstractAction {
+ class RunSimulationAction extends SimulationAction {
public RunSimulationAction() {
putValue(NAME, trans.get("simpanel.pop.run"));
putValue(SMALL_ICON, Icons.SIM_RUN);
@@ -802,9 +563,14 @@ public class SimulationPanel extends JPanel {
public void actionPerformed(ActionEvent e) {
runSimulation();
}
+
+ @Override
+ public void updateEnabledState() {
+ setEnabled(simulationTable.getSelectedRowCount() > 0);
+ }
}
- class DeleteSimulationAction extends AbstractAction {
+ class DeleteSimulationAction extends SimulationAction {
public DeleteSimulationAction() {
putValue(NAME, trans.get("simpanel.pop.delete"));
putValue(MNEMONIC_KEY, KeyEvent.VK_D);
@@ -816,9 +582,14 @@ public class SimulationPanel extends JPanel {
public void actionPerformed(ActionEvent e) {
deleteSimulation();
}
+
+ @Override
+ public void updateEnabledState() {
+ setEnabled(simulationTable.getSelectedRowCount() > 0);
+ }
}
- class PlotSimulationAction extends AbstractAction {
+ class PlotSimulationAction extends SimulationAction {
public PlotSimulationAction() {
putValue(NAME, trans.get("simpanel.pop.plot"));
putValue(SMALL_ICON, Icons.SIM_PLOT);
@@ -828,9 +599,14 @@ public class SimulationPanel extends JPanel {
public void actionPerformed(ActionEvent e) {
plotSimulation();
}
+
+ @Override
+ public void updateEnabledState() {
+ setEnabled(simulationTable.getSelectedRowCount() == 1);
+ }
}
- class DuplicateSimulationAction extends AbstractAction {
+ class DuplicateSimulationAction extends SimulationAction {
public DuplicateSimulationAction() {
putValue(NAME, trans.get("simpanel.pop.duplicate"));
putValue(MNEMONIC_KEY, KeyEvent.VK_D);
@@ -842,7 +618,12 @@ public class SimulationPanel extends JPanel {
public void actionPerformed(ActionEvent e) {
duplicateSimulation();
}
- }
+
+ @Override
+ public void updateEnabledState() {
+ setEnabled(simulationTable.getSelectedRowCount() > 0);
+ }
+ }
public static class CellTransferable implements Transferable {
@@ -955,6 +736,318 @@ public class SimulationPanel extends JPanel {
return tip;
}
+ }
+
+ private class SimulationTableModel extends ColumnTableModel {
+ private static final long serialVersionUID = 8686456963492628476L;
+
+ public SimulationTableModel() {
+ super(
+ //// Status and warning column
+ new Column("") {
+ private JLabel label = null;
+
+ @Override
+ public Object getValueAt(int row) {
+ if (row < 0 || row >= document.getSimulationCount())
+ return null;
+
+ // Initialize the label
+ if (label == null) {
+ label = new StyledLabel(2f);
+ label.setIconTextGap(1);
+ // label.setFont(label.getFont().deriveFont(Font.BOLD));
+ }
+
+ // Set simulation status icon
+ Simulation.Status status = document.getSimulation(row).getStatus();
+ label.setIcon(Icons.SIMULATION_STATUS_ICON_MAP.get(status));
+
+
+ // Set warning marker
+ if (status == Simulation.Status.NOT_SIMULATED ||
+ status == Simulation.Status.EXTERNAL) {
+
+ label.setText("");
+
+ } else {
+
+ WarningSet w = document.getSimulation(row).getSimulatedWarnings();
+ if (w == null) {
+ label.setText("");
+ } else if (w.isEmpty()) {
+ label.setForeground(OK_COLOR);
+ label.setText(OK_TEXT);
+ } else {
+ label.setForeground(WARNING_COLOR);
+ label.setText(WARNING_TEXT);
+ }
+ }
+
+ return label;
+ }
+
+ @Override
+ public int getExactWidth() {
+ return 36;
+ }
+
+ @Override
+ public Class> getColumnClass() {
+ return JLabel.class;
+ }
+ },
+
+ //// Simulation name
+ //// Name
+ new Column(trans.get("simpanel.col.Name")) {
+ @Override
+ public Object getValueAt(int row) {
+ if (row < 0 || row >= document.getSimulationCount())
+ return null;
+ return document.getSimulation(row).getName();
+ }
+
+ @Override
+ public int getDefaultWidth() {
+ return 125;
+ }
+
+ @Override
+ public Comparator getComparator() {
+ return new AlphanumComparator();
+ }
+ },
+
+ //// Simulation configuration
+ new Column(trans.get("simpanel.col.Configuration")) {
+ @Override
+ public Object getValueAt(int row) {
+ if (row < 0 || row >= document.getSimulationCount()) {
+ return null;
+ }
+
+ Rocket rkt = document.getRocket();
+ FlightConfigurationId fcid = document.getSimulation(row).getId();
+ return descriptor.format(rkt, fcid);
+ }
+
+ @Override
+ public int getDefaultWidth() {
+ return 125;
+ }
+ },
+
+ //// Launch rod velocity
+ new ValueColumn(trans.get("simpanel.col.Velocityoffrod"), UnitGroup.UNITS_VELOCITY) {
+ @Override
+ public Double valueAt(int row) {
+ if (row < 0 || row >= document.getSimulationCount())
+ return null;
+
+ FlightData data = document.getSimulation(row).getSimulatedData();
+ if (data == null)
+ return null;
+
+ return data.getLaunchRodVelocity();
+ }
+
+ },
+
+ //// Apogee
+ new ValueColumn(trans.get("simpanel.col.Apogee"), UnitGroup.UNITS_DISTANCE) {
+ @Override
+ public Double valueAt(int row) {
+ if (row < 0 || row >= document.getSimulationCount())
+ return null;
+
+ FlightData data = document.getSimulation(row).getSimulatedData();
+ if (data == null)
+ return null;
+
+ return data.getMaxAltitude();
+ }
+ },
+
+ //// Velocity at deployment
+ new ValueColumn(trans.get("simpanel.col.Velocityatdeploy"), UnitGroup.UNITS_VELOCITY) {
+ @Override
+ public Double valueAt(int row) {
+ if (row < 0 || row >= document.getSimulationCount())
+ return null;
+
+ FlightData data = document.getSimulation(row).getSimulatedData();
+ if (data == null)
+ return null;
+
+ return data.getDeploymentVelocity();
+ }
+ },
+
+ //// Deployment Time from Apogee
+ new ValueColumn(trans.get("simpanel.col.OptimumCoastTime"),
+ trans.get("simpanel.col.OptimumCoastTime.ttip"), UnitGroup.UNITS_SHORT_TIME) {
+ @Override
+ public Double valueAt(int row) {
+ if (row < 0 || row >= document.getSimulationCount())
+ return null;
+
+ FlightData data = document.getSimulation(row).getSimulatedData();
+ if (data == null || data.getBranchCount() == 0)
+ return null;
+
+ double val = data.getBranch(0).getOptimumDelay();
+ if (Double.isNaN(val)) {
+ return null;
+ }
+ return val;
+ }
+ },
+
+ //// Maximum velocity
+ new ValueColumn(trans.get("simpanel.col.Maxvelocity"), UnitGroup.UNITS_VELOCITY) {
+ @Override
+ public Double valueAt(int row) {
+ if (row < 0 || row >= document.getSimulationCount())
+ return null;
+
+ FlightData data = document.getSimulation(row).getSimulatedData();
+ if (data == null)
+ return null;
+
+ return data.getMaxVelocity();
+ }
+ },
+
+ //// Maximum acceleration
+ new ValueColumn(trans.get("simpanel.col.Maxacceleration"), UnitGroup.UNITS_ACCELERATION) {
+ @Override
+ public Double valueAt(int row) {
+ if (row < 0 || row >= document.getSimulationCount())
+ return null;
+
+ FlightData data = document.getSimulation(row).getSimulatedData();
+ if (data == null)
+ return null;
+
+ return data.getMaxAcceleration();
+ }
+ },
+
+ //// Time to apogee
+ new ValueColumn(trans.get("simpanel.col.Timetoapogee"), UnitGroup.UNITS_FLIGHT_TIME) {
+ @Override
+ public Double valueAt(int row) {
+ if (row < 0 || row >= document.getSimulationCount())
+ return null;
+
+ FlightData data = document.getSimulation(row).getSimulatedData();
+ if (data == null)
+ return null;
+
+ return data.getTimeToApogee();
+ }
+ },
+
+ //// Flight time
+ new ValueColumn(trans.get("simpanel.col.Flighttime"), UnitGroup.UNITS_FLIGHT_TIME) {
+ @Override
+ public Double valueAt(int row) {
+ if (row < 0 || row >= document.getSimulationCount())
+ return null;
+
+ FlightData data = document.getSimulation(row).getSimulatedData();
+ if (data == null)
+ return null;
+
+ return data.getFlightTime();
+ }
+ },
+
+ //// Ground hit velocity
+ new ValueColumn(trans.get("simpanel.col.Groundhitvelocity"), UnitGroup.UNITS_VELOCITY) {
+ @Override
+ public Double valueAt(int row) {
+ if (row < 0 || row >= document.getSimulationCount())
+ return null;
+
+ FlightData data = document.getSimulation(row).getSimulatedData();
+ if (data == null)
+ return null;
+
+ return data.getGroundHitVelocity();
+ }
+ }
+ );
+ }
+
+ @Override
+ public int getRowCount() {
+ return document.getSimulationCount();
+ }
+ }
+
+ private static class NextRowAction extends AbstractAction {
+ private final JTable table;
+ private final boolean cycle;
+
+ /**
+ * Action for cycling through the next row of the table.
+ * @param table table for which the action is intended
+ * @param cycle whether to go back to the first row if the end is reached.
+ */
+ public NextRowAction(JTable table, boolean cycle) {
+ this.table = table;
+ this.cycle = cycle;
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ int nextRow = table.getSelectedRow() + 1;
+
+ if (nextRow >= table.getRowCount()) {
+ if (cycle) {
+ nextRow = 0;
+ } else {
+ return;
+ }
+ }
+
+ table.getSelectionModel().setSelectionInterval(nextRow, nextRow);
+ table.getColumnModel().getSelectionModel().setSelectionInterval(0, 0);
+ }
+
+ }
+
+ private static class PreviousRowAction extends AbstractAction {
+ private final JTable table;
+ private final boolean cycle;
+
+ /**
+ * Action for cycling through the previous row of the table.
+ * @param table table for which the action is intended
+ * @param cycle whether to go back to the last row if the current row is the first one of the table.
+ */
+ public PreviousRowAction(JTable table, boolean cycle) {
+ this.table = table;
+ this.cycle = cycle;
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ int nextRow = table.getSelectedRow() - 1;
+
+ if (nextRow < 0) {
+ if (cycle) {
+ nextRow = table.getRowCount() - 1;
+ } else {
+ return;
+ }
+ }
+
+ table.getSelectionModel().setSelectionInterval(nextRow, nextRow);
+ table.getColumnModel().getSelectionModel().setSelectionInterval(0, 0);
+ }
}
}
diff --git a/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTree.java b/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTree.java
index a02e626f9..a638ba8d0 100644
--- a/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTree.java
+++ b/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTree.java
@@ -6,6 +6,9 @@ import javax.swing.ToolTipManager;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.gui.components.BasicTree;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+
@SuppressWarnings("serial")
public class ComponentTree extends BasicTree {
@@ -13,7 +16,21 @@ public class ComponentTree extends BasicTree {
public ComponentTree(OpenRocketDocument document) {
super();
this.setModel(new ComponentTreeModel(document.getRocket(), this));
-
+
+ addKeyListener(new KeyListener() {
+ @Override
+ public void keyTyped(KeyEvent e) { }
+
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if ((e.getKeyCode() == KeyEvent.VK_A) && ((e.getModifiersEx() & KeyEvent.META_DOWN_MASK) != 0)) {
+ setSelectionInterval(1, getRowCount()); // Don't select the rocket (row 0)
+ }
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) { }
+ });
this.setCellRenderer(new ComponentTreeRenderer());
this.setDragEnabled(true);
diff --git a/swing/src/net/sf/openrocket/gui/preset/PresetEditorDialog.java b/swing/src/net/sf/openrocket/gui/preset/PresetEditorDialog.java
index 1aad92e50..77b9b687c 100644
--- a/swing/src/net/sf/openrocket/gui/preset/PresetEditorDialog.java
+++ b/swing/src/net/sf/openrocket/gui/preset/PresetEditorDialog.java
@@ -72,7 +72,7 @@ public class PresetEditorDialog extends JDialog implements ItemListener {
private static final long serialVersionUID = -3298642844886682536L;
- private static Translator trans = Application.getTranslator();
+ private static final Translator trans = Application.getTranslator();
private static final Logger log = LoggerFactory.getLogger(PresetEditorDialog.class);
@@ -82,138 +82,137 @@ public class PresetEditorDialog extends JDialog implements ItemListener {
* Input of non-negative decimals.
*/
final PresetInputVerifier NON_NEGATIVE_INTEGER = new PresetInputVerifier(Pattern.compile(NON_NEGATIVE_INTEGER_FIELD));
-
- private final JPanel contentPanel = new JPanel();
- private DeselectableComboBox typeCombo;
- private JTextField mfgTextField;
- private MaterialChooser materialChooser;
+
+ private final DeselectableComboBox typeCombo;
+ private final JTextField mfgTextField;
+ private final MaterialChooser materialChooser;
private MaterialHolder holder = null;
- private JTextField ncPartNoTextField;
- private JTextField ncDescTextField;
- private DoubleModel ncLength;
- private JCheckBox ncFilledCB;
- private JComboBox ncShapeCB;
- private DoubleModel ncAftDia;
- private DoubleModel ncAftShoulderDia;
- private DoubleModel ncAftShoulderLen;
- private DoubleModel ncMass;
+ private final JTextField ncPartNoTextField;
+ private final JTextField ncDescTextField;
+ private final DoubleModel ncLength;
+ private final JCheckBox ncFilledCB;
+ private final JComboBox ncShapeCB;
+ private final DoubleModel ncAftDia;
+ private final DoubleModel ncAftShoulderDia;
+ private final DoubleModel ncAftShoulderLen;
+ private final DoubleModel ncMass;
private ImageIcon ncImage;
- private JButton ncImageBtn;
+ private final JButton ncImageBtn;
- private JTextField trPartNoTextField;
- private JTextField trDescTextField;
- private DoubleModel trLength;
- private DoubleModel trAftDia;
- private DoubleModel trAftShoulderDia;
- private DoubleModel trAftShoulderLen;
- private DoubleModel trForeDia;
- private DoubleModel trForeShoulderDia;
- private DoubleModel trForeShoulderLen;
+ private final JTextField trPartNoTextField;
+ private final JTextField trDescTextField;
+ private final DoubleModel trLength;
+ private final DoubleModel trAftDia;
+ private final DoubleModel trAftShoulderDia;
+ private final DoubleModel trAftShoulderLen;
+ private final DoubleModel trForeDia;
+ private final DoubleModel trForeShoulderDia;
+ private final DoubleModel trForeShoulderLen;
private DoubleModel trMass;
private ImageIcon trImage;
- private JCheckBox trFilledCB;
- private JComboBox trShapeCB;
- private JButton trImageBtn;
+ private final JCheckBox trFilledCB;
+ private final JComboBox trShapeCB;
+ private final JButton trImageBtn;
- private JTextField btPartNoTextField;
- private JTextField btDescTextField;
- private DoubleModel btMass;
- private DoubleModel btInnerDia;
- private DoubleModel btOuterDia;
- private DoubleModel btLength;
+ private final JTextField btPartNoTextField;
+ private final JTextField btDescTextField;
+ private final DoubleModel btMass;
+ private final DoubleModel btInnerDia;
+ private final DoubleModel btOuterDia;
+ private final DoubleModel btLength;
private ImageIcon btImage;
- private JButton btImageBtn;
+ private final JButton btImageBtn;
- private JTextField tcPartNoTextField;
- private JTextField tcDescTextField;
- private DoubleModel tcMass;
- private DoubleModel tcInnerDia;
- private DoubleModel tcOuterDia;
- private DoubleModel tcLength;
+ private final JTextField tcPartNoTextField;
+ private final JTextField tcDescTextField;
+ private final DoubleModel tcMass;
+ private final DoubleModel tcInnerDia;
+ private final DoubleModel tcOuterDia;
+ private final DoubleModel tcLength;
private ImageIcon tcImage;
- private JButton tcImageBtn;
+ private final JButton tcImageBtn;
- private JTextField bhPartNoTextField;
- private JTextField bhDescTextField;
- private DoubleModel bhOuterDia;
- private DoubleModel bhLength;
- private DoubleModel bhMass;
+ private final JTextField bhPartNoTextField;
+ private final JTextField bhDescTextField;
+ private final DoubleModel bhOuterDia;
+ private final DoubleModel bhLength;
+ private final DoubleModel bhMass;
private ImageIcon bhImage;
- private JButton bhImageBtn;
+ private final JButton bhImageBtn;
- private JTextField crPartNoTextField;
- private JTextField crDescTextField;
- private DoubleModel crOuterDia;
- private DoubleModel crInnerDia;
- private DoubleModel crThickness;
- private DoubleModel crMass;
+ private final JTextField crPartNoTextField;
+ private final JTextField crDescTextField;
+ private final DoubleModel crOuterDia;
+ private final DoubleModel crInnerDia;
+ private final DoubleModel crThickness;
+ private final DoubleModel crMass;
private ImageIcon crImage;
- private JButton crImageBtn;
+ private final JButton crImageBtn;
- private JTextField ebPartNoTextField;
- private JTextField ebDescTextField;
- private DoubleModel ebOuterDia;
- private DoubleModel ebInnerDia;
- private DoubleModel ebThickness;
- private DoubleModel ebMass;
+ private final JTextField ebPartNoTextField;
+ private final JTextField ebDescTextField;
+ private final DoubleModel ebOuterDia;
+ private final DoubleModel ebInnerDia;
+ private final DoubleModel ebThickness;
+ private final DoubleModel ebMass;
private ImageIcon ebImage;
- private JButton ebImageBtn;
+ private final JButton ebImageBtn;
- private JTextField llPartNoTextField;
- private JTextField llDescTextField;
- private DoubleModel llOuterDia;
- private DoubleModel llInnerDia;
- private DoubleModel llLength;
- private DoubleModel llMass;
+ private final JTextField llPartNoTextField;
+ private final JTextField llDescTextField;
+ private final DoubleModel llOuterDia;
+ private final DoubleModel llInnerDia;
+ private final DoubleModel llLength;
+ private final DoubleModel llMass;
private ImageIcon llImage;
- private JButton llImageBtn;
+ private final JButton llImageBtn;
- private JTextField rbPartNoTextField;
- private JTextField rbDescTextField;
- private DoubleModel rbOuterDia;
- private DoubleModel rbInnerDia;
- private DoubleModel rbHeight;
- private DoubleModel rbStandoffHeight;
- private DoubleModel rbFlangeHeight;
- private DoubleModel rbMass;
+ private final JTextField rbPartNoTextField;
+ private final JTextField rbDescTextField;
+ private final DoubleModel rbOuterDia;
+ private final DoubleModel rbInnerDia;
+ private final DoubleModel rbHeight;
+ private final DoubleModel rbStandoffHeight;
+ private final DoubleModel rbFlangeHeight;
+ private final DoubleModel rbMass;
private ImageIcon rbImage;
- private JButton rbImageBtn;
+ private final JButton rbImageBtn;
- private JTextField stPartNoTextField;
- private JTextField stDescTextField;
- private DoubleModel stThickness;
- private DoubleModel stWidth;
- private DoubleModel stLength;
- private DoubleModel stMass;
+ private final JTextField stPartNoTextField;
+ private final JTextField stDescTextField;
+ private final DoubleModel stThickness;
+ private final DoubleModel stWidth;
+ private final DoubleModel stLength;
+ private final DoubleModel stMass;
private ImageIcon stImage;
- private JButton stImageBtn;
+ private final JButton stImageBtn;
// Parachute Specific
- private JTextField pcPartNoTextField;
- private JTextField pcDescTextField;
- private DoubleModel pcDiameter;
+ private final JTextField pcPartNoTextField;
+ private final JTextField pcDescTextField;
+ private final DoubleModel pcDiameter;
private DoubleModel pcSpillDia;
private DoubleModel pcSurfaceArea;
private DoubleModel pcDragCoefficient;
// Canopy material = private MaterialChooser materialChooser;
- private JTextField pcSides;
- private JTextField pcLineCount;
- private DoubleModel pcLineLength;
+ private final JTextField pcSides;
+ private final JTextField pcLineCount;
+ private final DoubleModel pcLineLength;
private DoubleModel pcPackedLength;
private DoubleModel pcPackedDiameter;
- private MaterialChooser pcLineMaterialChooser;
- private DoubleModel pcMass;
+ private final MaterialChooser pcLineMaterialChooser;
+ private final DoubleModel pcMass;
private ImageIcon pcImage;
- private JButton pcImageBtn;
+ private final JButton pcImageBtn;
private final JFileChooser imageChooser = createImageChooser();
- private JPanel componentOverlayPanel;
+ private final JPanel componentOverlayPanel;
- private PresetResultListener resultListener;
+ private final PresetResultListener resultListener;
- private static Map componentMap = new HashMap();
+ private static final Map componentMap = new HashMap();
private static final String NOSE_CONE_KEY = "NoseCone.NoseCone";
private static final String BODY_TUBE_KEY = "BodyTube.BodyTube";
@@ -263,6 +262,7 @@ public class PresetEditorDialog extends JDialog implements ItemListener {
getContentPane().setMinimumSize(new Dimension(200, 200));
setBounds(100, 100, 825, 610);
getContentPane().setLayout(new BorderLayout());
+ JPanel contentPanel = new JPanel();
contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
getContentPane().add(contentPanel, BorderLayout.CENTER);
contentPanel.setLayout(new MigLayout("", "[][grow][94.00,grow][232.0,grow][130.00][grow]", "[][][20.00,grow][grow]"));
@@ -1582,8 +1582,8 @@ public class PresetEditorDialog extends JDialog implements ItemListener {
rbHeight.setValue(preset.get(ComponentPreset.HEIGHT));
rbHeight.setCurrentUnit(UnitGroup.UNITS_LENGTH.getDefaultUnit());
}
- if (preset.has(ComponentPreset.STANDOFF_HEIGHT)) {
- rbStandoffHeight.setValue(preset.get(ComponentPreset.STANDOFF_HEIGHT));
+ if (preset.has(ComponentPreset.BASE_HEIGHT)) {
+ rbStandoffHeight.setValue(preset.get(ComponentPreset.BASE_HEIGHT));
rbStandoffHeight.setCurrentUnit(UnitGroup.UNITS_LENGTH.getDefaultUnit());
}
if (preset.has(ComponentPreset.FLANGE_HEIGHT)) {
@@ -1610,8 +1610,8 @@ public class PresetEditorDialog extends JDialog implements ItemListener {
pcDiameter.setValue(preset.get(ComponentPreset.DIAMETER));
pcDiameter.setCurrentUnit(UnitGroup.UNITS_LENGTH.getDefaultUnit());
}
- if (preset.has(ComponentPreset.PARACHUTE_CD)) {
- pcDragCoefficient.setValue(preset.get(ComponentPreset.PARACHUTE_CD));
+ if (preset.has(ComponentPreset.CD)) {
+ pcDragCoefficient.setValue(preset.get(ComponentPreset.CD));
pcDragCoefficient.setCurrentUnit(UnitGroup.UNITS_COEFFICIENT.getDefaultUnit());
}
setMaterial(materialChooser, preset, matHolder, Material.Type.SURFACE, ComponentPreset.MATERIAL);
@@ -2132,7 +2132,7 @@ public class PresetEditorDialog extends JDialog implements ItemListener {
props.put(ComponentPreset.TYPE, ComponentPreset.Type.RAIL_BUTTON);
props.put(ComponentPreset.OUTER_DIAMETER, rbOuterDia.getValue());
props.put(ComponentPreset.INNER_DIAMETER, rbInnerDia.getValue());
- props.put(ComponentPreset.STANDOFF_HEIGHT, rbStandoffHeight.getValue());
+ props.put(ComponentPreset.BASE_HEIGHT, rbStandoffHeight.getValue());
props.put(ComponentPreset.FLANGE_HEIGHT, rbFlangeHeight.getValue());
props.put(ComponentPreset.DESCRIPTION, rbDescTextField.getText());
props.put(ComponentPreset.PARTNO, rbPartNoTextField.getText());
@@ -2179,7 +2179,7 @@ public class PresetEditorDialog extends JDialog implements ItemListener {
props.put(ComponentPreset.MANUFACTURER, Manufacturer.getManufacturer(mfgTextField.getText()));
props.put(ComponentPreset.PARTNO, pcPartNoTextField.getText());
props.put(ComponentPreset.DESCRIPTION, pcDescTextField.getText());
- props.put(ComponentPreset.PARACHUTE_CD, pcDragCoefficient.getValue());
+ props.put(ComponentPreset.CD, pcDragCoefficient.getValue());
Material material = (Material) materialChooser.getSelectedItem();
if (material != null) {
props.put(ComponentPreset.MATERIAL, material);
diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/EmptyShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/EmptyShapes.java
new file mode 100644
index 000000000..35b048551
--- /dev/null
+++ b/swing/src/net/sf/openrocket/gui/rocketfigure/EmptyShapes.java
@@ -0,0 +1,122 @@
+package net.sf.openrocket.gui.rocketfigure;
+
+import net.sf.openrocket.util.Coordinate;
+import net.sf.openrocket.util.Transformation;
+
+import java.awt.Shape;
+import java.awt.geom.Line2D;
+import java.awt.geom.Rectangle2D;
+
+/**
+ * Shapes of an "empty"/virtual object, e.g. a podset without any children.
+ * The shape is a center square with additional lines on the north, east, south and west side of the square.
+ *
+ * @author Sibo Van Gool
+ */
+public class EmptyShapes extends RocketComponentShape {
+ /**
+ * Returns the empty shape in the side view.
+ * @param radius radius of the center square
+ */
+ public static Shape[] getShapesSide(final Transformation transformation, final double radius) {
+ final Coordinate instanceAbsoluteLocation = transformation.transform(Coordinate.ZERO);
+ double x = instanceAbsoluteLocation.x;
+ double y = instanceAbsoluteLocation.y;
+
+ double lineLength = getLineLength(radius); // Length of the line protruding the center square
+
+ final Shape[] s = new Shape[5];
+ // Center square
+ s[0] = new Rectangle2D.Double(x - radius, y - radius, 2 * radius, 2 * radius);
+ // Line North
+ s[1] = new Line2D.Double(x, y + radius, x, y + radius + lineLength);
+ // Line East
+ s[2] = new Line2D.Double(x + radius, y, x + radius + lineLength, y);
+ // Line South
+ s[3] = new Line2D.Double(x, y - radius, x, y - radius - lineLength);
+ // Line West
+ s[4] = new Line2D.Double(x - radius, y, x - radius - lineLength, y);
+
+ return s;
+ }
+
+ /**
+ * Returns the empty shape in the side view, with an additional square encompassing the shape that can be used
+ * for selecting the object.
+ * @param radius radius of the center square
+ */
+ public static Shape[] getShapesSideWithSelectionSquare(final Transformation transformation, final double radius) {
+ final Coordinate instanceAbsoluteLocation = transformation.transform(Coordinate.ZERO);
+ double x = instanceAbsoluteLocation.x;
+ double y = instanceAbsoluteLocation.y;
+
+ double lineLength = getLineLength(radius); // Length of the line protruding the center square
+
+ Shape[] shapes = getShapesSide(transformation, radius);
+
+ // Invisible shape for selecting the component (= a square encompassing the component)
+ Shape selectionShape = new Rectangle2D.Double(x - radius - lineLength, y - radius - lineLength,
+ lineLength * 2 + radius * 2, lineLength * 2 + radius * 2);
+
+ Shape[] finalShapes = new Shape[shapes.length + 1];
+ System.arraycopy(shapes, 0, finalShapes, 0, shapes.length);
+ finalShapes[finalShapes.length - 1] = selectionShape;
+
+ return finalShapes;
+ }
+
+ /**
+ * Returns the empty shape in the side view.
+ * @param radius radius of the center square
+ */
+ public static Shape[] getShapesBack(final Transformation transformation, final double radius) {
+ final Coordinate instanceAbsoluteLocation = transformation.transform(Coordinate.ZERO);
+ double z = instanceAbsoluteLocation.z;
+ double y = instanceAbsoluteLocation.y;
+
+ double lineLength = getLineLength(radius); // Length of the line protruding the center square
+
+ final Shape[] s = new Shape[5];
+ // Center square
+ s[0] = new Rectangle2D.Double(z - radius, y - radius, 2 * radius, 2 * radius);
+ // Line North
+ s[1] = new Line2D.Double(z, y + radius, z, y + radius + lineLength);
+ // Line East
+ s[2] = new Line2D.Double(z + radius, y, z + radius + lineLength, y);
+ // Line South
+ s[3] = new Line2D.Double(z, y - radius, z, y - radius - lineLength);
+ // Line West
+ s[4] = new Line2D.Double(z - radius, y, z - radius - lineLength, y);
+
+ return s;
+ }
+
+ /**
+ * Returns the empty shape in the back view, with an additional square encompassing the shape that can be used
+ * for selecting the object.
+ * @param radius radius of the center square
+ */
+ public static Shape[] getShapesBackWithSelectionSquare(final Transformation transformation, final double radius) {
+ final Coordinate instanceAbsoluteLocation = transformation.transform(Coordinate.ZERO);
+ double z = instanceAbsoluteLocation.z;
+ double y = instanceAbsoluteLocation.y;
+
+ double lineLength = getLineLength(radius); // Length of the line protruding the center square
+
+ Shape[] shapes = getShapesBack(transformation, radius);
+
+ // Invisible shape for selecting the component (= a square encompassing the component)
+ Shape selectionShape = new Rectangle2D.Double(z - radius - lineLength, y - radius - lineLength,
+ lineLength * 2 + radius * 2, lineLength * 2 + radius * 2);
+
+ Shape[] finalShapes = new Shape[shapes.length + 1];
+ System.arraycopy(shapes, 0, finalShapes, 0, shapes.length);
+ finalShapes[finalShapes.length - 1] = selectionShape;
+
+ return finalShapes;
+ }
+
+ private static double getLineLength(double radius) {
+ return radius * 3;
+ }
+}
diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/ParallelStageShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/ParallelStageShapes.java
new file mode 100644
index 000000000..565a23591
--- /dev/null
+++ b/swing/src/net/sf/openrocket/gui/rocketfigure/ParallelStageShapes.java
@@ -0,0 +1,48 @@
+package net.sf.openrocket.gui.rocketfigure;
+
+import net.sf.openrocket.rocketcomponent.ParallelStage;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.util.Color;
+import net.sf.openrocket.util.Transformation;
+
+import java.awt.Shape;
+
+public class ParallelStageShapes extends RocketComponentShape {
+ public static final Color boosterColor = new Color(198,163,184);
+
+ public static RocketComponentShape[] getShapesSide(final RocketComponent component, final Transformation transformation) {
+ ParallelStage booster = (ParallelStage)component;
+ double radius = getDisplayRadius(booster);
+
+ Shape[] s = EmptyShapes.getShapesSideWithSelectionSquare(transformation, radius);
+ RocketComponentShape[] shapes = RocketComponentShape.toArray(s, component);
+
+ // Set the color of the shapes
+ for (int i = 0; i < shapes.length - 1; i++) {
+ shapes[i].setColor(boosterColor);
+ }
+ shapes[shapes.length - 1].setColor(Color.INVISIBLE);
+
+ return shapes;
+ }
+
+ public static RocketComponentShape[] getShapesBack(final RocketComponent component, final Transformation transformation) {
+ ParallelStage booster = (ParallelStage)component;
+ double radius = getDisplayRadius(booster);
+
+ Shape[] s = EmptyShapes.getShapesBackWithSelectionSquare(transformation, radius);
+ RocketComponentShape[] shapes = RocketComponentShape.toArray(s, component);
+
+ // Set the color of the shapes
+ for (int i = 0; i < shapes.length - 1; i++) {
+ shapes[i].setColor(boosterColor);
+ }
+ shapes[shapes.length - 1].setColor(Color.INVISIBLE);
+
+ return shapes;
+ }
+
+ private static double getDisplayRadius(ParallelStage booster) {
+ return booster.getRocket().getBoundingRadius() * 0.03;
+ }
+}
diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/PodSetShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/PodSetShapes.java
new file mode 100644
index 000000000..7ef928f2d
--- /dev/null
+++ b/swing/src/net/sf/openrocket/gui/rocketfigure/PodSetShapes.java
@@ -0,0 +1,48 @@
+package net.sf.openrocket.gui.rocketfigure;
+
+import net.sf.openrocket.rocketcomponent.PodSet;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.util.Color;
+import net.sf.openrocket.util.Transformation;
+
+import java.awt.Shape;
+
+public class PodSetShapes extends RocketComponentShape {
+ public static final Color podsetColor = new Color(160,160,215);
+
+ public static RocketComponentShape[] getShapesSide(final RocketComponent component, final Transformation transformation) {
+ PodSet podset = (PodSet)component;
+ double radius = getDisplayRadius(podset);
+
+ Shape[] s = EmptyShapes.getShapesSideWithSelectionSquare(transformation, radius);
+ RocketComponentShape[] shapes = RocketComponentShape.toArray(s, component);
+
+ // Set the color of the shapes
+ for (int i = 0; i < shapes.length - 1; i++) {
+ shapes[i].setColor(podsetColor);
+ }
+ shapes[shapes.length - 1].setColor(Color.INVISIBLE);
+
+ return shapes;
+ }
+
+ public static RocketComponentShape[] getShapesBack(final RocketComponent component, final Transformation transformation) {
+ PodSet podset = (PodSet)component;
+ double radius = getDisplayRadius(podset);
+
+ Shape[] s = EmptyShapes.getShapesBackWithSelectionSquare(transformation, radius);
+ RocketComponentShape[] shapes = RocketComponentShape.toArray(s, component);
+
+ // Set the color of the shapes
+ for (int i = 0; i < shapes.length - 1; i++) {
+ shapes[i].setColor(podsetColor);
+ }
+ shapes[shapes.length - 1].setColor(Color.INVISIBLE);
+
+ return shapes;
+ }
+
+ private static double getDisplayRadius(PodSet podset) {
+ return podset.getRocket().getBoundingRadius() * 0.03;
+ }
+}
diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/RailButtonShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/RailButtonShapes.java
index 8250c41b8..26c95a24d 100644
--- a/swing/src/net/sf/openrocket/gui/rocketfigure/RailButtonShapes.java
+++ b/swing/src/net/sf/openrocket/gui/rocketfigure/RailButtonShapes.java
@@ -29,7 +29,7 @@ public class RailButtonShapes extends RocketComponentShape {
public static RocketComponentShape[] getShapesSide( final RocketComponent component, final Transformation transformation) {
final RailButton btn = (RailButton)component;
- final double baseHeight = btn.getStandoff();
+ final double baseHeight = btn.getBaseHeight();
final double innerHeight = btn.getInnerHeight();
final double flangeHeight = btn.getFlangeHeight();
@@ -53,72 +53,73 @@ public class RailButtonShapes extends RocketComponentShape {
Path2D.Double path = new Path2D.Double();
Path2D.Double pathInvis = new Path2D.Double(); // Path for the invisible triangles
- {// central pillar
- final double drawWidth = outerDiameter;
- final double drawHeight = outerDiameter*sinr;
- final Point2D.Double center = new Point2D.Double( loc.x, loc.y );
- Point2D.Double lowerLeft = new Point2D.Double( center.x - outerRadius, center.y-outerRadius*sinr);
- path.append( new Ellipse2D.Double( lowerLeft.x, lowerLeft.y, drawWidth, drawHeight), false);
-
- path.append( new Line2D.Double( lowerLeft.x, center.y, lowerLeft.x, (center.y+baseHeightcos) ), false);
- path.append( new Line2D.Double( (center.x+outerRadius), center.y, (center.x+outerRadius), (center.y+baseHeightcos) ), false);
-
- path.append( new Ellipse2D.Double( lowerLeft.x, (lowerLeft.y+baseHeightcos), drawWidth, drawHeight), false);
+ {// base cylinder
+ if (baseHeight > 0) {
+ final double drawWidth = outerDiameter;
+ final double drawHeight = outerDiameter * sinr;
+ final Point2D.Double center = new Point2D.Double(loc.x, loc.y);
+ Point2D.Double lowerLeft = new Point2D.Double(center.x - outerRadius, center.y - outerRadius * sinr);
+ path.append(new Ellipse2D.Double(lowerLeft.x, lowerLeft.y, drawWidth, drawHeight), false);
- // Invisible rectangle
- double y_invis;
- if (baseHeightcos >= 0) {
- y_invis = center.y;
+ path.append(new Line2D.Double(lowerLeft.x, center.y, lowerLeft.x, (center.y + baseHeightcos)), false);
+ path.append(new Line2D.Double((center.x + outerRadius), center.y, (center.x + outerRadius), (center.y + baseHeightcos)), false);
+
+ path.append(new Ellipse2D.Double(lowerLeft.x, (lowerLeft.y + baseHeightcos), drawWidth, drawHeight), false);
+
+ // Invisible rectangle
+ double y_invis;
+ if (baseHeightcos >= 0) {
+ y_invis = center.y;
+ } else {
+ y_invis = center.y + baseHeightcos;
+ }
+ pathInvis.append(new Rectangle2D.Double(center.x - outerRadius, y_invis, drawWidth, Math.abs(baseHeightcos)), false);
}
- else {
- y_invis = center.y + baseHeightcos;
- }
- pathInvis.append(new Rectangle2D.Double(center.x-outerRadius, y_invis, drawWidth, Math.abs(baseHeightcos)), false);
}
- {// inner flange
+ {// inner cylinder
final double drawWidth = innerDiameter;
- final double drawHeight = innerDiameter*sinr;
- final Point2D.Double center = new Point2D.Double( loc.x, loc.y + baseHeightcos);
- final Point2D.Double lowerLeft = new Point2D.Double( center.x - innerRadius, center.y-innerRadius*sinr);
- path.append( new Ellipse2D.Double( lowerLeft.x, lowerLeft.y, drawWidth, drawHeight), false);
-
- path.append( new Line2D.Double( lowerLeft.x, center.y, lowerLeft.x, (center.y+innerHeightcos) ), false);
- path.append( new Line2D.Double( (center.x+innerRadius), center.y, (center.x+innerRadius), (center.y+innerHeightcos) ), false);
-
- path.append( new Ellipse2D.Double( lowerLeft.x, (lowerLeft.y+innerHeightcos), drawWidth, drawHeight), false);
+ final double drawHeight = innerDiameter * sinr;
+ final Point2D.Double center = new Point2D.Double(loc.x, loc.y + baseHeightcos);
+ final Point2D.Double lowerLeft = new Point2D.Double(center.x - innerRadius, center.y - innerRadius * sinr);
+ path.append(new Ellipse2D.Double(lowerLeft.x, lowerLeft.y, drawWidth, drawHeight), false);
+
+ path.append(new Line2D.Double(lowerLeft.x, center.y, lowerLeft.x, (center.y + innerHeightcos)), false);
+ path.append(new Line2D.Double((center.x + innerRadius), center.y, (center.x + innerRadius), (center.y + innerHeightcos)), false);
+
+ path.append(new Ellipse2D.Double(lowerLeft.x, (lowerLeft.y + innerHeightcos), drawWidth, drawHeight), false);
// Invisible rectangle
double y_invis;
if (innerHeightcos >= 0) {
y_invis = center.y;
- }
- else {
+ } else {
y_invis = center.y + innerHeightcos;
}
- pathInvis.append(new Rectangle2D.Double(center.x-innerRadius, y_invis, drawWidth, Math.abs(innerHeightcos)), false);
+ pathInvis.append(new Rectangle2D.Double(center.x - innerRadius, y_invis, drawWidth, Math.abs(innerHeightcos)), false);
}
- {// outer flange
- final double drawWidth = outerDiameter;
- final double drawHeight = outerDiameter*sinr;
- final Point2D.Double center = new Point2D.Double( loc.x, loc.y+baseHeightcos+innerHeightcos);
- final Point2D.Double lowerLeft = new Point2D.Double( center.x - outerRadius, center.y-outerRadius*sinr);
- path.append( new Ellipse2D.Double( lowerLeft.x, lowerLeft.y, drawWidth, drawHeight), false);
-
- path.append( new Line2D.Double( lowerLeft.x, center.y, lowerLeft.x, (center.y+flangeHeightcos) ), false);
- path.append( new Line2D.Double( (center.x+outerRadius), center.y, (center.x+outerRadius), (center.y+flangeHeightcos) ), false);
-
- path.append( new Ellipse2D.Double( lowerLeft.x, (lowerLeft.y+flangeHeightcos), drawWidth, drawHeight), false);
+ {// flange cylinder
+ if (flangeHeight > 0) {
+ final double drawWidth = outerDiameter;
+ final double drawHeight = outerDiameter * sinr;
+ final Point2D.Double center = new Point2D.Double(loc.x, loc.y + baseHeightcos + innerHeightcos);
+ final Point2D.Double lowerLeft = new Point2D.Double(center.x - outerRadius, center.y - outerRadius * sinr);
+ path.append(new Ellipse2D.Double(lowerLeft.x, lowerLeft.y, drawWidth, drawHeight), false);
- // Invisible rectangle
- double y_invis;
- if (flangeHeightcos >= 0) {
- y_invis = center.y;
+ path.append(new Line2D.Double(lowerLeft.x, center.y, lowerLeft.x, (center.y + flangeHeightcos)), false);
+ path.append(new Line2D.Double((center.x + outerRadius), center.y, (center.x + outerRadius), (center.y + flangeHeightcos)), false);
+
+ path.append(new Ellipse2D.Double(lowerLeft.x, (lowerLeft.y + flangeHeightcos), drawWidth, drawHeight), false);
+
+ // Invisible rectangle
+ double y_invis;
+ if (flangeHeightcos >= 0) {
+ y_invis = center.y;
+ } else {
+ y_invis = center.y + flangeHeightcos;
+ }
+ pathInvis.append(new Rectangle2D.Double(center.x - outerRadius, y_invis, drawWidth, Math.abs(flangeHeightcos)), false);
}
- else {
- y_invis = center.y + flangeHeightcos;
- }
- pathInvis.append(new Rectangle2D.Double(center.x-outerRadius, y_invis, drawWidth, Math.abs(flangeHeightcos)), false);
}
RocketComponentShape[] shapes = RocketComponentShape.toArray(new Shape[]{ path }, component);
@@ -136,7 +137,7 @@ public class RailButtonShapes extends RocketComponentShape {
public static RocketComponentShape[] getShapesBack( final RocketComponent component, final Transformation transformation) {
final RailButton btn = (RailButton)component;
- final double baseHeight = btn.getStandoff();
+ final double baseHeight = btn.getBaseHeight();
final double innerHeight = btn.getInnerHeight();
final double flangeHeight = btn.getFlangeHeight();
@@ -159,7 +160,9 @@ public class RailButtonShapes extends RocketComponentShape {
Path2D.Double path = new Path2D.Double();
// base
- path.append( getRotatedRectangle( loc.z, loc.y, outerRadius, baseHeight, combined_angle_rad), false );
+ if (baseHeight > 0) {
+ path.append(getRotatedRectangle(loc.z, loc.y, outerRadius, baseHeight, combined_angle_rad), false);
+ }
{// inner
final double delta_r = baseHeight;
@@ -167,11 +170,13 @@ public class RailButtonShapes extends RocketComponentShape {
final double delta_z = delta_r*sinr;
path.append( getRotatedRectangle( loc.z+delta_z, loc.y+delta_y, innerRadius, innerHeight, combined_angle_rad), false);
}
- {// outer flange
- final double delta_r = baseHeight + innerHeight;
- final double delta_y = delta_r*cosr;
- final double delta_z = delta_r*sinr;
- path.append( getRotatedRectangle( loc.z+delta_z, loc.y+delta_y, outerRadius, flangeHeight, combined_angle_rad), false);
+ {// flange
+ if (flangeHeight > 0) {
+ final double delta_r = baseHeight + innerHeight;
+ final double delta_y = delta_r * cosr;
+ final double delta_z = delta_r * sinr;
+ path.append(getRotatedRectangle(loc.z + delta_z, loc.y + delta_y, outerRadius, flangeHeight, combined_angle_rad), false);
+ }
}
return RocketComponentShape.toArray( new Shape[]{ path }, component);
diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/RocketComponentShape.java b/swing/src/net/sf/openrocket/gui/rocketfigure/RocketComponentShape.java
index 725713b80..c132bfd3d 100644
--- a/swing/src/net/sf/openrocket/gui/rocketfigure/RocketComponentShape.java
+++ b/swing/src/net/sf/openrocket/gui/rocketfigure/RocketComponentShape.java
@@ -64,6 +64,10 @@ public class RocketComponentShape {
return new RocketComponentShape[0];
}
+ public Color getColor() {
+ return color;
+ }
+
public void setColor(Color color) {
this.color = color;
}
diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java
index 3226abee2..0fe7cf400 100644
--- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java
+++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java
@@ -17,6 +17,9 @@ import java.awt.geom.Rectangle2D;
import java.util.*;
import java.util.Map.Entry;
+import net.sf.openrocket.rocketcomponent.AxialStage;
+import net.sf.openrocket.rocketcomponent.ParallelStage;
+import net.sf.openrocket.rocketcomponent.PodSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -26,7 +29,6 @@ import net.sf.openrocket.gui.util.ColorConversion;
import net.sf.openrocket.gui.util.SwingPreferences;
import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.motor.MotorConfiguration;
-import net.sf.openrocket.rocketcomponent.ComponentAssembly;
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
import net.sf.openrocket.rocketcomponent.InstanceContext;
import net.sf.openrocket.rocketcomponent.MotorMount;
@@ -52,6 +54,7 @@ import net.sf.openrocket.util.Transformation;
public class RocketFigure extends AbstractScaleFigure {
private final static Logger log = LoggerFactory.getLogger(FinPointFigure.class);
+ protected final SwingPreferences preferences = (SwingPreferences) Application.getPreferences();
private static final String ROCKET_FIGURE_PACKAGE = "net.sf.openrocket.gui.rocketfigure";
private static final String ROCKET_FIGURE_SUFFIX = "Shapes";
@@ -279,38 +282,38 @@ public class RocketFigure extends AbstractScaleFigure {
Color borderColor = ((SwingPreferences)Application.getPreferences()).getMotorBorderColor();
FlightConfiguration config = rocket.getSelectedConfiguration();
- for( MotorConfiguration curInstance : config.getActiveMotors()){
+ for (MotorConfiguration curInstance : config.getActiveMotors()) {
MotorMount mount = curInstance.getMount();
Motor motor = curInstance.getMotor();
double motorLength = motor.getLength();
double motorRadius = motor.getDiameter() / 2;
RocketComponent mountComponent = ((RocketComponent) mount);
-
+
// .getLocation() will return all the parent instances of this owning component, AND all of it's own instances as well.
// so, just draw a motor once for each Coordinate returned...
Coordinate[] mountLocations = mount.getLocations();
-
+
double mountLength = mountComponent.getLength();
// System.err.println("Drawing Motor: "+motor.getDesignation()+" (x"+mountLocations.length+")");
- for ( Coordinate curMountLocation : mountLocations ){
- Coordinate curMotorLocation = curMountLocation.add( mountLength - motorLength + mount.getMotorOverhang(), 0, 0);
+ for (Coordinate curMountLocation : mountLocations) {
+ Coordinate curMotorLocation = curMountLocation.add(mountLength - motorLength + mount.getMotorOverhang(), 0, 0);
// System.err.println(String.format(" mount instance: %s => %s", curMountLocation.toString(), curMotorLocation.toString() ));
-
- // rotate by figure's axial rotation:
- curMotorLocation = this.axialRotation.transform(curMotorLocation);
+
+ // rotate by figure's axial rotation:
+ curMotorLocation = this.axialRotation.transform(curMotorLocation);
{
Shape s;
if (currentViewType == RocketPanel.VIEW_TYPE.SideView) {
- s = new Rectangle2D.Double( curMotorLocation.x,
- (curMotorLocation.y - motorRadius),
- motorLength,
- 2 * motorRadius);
+ s = new Rectangle2D.Double(curMotorLocation.x,
+ (curMotorLocation.y - motorRadius),
+ motorLength,
+ 2 * motorRadius);
} else {
s = new Ellipse2D.Double((curMotorLocation.z - motorRadius),
- (curMotorLocation.y - motorRadius),
- 2 * motorRadius,
- 2 * motorRadius);
+ (curMotorLocation.y - motorRadius),
+ 2 * motorRadius,
+ 2 * motorRadius);
}
g2.setColor(fillColor);
g2.fill(s);
@@ -375,23 +378,41 @@ public class RocketFigure extends AbstractScaleFigure {
// allShapes is an output buffer -- it stores all the generated shapes
allShapes.clear();
-
- for(Entry> entry: config.getActiveInstances().entrySet() ) {
+
+ for (Entry> entry : config.getActiveInstances().entrySet()) {
final RocketComponent comp = entry.getKey();
-
+
+ // Only draw podsets when they are selected
+ if ((comp instanceof PodSet || comp instanceof ParallelStage) && preferences.isShowMarkers()) {
+ boolean selected = false;
+
+ // Check if component is in the selection
+ for (RocketComponent component : selection) {
+ if (comp == component) {
+ selected = true;
+ break;
+ }
+ }
+ if (!selected) continue;
+ }
+
final ArrayList contextList = entry.getValue();
- for(InstanceContext context: contextList ) {
+ for (InstanceContext context : contextList) {
final Transformation currentTransform = this.axialRotation.applyTransformation(context.transform);
- allShapes = addThisShape( allShapes, this.currentViewType, comp, currentTransform);
+ allShapes = addThisShape(allShapes, this.currentViewType, comp, currentTransform);
}
- }
+ }
}
/**
* Gets the shapes required to draw the component.
- *
- * @param component
+ *
+ * @param allShapes output buffer for the shapes to add to
+ * @param viewType the view type to draw the component in
+ * @param component component to draw and add to
+ * @param transformation transformation to apply to the component before drawing it
+ * @param color color to draw the component in
*
* @return the ArrayList containing all the shapes to draw.
*/
@@ -399,10 +420,11 @@ public class RocketFigure extends AbstractScaleFigure {
PriorityQueue allShapes, // this is the output parameter
final RocketPanel.VIEW_TYPE viewType,
final RocketComponent component,
- final Transformation transformation) {
+ final Transformation transformation,
+ final net.sf.openrocket.util.Color color) {
Reflection.Method m;
- if(( component instanceof Rocket)||( component instanceof ComponentAssembly )){
+ if ((component instanceof Rocket) || (component instanceof AxialStage && !(component instanceof ParallelStage))){
// no-op; no shapes here
return allShapes;
}
@@ -431,9 +453,35 @@ public class RocketFigure extends AbstractScaleFigure {
RocketComponentShape[] returnValue = (RocketComponentShape[]) m.invokeStatic(component, transformation);
+
+ if (color != null) {
+ for (RocketComponentShape rcs : returnValue) {
+ if (rcs.getColor() == net.sf.openrocket.util.Color.INVISIBLE) continue; // don't change the color of invisible (often selection) components
+ rcs.setColor(color);
+ }
+ }
+
allShapes.addAll(Arrays.asList(returnValue));
return allShapes;
}
+
+ /**
+ * Gets the shapes required to draw the component.
+ *
+ * @param allShapes output buffer for the shapes to add to
+ * @param viewType the view type to draw the component in
+ * @param component component to draw and add to
+ * @param transformation transformation to apply to the component before drawing it
+ *
+ * @return the ArrayList containing all the shapes to draw.
+ */
+ private static PriorityQueue addThisShape(
+ PriorityQueue allShapes, // this is the output parameter
+ final RocketPanel.VIEW_TYPE viewType,
+ final RocketComponent component,
+ final Transformation transformation) {
+ return addThisShape(allShapes, viewType, component, transformation, null);
+ }
/**
diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java
index 92df95906..9f4f309f7 100644
--- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java
+++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java
@@ -252,7 +252,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
});
}
- private void updateFigures() {
+ public void updateFigures() {
if (!is3d)
figure.updateFigure();
else
diff --git a/swing/src/net/sf/openrocket/gui/util/WindowLocationUtil.java b/swing/src/net/sf/openrocket/gui/util/WindowLocationUtil.java
new file mode 100644
index 000000000..d4443954c
--- /dev/null
+++ b/swing/src/net/sf/openrocket/gui/util/WindowLocationUtil.java
@@ -0,0 +1,74 @@
+package net.sf.openrocket.gui.util;
+
+import java.awt.GraphicsDevice;
+import java.awt.Window;
+
+/**
+ * Helper class for setting the location of a Swing window. E.g. to check when the window is outside the screen and
+ * recenter it if so.
+ *
+ * @author Sibo Van Gool
+ */
+public abstract class WindowLocationUtil {
+ /**
+ * Sets the location of the window, but don't go outside the screen.
+ * @param window the window to move
+ * @param x the target x position on the screen
+ * @param y the target y position on the screen
+ */
+ public static void setLocationWithinScreen(Window window, int x, int y) {
+ java.awt.Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
+ java.awt.Dimension windowSize = window.getSize();
+ int screenWidth = screenSize.width;
+ int screenHeight = screenSize.height;
+ int windowWidth = windowSize.width;
+ int windowHeight = windowSize.height;
+ int xPos = x;
+ int yPos = y;
+ if (xPos + windowWidth > screenWidth) {
+ xPos = screenWidth - windowWidth;
+ }
+ if (yPos + windowHeight > screenHeight) {
+ yPos = screenHeight - windowHeight;
+ }
+ window.setLocation(xPos, yPos);
+ }
+
+ /**
+ * Moves the window to the center of the screen if its location is outside the boundary of the screen.
+ * @param window window to move
+ */
+ public static void moveIfOutsideOfMonitor(Window window) {
+ GraphicsDevice currentDevice = window.getGraphicsConfiguration().getDevice();
+ if (currentDevice != null && window.isVisible() &&
+ !currentDevice.getDefaultConfiguration().getBounds().contains(window.getLocationOnScreen())) {
+ int width = currentDevice.getDefaultConfiguration().getBounds().width;
+ int height = currentDevice.getDefaultConfiguration().getBounds().height;
+ window.setLocation(
+ ((width / 2) - (window.getSize().width / 2)) + currentDevice.getDefaultConfiguration().getBounds().x,
+ ((height / 2) - (window.getSize().height / 2)) + currentDevice.getDefaultConfiguration().getBounds().y
+ );
+ }
+ }
+
+ /**
+ * Moves the window to the center of the screen if it is on another monitor as the parent window, or if its location
+ * is outside the boundary of the parent's monitor screen.
+ * @param window window to move
+ * @param parent parent window
+ */
+ public static void moveIfOutsideOfParentMonitor(Window window, Window parent) {
+ GraphicsDevice parentDevice = parent.getGraphicsConfiguration().getDevice();
+ GraphicsDevice currentDevice = window.getGraphicsConfiguration().getDevice();
+ if (parentDevice != null && (currentDevice == null || currentDevice != parentDevice ||
+ (window.isVisible() && !parentDevice.getDefaultConfiguration().getBounds().contains(
+ window.getLocationOnScreen())))) {
+ int width = parentDevice.getDefaultConfiguration().getBounds().width;
+ int height = parentDevice.getDefaultConfiguration().getBounds().height;
+ window.setLocation(
+ ((width / 2) - (window.getSize().width / 2)) + parentDevice.getDefaultConfiguration().getBounds().x,
+ ((height / 2) - (window.getSize().height / 2)) + parentDevice.getDefaultConfiguration().getBounds().y
+ );
+ }
+ }
+}
diff --git a/swing/src/net/sf/openrocket/simulation/extension/impl/ScriptingConfigurator.java b/swing/src/net/sf/openrocket/simulation/extension/impl/ScriptingConfigurator.java
index 1fd7c2eae..d49753dac 100644
--- a/swing/src/net/sf/openrocket/simulation/extension/impl/ScriptingConfigurator.java
+++ b/swing/src/net/sf/openrocket/simulation/extension/impl/ScriptingConfigurator.java
@@ -8,7 +8,6 @@ import java.awt.event.FocusListener;
import java.util.Set;
import javax.script.ScriptEngine;
-import javax.script.ScriptEngineManager;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
@@ -33,7 +32,6 @@ import com.google.inject.Inject;
@Plugin
public class ScriptingConfigurator extends AbstractSwingSimulationExtensionConfigurator {
-
@Inject
private ScriptingUtil util;
@@ -66,7 +64,6 @@ public class ScriptingConfigurator extends AbstractSwingSimulationExtensionConfi
});
panel.add(languageSelector, "wrap para");
-
text = new RSyntaxTextArea(extension.getScript(), 20, 80);
text.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT);
text.setCodeFoldingEnabled(true);
@@ -91,7 +88,6 @@ public class ScriptingConfigurator extends AbstractSwingSimulationExtensionConfi
RTextScrollPane scroll = new RTextScrollPane(text);
panel.add(scroll, "spanx, grow, wrap para");
-
BooleanModel enabled = new BooleanModel(extension, "Enabled");
JCheckBox check = new JCheckBox(enabled);
check.setText(trans.get("SimulationExtension.scripting.text.enabled"));
@@ -116,7 +112,6 @@ public class ScriptingConfigurator extends AbstractSwingSimulationExtensionConfi
});
panel.add(button, "wrap rel");
-
StyledLabel label = new StyledLabel(trans.get("SimulationExtension.scripting.text.trusted.msg"), -1, Style.ITALIC);
panel.add(label);
@@ -130,7 +125,6 @@ public class ScriptingConfigurator extends AbstractSwingSimulationExtensionConfi
util.setTrustedScript(extension.getLanguage(), extension.getScript(), trusted.isSelected());
}
-
private void setLanguage(String language) {
if (language == null) {
language = "";
@@ -144,8 +138,7 @@ public class ScriptingConfigurator extends AbstractSwingSimulationExtensionConfi
}
private String findSyntaxLanguage(String language) {
- ScriptEngineManager manager = new ScriptEngineManager();
- ScriptEngine engine = manager.getEngineByName(language);
+ ScriptEngine engine = util.getEngineByName(language);
if (engine != null) {
Set supported = TokenMakerFactory.getDefaultInstance().keySet();
@@ -163,5 +156,4 @@ public class ScriptingConfigurator extends AbstractSwingSimulationExtensionConfi
return SyntaxConstants.SYNTAX_STYLE_NONE;
}
-
}
diff --git a/swing/src/net/sf/openrocket/startup/SwingStartup.java b/swing/src/net/sf/openrocket/startup/SwingStartup.java
index 5d6ff3386..462549e11 100644
--- a/swing/src/net/sf/openrocket/startup/SwingStartup.java
+++ b/swing/src/net/sf/openrocket/startup/SwingStartup.java
@@ -223,7 +223,8 @@ public class SwingStartup {
// Starting action (load files or open new document)
log.info("Opening main application window");
if (!handleCommandLine(args)) {
- BasicFrame.reopen();
+ BasicFrame startupFrame = BasicFrame.reopen();
+ BasicFrame.setStartupFrame(startupFrame);
}
// Check whether update info has been fetched or whether it needs more time
@@ -298,7 +299,7 @@ public class SwingStartup {
// Check command-line for files
boolean opened = false;
for (String file : args) {
- if (BasicFrame.open(new File(file), null)) {
+ if (BasicFrame.open(new File(file), null) != null) {
opened = true;
}
}
diff --git a/swing/src/net/sf/openrocket/utils/Scripting.java b/swing/src/net/sf/openrocket/utils/Scripting.java
index ca1a2993d..ec347a189 100644
--- a/swing/src/net/sf/openrocket/utils/Scripting.java
+++ b/swing/src/net/sf/openrocket/utils/Scripting.java
@@ -5,16 +5,17 @@ import java.util.Collections;
import java.util.List;
import javax.script.ScriptEngineFactory;
-import javax.script.ScriptEngineManager;
import org.fife.ui.rsyntaxtextarea.TokenMakerFactory;
+import net.sf.openrocket.scripting.ScriptEngineManagerRedux;
+
public class Scripting {
public static void main(String[] args) {
System.out.println("Scripting APIs:");
- ScriptEngineManager manager = new ScriptEngineManager();
+ ScriptEngineManagerRedux manager = new ScriptEngineManagerRedux();
for (ScriptEngineFactory factory : manager.getEngineFactories()) {
System.out.println(" engineName=" + factory.getEngineName() +
" engineVersion=" + factory.getEngineVersion() +
diff --git a/swing/test/net/sf/openrocket/IntegrationTest.java b/swing/test/net/sf/openrocket/IntegrationTest.java
index 24cb6178e..e0a280f09 100644
--- a/swing/test/net/sf/openrocket/IntegrationTest.java
+++ b/swing/test/net/sf/openrocket/IntegrationTest.java
@@ -71,7 +71,7 @@ public class IntegrationTest {
private Action undoAction, redoAction;
private AerodynamicCalculator aeroCalc = new BarrowmanCalculator();
- private FlightConfiguration config;
+ private FlightConfigurationId fcid;
private FlightConditions conditions;
private String massComponentID = null;
@@ -112,14 +112,12 @@ public class IntegrationTest {
undoAction = UndoRedoAction.newUndoAction(document);
redoAction = UndoRedoAction.newRedoAction(document);
- FlightConfigurationId fcid = document.getSimulation(0).getFlightConfigurationId();
- config = document.getRocket().getFlightConfiguration(fcid);
+ fcid = document.getSimulation(0).getFlightConfigurationId();
+ FlightConfiguration config = document.getRocket().getFlightConfiguration(fcid);
conditions = new FlightConditions(config);
// Test undo state
checkUndoState(null, null);
-
- InnerTube mmt = (InnerTube)config.getRocket().getChild(0).getChild(1).getChild(2);
// Compute cg+cp + altitude
// double cgx, double mass, double cpx, double cna)
@@ -330,6 +328,7 @@ public class IntegrationTest {
}
private void checkCgCp(double cgx, double mass, double cpx, double cna) {
+ FlightConfiguration config = document.getRocket().getFlightConfiguration(fcid);
final RigidBody launchData = MassCalculator.calculateLaunch(config);
final Coordinate cg = launchData.getCenterOfMass();
assertEquals(cgx, cg.x, 0.001);