Merge branch 'unstable' into issue-1564
|
Before Width: | Height: | Size: 225 B |
|
Before Width: | Height: | Size: 473 B |
|
Before Width: | Height: | Size: 624 B After Width: | Height: | Size: 649 B |
|
Before Width: | Height: | Size: 395 B |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 537 B |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 647 B |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 6.3 KiB |
BIN
core/resources-src/pix/icon/icon-128.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 53 KiB |
BIN
core/resources-src/pix/icon/icon-256.xcf
Normal file
363
core/resources-src/pix/icon/icon-design.ai
Normal file
BIN
core/resources-src/pix/icon/logo_rocket.ork
Normal file
BIN
core/resources-src/pix/splashscreen-2.1.png
Normal file
|
After Width: | Height: | Size: 136 KiB |
BIN
core/resources-src/pix/splashscreen-2.1.xcf
Normal file
@ -1880,6 +1880,7 @@ PlotConfiguration.Groundtrack = Ground track
|
|||||||
Warning.LargeAOA.str1 = Large angle of attack encountered.
|
Warning.LargeAOA.str1 = Large angle of attack encountered.
|
||||||
Warning.LargeAOA.str2 = Large angle of attack encountered (
|
Warning.LargeAOA.str2 = Large angle of attack encountered (
|
||||||
Warning.DISCONTINUITY = Discontinuity in rocket body diameter
|
Warning.DISCONTINUITY = Discontinuity in rocket body diameter
|
||||||
|
Warning.OPEN_AIRFRAME_FORWARD = Forward end of airframe is open (radius is > 0)
|
||||||
Warning.THICK_FIN = Thick fins may not simulate accurately.
|
Warning.THICK_FIN = Thick fins may not simulate accurately.
|
||||||
Warning.JAGGED_EDGED_FIN = Jagged-edged fin predictions may be inaccurate.
|
Warning.JAGGED_EDGED_FIN = Jagged-edged fin predictions may be inaccurate.
|
||||||
Warning.LISTENERS_AFFECTED = Listeners modified the flight simulation
|
Warning.LISTENERS_AFFECTED = Listeners modified the flight simulation
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 624 B After Width: | Height: | Size: 649 B |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 6.3 KiB |
BIN
core/resources/pix/icon/icon-128.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 871 KiB After Width: | Height: | Size: 136 KiB |
@ -69,7 +69,7 @@ public interface AerodynamicCalculator extends Monitorable {
|
|||||||
public AerodynamicCalculator newInstance();
|
public AerodynamicCalculator newInstance();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test component assembly for continuity (esp. diameter), and post any needed warnings
|
* Check component assembly for geometric problems and post any needed warnings
|
||||||
*/
|
*/
|
||||||
public void testIsContinuous(FlightConfiguration configuration, final RocketComponent component, WarningSet warnings);
|
public void checkGeometry(FlightConfiguration configuration, final RocketComponent component, WarningSet warnings);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import net.sf.openrocket.rocketcomponent.InstanceMap;
|
|||||||
import net.sf.openrocket.rocketcomponent.Rocket;
|
import net.sf.openrocket.rocketcomponent.Rocket;
|
||||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||||
import net.sf.openrocket.rocketcomponent.SymmetricComponent;
|
import net.sf.openrocket.rocketcomponent.SymmetricComponent;
|
||||||
|
import net.sf.openrocket.unit.UnitGroup;
|
||||||
import net.sf.openrocket.util.Coordinate;
|
import net.sf.openrocket.util.Coordinate;
|
||||||
import net.sf.openrocket.util.MathUtil;
|
import net.sf.openrocket.util.MathUtil;
|
||||||
import net.sf.openrocket.util.PolyInterpolator;
|
import net.sf.openrocket.util.PolyInterpolator;
|
||||||
@ -44,7 +45,6 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
|
|||||||
private double cacheDiameter = -1;
|
private double cacheDiameter = -1;
|
||||||
private double cacheLength = -1;
|
private double cacheLength = -1;
|
||||||
|
|
||||||
|
|
||||||
public BarrowmanCalculator() {
|
public BarrowmanCalculator() {
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -252,7 +252,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
|
|||||||
if (calcMap == null)
|
if (calcMap == null)
|
||||||
buildCalcMap(configuration);
|
buildCalcMap(configuration);
|
||||||
|
|
||||||
testIsContinuous(configuration, configuration.getRocket(), warnings);
|
checkGeometry(configuration, configuration.getRocket(), warnings);
|
||||||
|
|
||||||
final InstanceMap imap = configuration.getActiveInstances();
|
final InstanceMap imap = configuration.getActiveInstances();
|
||||||
|
|
||||||
@ -276,7 +276,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void testIsContinuous(FlightConfiguration configuration, final RocketComponent treeRoot, WarningSet warnings ){
|
public void checkGeometry(FlightConfiguration configuration, final RocketComponent treeRoot, WarningSet warnings ){
|
||||||
Queue<RocketComponent> queue = new LinkedList<>();
|
Queue<RocketComponent> queue = new LinkedList<>();
|
||||||
for (RocketComponent child : treeRoot.getChildren()) {
|
for (RocketComponent child : treeRoot.getChildren()) {
|
||||||
// Ignore inactive stages
|
// Ignore inactive stages
|
||||||
@ -299,14 +299,20 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SymmetricComponent sym = (SymmetricComponent) comp;
|
SymmetricComponent sym = (SymmetricComponent) comp;
|
||||||
|
prevComp = sym.getPreviousSymmetricComponent();
|
||||||
if( null == prevComp){
|
if( null == prevComp){
|
||||||
prevComp = sym;
|
if (sym.getForeRadius() - sym.getThickness() > MathUtil.EPSILON) {
|
||||||
continue;
|
warnings.add(Warning.OPEN_AIRFRAME_FORWARD, sym.toString());
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
// Check for radius discontinuity
|
// Check for radius discontinuity
|
||||||
if ( !MathUtil.equals(sym.getForeRadius(), prevComp.getAftRadius())) {
|
// We're going to say it's discontinuous if it is presented to the user as having two different
|
||||||
warnings.add( Warning.DIAMETER_DISCONTINUITY, sym + ", " + prevComp);
|
// string representations. Hopefully there are enough digits in the string that it will
|
||||||
|
// present as different if the discontinuity is big enough to matter.
|
||||||
|
if (!UnitGroup.UNITS_LENGTH.getDefaultUnit().toStringUnit(2.0*sym.getForeRadius()).equals(UnitGroup.UNITS_LENGTH.getDefaultUnit().toStringUnit(2.0*prevComp.getAftRadius()))) {
|
||||||
|
// if ( !MathUtil.equals(sym.getForeRadius(), prevComp.getAftRadius())) {
|
||||||
|
warnings.add( Warning.DIAMETER_DISCONTINUITY, sym + ", " + prevComp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// double x = component.toAbsolute(Coordinate.NUL)[0].x;
|
// double x = component.toAbsolute(Coordinate.NUL)[0].x;
|
||||||
@ -318,9 +324,8 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
|
|||||||
//}
|
//}
|
||||||
//componentX = component.toAbsolute(new Coordinate(component.getLengthAerodynamic()))[0].x;
|
//componentX = component.toAbsolute(new Coordinate(component.getLengthAerodynamic()))[0].x;
|
||||||
|
|
||||||
prevComp = sym;
|
|
||||||
}else if( comp instanceof ComponentAssembly ){
|
}else if( comp instanceof ComponentAssembly ){
|
||||||
testIsContinuous(configuration, comp, warnings);
|
checkGeometry(configuration, comp, warnings);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -358,6 +358,9 @@ public abstract class Warning {
|
|||||||
/** A <code>Warning</code> that the body diameter is discontinuous. */
|
/** A <code>Warning</code> that the body diameter is discontinuous. */
|
||||||
////Discontinuity in rocket body diameter.
|
////Discontinuity in rocket body diameter.
|
||||||
public static final Warning DIAMETER_DISCONTINUITY = new Other(trans.get("Warning.DISCONTINUITY"));
|
public static final Warning DIAMETER_DISCONTINUITY = new Other(trans.get("Warning.DISCONTINUITY"));
|
||||||
|
|
||||||
|
/** A <code>Warning</code> that a ComponentAssembly has an open forward end */
|
||||||
|
public static final Warning OPEN_AIRFRAME_FORWARD = new Other(trans.get("Warning.OPEN_AIRFRAME_FORWARD"));
|
||||||
|
|
||||||
/** A <code>Warning</code> that the fins are thick compared to the rocket body. */
|
/** A <code>Warning</code> that the fins are thick compared to the rocket body. */
|
||||||
////Thick fins may not be modeled accurately.
|
////Thick fins may not be modeled accurately.
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import net.sf.openrocket.preset.ComponentPreset;
|
|||||||
import net.sf.openrocket.util.BoundingBox;
|
import net.sf.openrocket.util.BoundingBox;
|
||||||
import net.sf.openrocket.util.Coordinate;
|
import net.sf.openrocket.util.Coordinate;
|
||||||
import net.sf.openrocket.util.MathUtil;
|
import net.sf.openrocket.util.MathUtil;
|
||||||
|
import net.sf.openrocket.rocketcomponent.position.RadiusMethod;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class for an axially symmetric rocket component generated by rotating
|
* Class for an axially symmetric rocket component generated by rotating
|
||||||
@ -621,7 +621,11 @@ public abstract class SymmetricComponent extends BodyComponent implements BoxBou
|
|||||||
while (0 <= searchSiblingIndex) {
|
while (0 <= searchSiblingIndex) {
|
||||||
final RocketComponent searchSibling = searchParent.getChild(searchSiblingIndex);
|
final RocketComponent searchSibling = searchParent.getChild(searchSiblingIndex);
|
||||||
if (searchSibling instanceof SymmetricComponent) {
|
if (searchSibling instanceof SymmetricComponent) {
|
||||||
return (SymmetricComponent) searchSibling;
|
SymmetricComponent candidate = (SymmetricComponent) searchSibling;
|
||||||
|
if (inline(candidate)) {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
--searchSiblingIndex;
|
--searchSiblingIndex;
|
||||||
}
|
}
|
||||||
@ -658,9 +662,12 @@ public abstract class SymmetricComponent extends BodyComponent implements BoxBou
|
|||||||
if(searchParent instanceof ComponentAssembly){
|
if(searchParent instanceof ComponentAssembly){
|
||||||
while (searchSiblingIndex < searchParent.getChildCount()) {
|
while (searchSiblingIndex < searchParent.getChildCount()) {
|
||||||
final RocketComponent searchSibling = searchParent.getChild(searchSiblingIndex);
|
final RocketComponent searchSibling = searchParent.getChild(searchSiblingIndex);
|
||||||
|
|
||||||
if (searchSibling instanceof SymmetricComponent) {
|
if (searchSibling instanceof SymmetricComponent) {
|
||||||
return (SymmetricComponent) searchSibling;
|
SymmetricComponent candidate = (SymmetricComponent) searchSibling;
|
||||||
|
if (inline(candidate)) {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
++searchSiblingIndex;
|
++searchSiblingIndex;
|
||||||
}
|
}
|
||||||
@ -671,6 +678,29 @@ public abstract class SymmetricComponent extends BodyComponent implements BoxBou
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Determine whether a candidate symmetric component is in line with us
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private boolean inline(final SymmetricComponent candidate) {
|
||||||
|
// if we share a parent, we are in line
|
||||||
|
if (this.parent == candidate.parent)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// if both of our parents are either not ring instanceable, or
|
||||||
|
// have a radial offset of 0 from their centerline, we are in line.
|
||||||
|
|
||||||
|
if ((this.parent instanceof RingInstanceable) &&
|
||||||
|
(!MathUtil.equals(this.parent.getRadiusMethod().getRadius(this.parent.parent, this, this.parent.getRadiusOffset()), 0)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ((candidate.parent instanceof RingInstanceable) &&
|
||||||
|
(!MathUtil.equals(candidate.parent.getRadiusMethod().getRadius(candidate.parent.parent, candidate, candidate.parent.getRadiusOffset()), 0)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the component uses the previous symmetric component for its auto diameter.
|
* Checks whether the component uses the previous symmetric component for its auto diameter.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -396,7 +396,13 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
|||||||
|
|
||||||
case IGNITION: {
|
case IGNITION: {
|
||||||
MotorClusterState motorState = (MotorClusterState) event.getData();
|
MotorClusterState motorState = (MotorClusterState) event.getData();
|
||||||
|
|
||||||
|
// If there are multiple ignition events (as is the case if the preceding stage has several burnout events, for instance)
|
||||||
|
// We get multiple ignition events for the upper stage motor. Ignore are all after the first.
|
||||||
|
if (motorState.getIgnitionTime() < currentStatus.getSimulationTime()) {
|
||||||
|
log.info("Ignoring motor " +motorState.toDescription()+" ignition event @"+currentStatus.getSimulationTime());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
log.info(" Igniting motor: "+motorState.toDescription()+" @"+currentStatus.getSimulationTime());
|
log.info(" Igniting motor: "+motorState.toDescription()+" @"+currentStatus.getSimulationTime());
|
||||||
motorState.ignite( event.getTime());
|
motorState.ignite( event.getTime());
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package net.sf.openrocket.simulation;
|
package net.sf.openrocket.simulation;
|
||||||
|
|
||||||
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
|
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
|
||||||
|
import net.sf.openrocket.rocketcomponent.InstanceMap;
|
||||||
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
|
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
|
||||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||||
import net.sf.openrocket.util.Coordinate;
|
import net.sf.openrocket.util.Coordinate;
|
||||||
@ -35,8 +36,10 @@ public class BasicLandingStepper extends AbstractSimulationStepper {
|
|||||||
|
|
||||||
// Get total CD
|
// Get total CD
|
||||||
double mach = airSpeed.length() / atmosphere.getMachSpeed();
|
double mach = airSpeed.length() / atmosphere.getMachSpeed();
|
||||||
|
|
||||||
|
final InstanceMap imap = status.getConfiguration().getActiveInstances();
|
||||||
for (RecoveryDevice c : status.getDeployedRecoveryDevices()) {
|
for (RecoveryDevice c : status.getDeployedRecoveryDevices()) {
|
||||||
totalCD += c.getCD(mach) * c.getArea() / refArea;
|
totalCD += imap.count(c) * c.getCD(mach) * c.getArea() / refArea;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute drag force
|
// Compute drag force
|
||||||
|
|||||||
@ -69,6 +69,27 @@ public class BasicTumbleStepper extends AbstractSimulationStepper {
|
|||||||
double timeStep = MathUtil.min(0.5 / linearAcceleration.length(), RECOVERY_TIME_STEP);
|
double timeStep = MathUtil.min(0.5 / linearAcceleration.length(), RECOVERY_TIME_STEP);
|
||||||
|
|
||||||
// Perform Euler integration
|
// Perform Euler integration
|
||||||
|
Coordinate newPosition = status.getRocketPosition().add(status.getRocketVelocity().multiply(timeStep)).
|
||||||
|
add(linearAcceleration.multiply(MathUtil.pow2(timeStep) / 2));
|
||||||
|
|
||||||
|
// If I've hit the ground, recalculate time step and position
|
||||||
|
if (newPosition.z < 0) {
|
||||||
|
|
||||||
|
final double a = linearAcceleration.z;
|
||||||
|
final double v = status.getRocketVelocity().z;
|
||||||
|
final double z0 = status.getRocketPosition().z;
|
||||||
|
|
||||||
|
// The new timestep is the solution of
|
||||||
|
// 1/2 at^2 + vt + z0 = 0
|
||||||
|
timeStep = (-v - Math.sqrt(v*v - 2*a*z0))/a;
|
||||||
|
|
||||||
|
newPosition = status.getRocketPosition().add(status.getRocketVelocity().multiply(timeStep)).
|
||||||
|
add(linearAcceleration.multiply(MathUtil.pow2(timeStep) / 2));
|
||||||
|
|
||||||
|
// avoid rounding error in new altitude
|
||||||
|
newPosition = newPosition.setZ(0);
|
||||||
|
}
|
||||||
|
|
||||||
status.setRocketPosition(status.getRocketPosition().add(status.getRocketVelocity().multiply(timeStep)).
|
status.setRocketPosition(status.getRocketPosition().add(status.getRocketVelocity().multiply(timeStep)).
|
||||||
add(linearAcceleration.multiply(MathUtil.pow2(timeStep) / 2)));
|
add(linearAcceleration.multiply(MathUtil.pow2(timeStep) / 2)));
|
||||||
status.setRocketVelocity(status.getRocketVelocity().add(linearAcceleration.multiply(timeStep)));
|
status.setRocketVelocity(status.getRocketVelocity().add(linearAcceleration.multiply(timeStep)));
|
||||||
|
|||||||
@ -290,7 +290,7 @@ public class BarrowmanCalculatorTest {
|
|||||||
FlightConfiguration configuration = rocket.getSelectedConfiguration();
|
FlightConfiguration configuration = rocket.getSelectedConfiguration();
|
||||||
WarningSet warnings = new WarningSet();
|
WarningSet warnings = new WarningSet();
|
||||||
|
|
||||||
calc.testIsContinuous(configuration, rocket, warnings);
|
calc.checkGeometry(configuration, rocket, warnings);
|
||||||
assertTrue("Estes Alpha III should be continuous: ", warnings.isEmpty());
|
assertTrue("Estes Alpha III should be continuous: ", warnings.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,7 +301,7 @@ public class BarrowmanCalculatorTest {
|
|||||||
FlightConfiguration configuration = rocket.getSelectedConfiguration();
|
FlightConfiguration configuration = rocket.getSelectedConfiguration();
|
||||||
WarningSet warnings = new WarningSet();
|
WarningSet warnings = new WarningSet();
|
||||||
|
|
||||||
calc.testIsContinuous(configuration, rocket, warnings);
|
calc.checkGeometry(configuration, rocket, warnings);
|
||||||
assertTrue("F9H should be continuous: ", warnings.isEmpty());
|
assertTrue("F9H should be continuous: ", warnings.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,7 +319,7 @@ public class BarrowmanCalculatorTest {
|
|||||||
body.setOuterRadius( 0.012 );
|
body.setOuterRadius( 0.012 );
|
||||||
body.setName( body.getName()+" << discontinuous");
|
body.setName( body.getName()+" << discontinuous");
|
||||||
|
|
||||||
calc.testIsContinuous(configuration, rocket, warnings);
|
calc.checkGeometry(configuration, rocket, warnings);
|
||||||
assertFalse(" Estes Alpha III has an undetected discontinuity:", warnings.isEmpty());
|
assertFalse(" Estes Alpha III has an undetected discontinuity:", warnings.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,7 +340,7 @@ public class BarrowmanCalculatorTest {
|
|||||||
body.setOuterRadius( 0.012 );
|
body.setOuterRadius( 0.012 );
|
||||||
body.setName( body.getName()+" << discontinuous");
|
body.setName( body.getName()+" << discontinuous");
|
||||||
|
|
||||||
calc.testIsContinuous(configuration, rocket, warnings);
|
calc.checkGeometry(configuration, rocket, warnings);
|
||||||
assertFalse(" Missed discontinuity in Falcon 9 Heavy:" , warnings.isEmpty());
|
assertFalse(" Missed discontinuity in Falcon 9 Heavy:" , warnings.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
package net.sf.openrocket.gui;
|
package net.sf.openrocket.gui;
|
||||||
|
|
||||||
|
import net.sf.openrocket.gui.adaptors.TextComponentSelectionKeyListener;
|
||||||
|
|
||||||
import javax.swing.JSpinner;
|
import javax.swing.JSpinner;
|
||||||
import javax.swing.JTextField;
|
import javax.swing.JTextField;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
@ -7,8 +9,8 @@ import javax.swing.text.DefaultFormatter;
|
|||||||
import javax.swing.text.DefaultFormatterFactory;
|
import javax.swing.text.DefaultFormatterFactory;
|
||||||
import java.awt.event.FocusEvent;
|
import java.awt.event.FocusEvent;
|
||||||
import java.awt.event.FocusListener;
|
import java.awt.event.FocusListener;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.awt.event.MouseListener;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Editable editor for a JSpinner. Simply uses JSpinner.DefaultEditor, which has been made
|
* Editable editor for a JSpinner. Simply uses JSpinner.DefaultEditor, which has been made
|
||||||
@ -29,58 +31,7 @@ public class SpinnerEditor extends JSpinner.DefaultEditor {
|
|||||||
DefaultFormatter formatter = (DefaultFormatter) dff.getDefaultFormatter();
|
DefaultFormatter formatter = (DefaultFormatter) dff.getDefaultFormatter();
|
||||||
formatter.setOverwriteMode(false);
|
formatter.setOverwriteMode(false);
|
||||||
|
|
||||||
|
addListeners();
|
||||||
// Add listeners to select all the text when the field is focussed
|
|
||||||
{
|
|
||||||
getTextField().addFocusListener(new FocusListener() {
|
|
||||||
@Override
|
|
||||||
public void focusGained(FocusEvent e) {
|
|
||||||
selectAllText();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void focusLost(FocusEvent e) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
getTextField().addMouseListener(new MouseListener() {
|
|
||||||
private boolean isFocussed = false; // Checks whether the text field was focussed when it was clicked upon
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mouseClicked(MouseEvent e) {
|
|
||||||
// If the text field was focussed when it was clicked upon instead of e.g. tab-switching to gain focus,
|
|
||||||
// then the select all action from the focus listener is ignored (it is replaced by a cursor-click event).
|
|
||||||
// So if we detect such a focus change, then redo the select all action.
|
|
||||||
if (!isFocussed) {
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
JTextField tf = (JTextField) e.getSource();
|
|
||||||
tf.selectAll();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mousePressed(MouseEvent e) {
|
|
||||||
JTextField tf = (JTextField) e.getSource();
|
|
||||||
isFocussed = tf.hasFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mouseReleased(MouseEvent e) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mouseEntered(MouseEvent e) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mouseExited(MouseEvent e) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -93,14 +44,54 @@ public class SpinnerEditor extends JSpinner.DefaultEditor {
|
|||||||
getTextField().setColumns(cols);
|
getTextField().setColumns(cols);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addListeners() {
|
||||||
|
// Select all the text when the field is focussed
|
||||||
|
getTextField().addFocusListener(new FocusListener() {
|
||||||
|
@Override
|
||||||
|
public void focusGained(FocusEvent e) {
|
||||||
|
selectAllText(getTextField());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void focusLost(FocusEvent e) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Select all the text when the field is first clicked upon
|
||||||
|
getTextField().addMouseListener(new MouseAdapter() {
|
||||||
|
private boolean isFocussed = false; // Checks whether the text field was focussed when it was clicked upon
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseClicked(MouseEvent e) {
|
||||||
|
/*
|
||||||
|
If the text field was focussed when it was clicked upon instead of e.g. tab-switching to gain focus,
|
||||||
|
then the select all action from the focus listener is ignored (it is replaced by a cursor-click event).
|
||||||
|
So if we detect such a focus change, then redo the select all action.
|
||||||
|
*/
|
||||||
|
if (!isFocussed) {
|
||||||
|
selectAllText((JTextField) e.getSource());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mousePressed(MouseEvent e) {
|
||||||
|
JTextField tf = (JTextField) e.getSource();
|
||||||
|
isFocussed = tf.hasFocus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fix key behavior on text selection
|
||||||
|
getTextField().addKeyListener(new TextComponentSelectionKeyListener(getTextField()));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Highlights all the text in the text field.
|
* Highlights all the text in the text field.
|
||||||
*/
|
*/
|
||||||
private void selectAllText() {
|
private void selectAllText(JTextField tf) {
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
getTextField().selectAll();
|
tf.selectAll();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,43 @@
|
|||||||
|
package net.sf.openrocket.gui.adaptors;
|
||||||
|
|
||||||
|
import javax.swing.text.JTextComponent;
|
||||||
|
import java.awt.event.KeyAdapter;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This key listener fixes a default behavior by Java Swing text components, where if you select a text, pressing the
|
||||||
|
* left or right arrow key would not bring the text cursor to the beginning or the end of the selection
|
||||||
|
* (@Java, please fix...).
|
||||||
|
* <p>
|
||||||
|
* This listener's behavior:
|
||||||
|
* If some text of the editor is selected, set the caret position to:
|
||||||
|
* - the end of the selection if the user presses the right arrow key
|
||||||
|
* - the beginning of the selection if the user presses the left arrow key
|
||||||
|
*/
|
||||||
|
public class TextComponentSelectionKeyListener extends KeyAdapter {
|
||||||
|
private final JTextComponent textField;
|
||||||
|
|
||||||
|
public TextComponentSelectionKeyListener(JTextComponent textField) {
|
||||||
|
this.textField = textField;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void keyPressed(KeyEvent e) {
|
||||||
|
if (e.isShiftDown()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (e.getKeyCode() == KeyEvent.VK_LEFT || e.getKeyCode() == KeyEvent.VK_KP_LEFT) {
|
||||||
|
int start = textField.getSelectionStart();
|
||||||
|
int end = textField.getSelectionEnd();
|
||||||
|
if (end > start) {
|
||||||
|
textField.setCaretPosition(start + 1);
|
||||||
|
}
|
||||||
|
} else if (e.getKeyCode() == KeyEvent.VK_RIGHT || e.getKeyCode() == KeyEvent.VK_KP_RIGHT) {
|
||||||
|
int start = textField.getSelectionStart();
|
||||||
|
int end = textField.getSelectionEnd();
|
||||||
|
if (end > start) {
|
||||||
|
textField.setCaretPosition(end - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,7 +5,10 @@ import java.awt.Color;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
import java.awt.event.*;
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.event.FocusEvent;
|
||||||
|
import java.awt.event.FocusListener;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -35,6 +38,7 @@ import net.sf.openrocket.gui.adaptors.BooleanModel;
|
|||||||
import net.sf.openrocket.gui.adaptors.DoubleModel;
|
import net.sf.openrocket.gui.adaptors.DoubleModel;
|
||||||
import net.sf.openrocket.gui.adaptors.IntegerModel;
|
import net.sf.openrocket.gui.adaptors.IntegerModel;
|
||||||
import net.sf.openrocket.gui.adaptors.PresetModel;
|
import net.sf.openrocket.gui.adaptors.PresetModel;
|
||||||
|
import net.sf.openrocket.gui.adaptors.TextComponentSelectionKeyListener;
|
||||||
import net.sf.openrocket.gui.components.BasicSlider;
|
import net.sf.openrocket.gui.components.BasicSlider;
|
||||||
import net.sf.openrocket.gui.components.DescriptionArea;
|
import net.sf.openrocket.gui.components.DescriptionArea;
|
||||||
import net.sf.openrocket.gui.components.StyledLabel;
|
import net.sf.openrocket.gui.components.StyledLabel;
|
||||||
@ -120,6 +124,7 @@ public class RocketComponentConfig extends JPanel {
|
|||||||
textFieldListener = new TextFieldListener();
|
textFieldListener = new TextFieldListener();
|
||||||
componentNameField.addActionListener(textFieldListener);
|
componentNameField.addActionListener(textFieldListener);
|
||||||
componentNameField.addFocusListener(textFieldListener);
|
componentNameField.addFocusListener(textFieldListener);
|
||||||
|
componentNameField.addKeyListener(new TextComponentSelectionKeyListener(componentNameField));
|
||||||
//// The component name.
|
//// The component name.
|
||||||
componentNameField.setToolTipText(trans.get("RocketCompCfg.lbl.Componentname.ttip"));
|
componentNameField.setToolTipText(trans.get("RocketCompCfg.lbl.Componentname.ttip"));
|
||||||
this.add(componentNameField, "growx");
|
this.add(componentNameField, "growx");
|
||||||
@ -672,6 +677,7 @@ public class RocketComponentConfig extends JPanel {
|
|||||||
commentTextArea.setEditable(true);
|
commentTextArea.setEditable(true);
|
||||||
GUIUtil.setTabToFocusing(commentTextArea);
|
GUIUtil.setTabToFocusing(commentTextArea);
|
||||||
commentTextArea.addFocusListener(textFieldListener);
|
commentTextArea.addFocusListener(textFieldListener);
|
||||||
|
commentTextArea.addKeyListener(new TextComponentSelectionKeyListener(commentTextArea));
|
||||||
|
|
||||||
panel.add(new JScrollPane(commentTextArea), "grow");
|
panel.add(new JScrollPane(commentTextArea), "grow");
|
||||||
order.add(commentTextArea);
|
order.add(commentTextArea);
|
||||||
|
|||||||
@ -94,7 +94,7 @@ public class AboutDialog extends JDialog {
|
|||||||
|
|
||||||
|
|
||||||
// OpenRocket logo
|
// OpenRocket logo
|
||||||
panel.add(new JLabel(Icons.loadImageIcon("pix/icon/icon-about.png", "OpenRocket")), "top");
|
panel.add(new JLabel(Icons.loadImageIcon("pix/icon/icon-128.png", "OpenRocket")), "top");
|
||||||
|
|
||||||
|
|
||||||
// OpenRocket version info + copyright
|
// OpenRocket version info + copyright
|
||||||
|
|||||||
@ -30,7 +30,7 @@ public class LicenseDialog extends JDialog {
|
|||||||
JPanel panel = new JPanel(new MigLayout("fill"));
|
JPanel panel = new JPanel(new MigLayout("fill"));
|
||||||
|
|
||||||
// OpenRocket logo
|
// OpenRocket logo
|
||||||
panel.add(new JLabel(Icons.loadImageIcon("pix/icon/icon-about.png", "OpenRocket")), "top");
|
panel.add(new JLabel(Icons.loadImageIcon("pix/icon/icon-128.png", "OpenRocket")), "top");
|
||||||
|
|
||||||
panel.add(new StyledLabel("Software Licenses", 10), "ax 50%, pushx, wrap para");
|
panel.add(new StyledLabel("Software Licenses", 10), "ax 50%, pushx, wrap para");
|
||||||
|
|
||||||
|
|||||||
@ -58,7 +58,7 @@ public class UpdateInfoDialog extends JDialog {
|
|||||||
JPanel panel = new JPanel(new MigLayout("insets n n 8px n, fill"));
|
JPanel panel = new JPanel(new MigLayout("insets n n 8px n, fill"));
|
||||||
|
|
||||||
// OpenRocket logo on the left
|
// OpenRocket logo on the left
|
||||||
panel.add(new JLabel(Icons.getScaledIcon(Icons.loadImageIcon("pix/icon/icon-about.png", "OpenRocket"), 0.6)),
|
panel.add(new JLabel(Icons.loadImageIcon("pix/icon/icon-128.png", "OpenRocket")),
|
||||||
"spany, top, gapright 20px, cell 0 0");
|
"spany, top, gapright 20px, cell 0 0");
|
||||||
|
|
||||||
// OpenRocket version available!
|
// OpenRocket version available!
|
||||||
|
|||||||
@ -43,7 +43,7 @@ public class WelcomeDialog extends JDialog {
|
|||||||
JPanel panel = new JPanel(new MigLayout("insets n n 8px n, fill"));
|
JPanel panel = new JPanel(new MigLayout("insets n n 8px n, fill"));
|
||||||
|
|
||||||
// OpenRocket logo on the left
|
// OpenRocket logo on the left
|
||||||
panel.add(new JLabel(Icons.getScaledIcon(Icons.loadImageIcon("pix/icon/icon-about.png", "OpenRocket"), 0.6)),
|
panel.add(new JLabel(Icons.loadImageIcon("pix/icon/icon-128.png", "OpenRocket")),
|
||||||
"spany, top, gapright 20px, cell 0 0");
|
"spany, top, gapright 20px, cell 0 0");
|
||||||
|
|
||||||
// Thank you for downloading!
|
// Thank you for downloading!
|
||||||
|
|||||||