[#1956] Fix issue in multi-comp edit during undo operation

At least I hope this fixes it; it was a weird bug and hard to replicate...
This commit is contained in:
SiboVG 2023-01-08 03:20:56 +01:00
parent ac03080588
commit e8638b25cf
9 changed files with 28 additions and 9 deletions

View File

@ -241,6 +241,7 @@ public class AxialStage extends ComponentAssembly implements FlightConfigurableC
@Override
public void clearConfigListeners() {
super.clearConfigListeners();
// StageSeparationConfiguration also has config listeners, so clear them as well
StageSeparationConfiguration thisConfig = getSeparationConfiguration();
thisConfig.clearConfigListeners();
}

View File

@ -549,6 +549,7 @@ public class BodyTube extends SymmetricComponent implements BoxBounded, MotorMou
@Override
public void clearConfigListeners() {
super.clearConfigListeners();
// The motor config also has listeners, so clear them as well
getDefaultMotorConfig().clearConfigListeners();
}
}

View File

@ -482,6 +482,7 @@ public class InnerTube extends ThicknessRingComponent implements AxialPositionab
@Override
public void clearConfigListeners() {
super.clearConfigListeners();
// The motor config also has listeners, so clear them as well
getDefaultMotorConfig().clearConfigListeners();
}

View File

@ -1,14 +1,9 @@
package net.sf.openrocket.rocketcomponent;
import net.sf.openrocket.appearance.Appearance;
import net.sf.openrocket.appearance.Decal;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.preset.ComponentPreset;
import net.sf.openrocket.preset.ComponentPreset.Type;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.StateChangeListener;
import java.util.EventObject;
/**
* Rocket nose cones of various types. Implemented as a transition with the
@ -205,7 +200,7 @@ public class NoseCone extends Transition implements InsideColorComponent {
return;
}
boolean previousByPass = getBypassComponentChangeEvent();
boolean previousByPass = isBypassComponentChangeEvent();
setBypassChangeEvent(true);
if (flipped) {
setForeRadius(getAftRadiusNoAutomatic());

View File

@ -177,6 +177,7 @@ public abstract class RecoveryDevice extends MassObject implements FlightConfigu
@Override
public void clearConfigListeners() {
super.clearConfigListeners();
// The DeploymentConfiguration also has listeners, so clear them as well
DeploymentConfiguration thisConfig = getDeploymentConfigurations().getDefault();
thisConfig.clearConfigListeners();
}

View File

@ -448,6 +448,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
}
// Make sure the config listeners aren't cloned
clone.configListeners = new LinkedList<>();
clone.bypassComponentChangeEvent = false;
return clone;
}
@ -603,7 +604,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
for (RocketComponent listener : configListeners) {
listener.setBypassChangeEvent(false);
listener.setMassOverridden(o);
listener.setBypassChangeEvent(false);
listener.setBypassChangeEvent(true);
}
if (massOverridden == o) {
@ -2277,7 +2278,11 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
this.bypassComponentChangeEvent = newValue;
}
public boolean getBypassComponentChangeEvent() {
/**
* Returns whether the current component if ignoring ComponentChangeEvents.
* @return true if the component is ignoring ComponentChangeEvents.
*/
public boolean isBypassComponentChangeEvent() {
return this.bypassComponentChangeEvent;
}
@ -2287,7 +2292,18 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
* @return true if listener was successfully added, false if not
*/
public boolean addConfigListener(RocketComponent listener) {
if (listener == null || configListeners.contains(listener) || listener == this) {
if (isBypassComponentChangeEvent()) {
// This is a precaution. If you are multi-comp editing and the current component is bypassing events,
// the editing will be REALLY weird, see GitHub issue #1956.
throw new IllegalStateException("Cannot add config listener while bypassing events");
}
if (listener == null) {
return false;
}
if (listener.getConfigListeners().size() > 0) {
throw new IllegalArgumentException("Listener already has config listeners");
}
if (configListeners.contains(listener) || listener == this) {
return false;
}
configListeners.add(listener);
@ -2555,6 +2571,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
this.displayOrder_side = src.displayOrder_side;
this.displayOrder_back = src.displayOrder_back;
this.configListeners = new LinkedList<>();
this.bypassComponentChangeEvent = false;
if (this instanceof InsideColorComponent && src instanceof InsideColorComponent) {
InsideColorComponentHandler icch = new InsideColorComponentHandler(this);
icch.copyFrom(((InsideColorComponent) src).getInsideColorComponentHandler());

View File

@ -105,6 +105,7 @@ public class DesignPanel extends JSplitPane {
// Double-click
if ((e.getButton() == MouseEvent.BUTTON1) && (e.getClickCount() == 2) && !ComponentConfigDialog.isDialogVisible()) {
RocketComponent component = (RocketComponent) selPath.getLastPathComponent();
component.clearConfigListeners();
// Multi-component edit if shift/meta key is pressed
if ((e.isShiftDown() || e.isMetaDown()) && tree.getSelectionPaths() != null) {

View File

@ -985,6 +985,7 @@ public class RocketActions {
ComponentConfigDialog.disposeDialog();
RocketComponent component = components.get(0);
component.clearConfigListeners();
if (components.size() > 1) {
for (int i = 1; i < components.size(); i++) {
RocketComponent listener = components.get(i);

View File

@ -636,6 +636,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
if (event.isShiftDown() || event.isMetaDown()) {
List<TreePath> paths = new ArrayList<>(Arrays.asList(selectionModel.getSelectionPaths()));
RocketComponent component = selectedComponents.get(selectedComponents.size() - 1);
component.clearConfigListeners();
// Make sure the clicked component is selected
for (RocketComponent c : clicked) {