diff --git a/core/src/net/sf/openrocket/appearance/AppearanceBuilder.java b/core/src/net/sf/openrocket/appearance/AppearanceBuilder.java index 3763eae0e..5dfb7ab3b 100644 --- a/core/src/net/sf/openrocket/appearance/AppearanceBuilder.java +++ b/core/src/net/sf/openrocket/appearance/AppearanceBuilder.java @@ -555,7 +555,7 @@ public class AppearanceBuilder extends AbstractChangeSource { * @return true if listener was successfully added, false if not */ public boolean addConfigListener(RocketComponent component, AppearanceBuilder ab) { - if (component == null || ab == null) { + if (component == null || ab == null || configListeners.values().contains(ab) || ab == this) { return false; } configListeners.put(component, ab); diff --git a/core/src/net/sf/openrocket/rocketcomponent/BodyTube.java b/core/src/net/sf/openrocket/rocketcomponent/BodyTube.java index ee84e8014..212aadd48 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/BodyTube.java +++ b/core/src/net/sf/openrocket/rocketcomponent/BodyTube.java @@ -31,7 +31,7 @@ public class BodyTube extends SymmetricComponent implements BoxBounded, MotorMou private MotorConfigurationSet motors; - private final InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this); + private InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this); public BodyTube() { this(8 * DEFAULT_RADIUS, DEFAULT_RADIUS); @@ -521,6 +521,11 @@ public class BodyTube extends SymmetricComponent implements BoxBounded, MotorMou return this.insideColorComponentHandler; } + @Override + public void setInsideColorComponentHandler(InsideColorComponentHandler handler) { + this.insideColorComponentHandler = handler; + } + @Override public boolean addConfigListener(RocketComponent listener) { boolean success = super.addConfigListener(listener); diff --git a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java index 4bd080a96..93ed781b3 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java @@ -123,7 +123,7 @@ public abstract class FinSet extends ExternalComponent implements AxialPositiona private double totalVolume = Double.NaN; private Coordinate centerOfMass = Coordinate.NaN; - private final InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this); + private InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this); /** * New FinSet with given number of fins and given base rotation angle. @@ -1369,4 +1369,9 @@ public abstract class FinSet extends ExternalComponent implements AxialPositiona public InsideColorComponentHandler getInsideColorComponentHandler() { return this.insideColorComponentHandler; } + + @Override + public void setInsideColorComponentHandler(InsideColorComponentHandler handler) { + this.insideColorComponentHandler = handler; + } } diff --git a/core/src/net/sf/openrocket/rocketcomponent/InsideColorComponent.java b/core/src/net/sf/openrocket/rocketcomponent/InsideColorComponent.java index c964b902a..f64a1bb84 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/InsideColorComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/InsideColorComponent.java @@ -22,4 +22,6 @@ public interface InsideColorComponent { * @return the InsideColorComponentHandler */ InsideColorComponentHandler getInsideColorComponentHandler(); + + void setInsideColorComponentHandler(InsideColorComponentHandler handler); } diff --git a/core/src/net/sf/openrocket/rocketcomponent/LaunchLug.java b/core/src/net/sf/openrocket/rocketcomponent/LaunchLug.java index f6fa6f65b..12d152c8e 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/LaunchLug.java +++ b/core/src/net/sf/openrocket/rocketcomponent/LaunchLug.java @@ -30,7 +30,7 @@ public class LaunchLug extends Tube implements AnglePositionable, BoxBounded, Li private int instanceCount = 1; private double instanceSeparation = 0; // front-front along the positive rocket axis. i.e. [1,0,0]; - private final InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this); + private InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this); public LaunchLug() { super(AxialMethod.MIDDLE); @@ -333,4 +333,9 @@ public class LaunchLug extends Tube implements AnglePositionable, BoxBounded, Li public InsideColorComponentHandler getInsideColorComponentHandler() { return this.insideColorComponentHandler; } + + @Override + public void setInsideColorComponentHandler(InsideColorComponentHandler handler) { + this.insideColorComponentHandler = handler; + } } diff --git a/core/src/net/sf/openrocket/rocketcomponent/NoseCone.java b/core/src/net/sf/openrocket/rocketcomponent/NoseCone.java index 851dfd2d5..ed436981d 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/NoseCone.java +++ b/core/src/net/sf/openrocket/rocketcomponent/NoseCone.java @@ -20,7 +20,7 @@ import java.util.EventObject; public class NoseCone extends Transition implements InsideColorComponent { private static final Translator trans = Application.getTranslator(); - private final InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this); + private InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this); /********* Constructors **********/ public NoseCone() { @@ -155,4 +155,9 @@ public class NoseCone extends Transition implements InsideColorComponent { public InsideColorComponentHandler getInsideColorComponentHandler() { return this.insideColorComponentHandler; } + + @Override + public void setInsideColorComponentHandler(InsideColorComponentHandler handler) { + this.insideColorComponentHandler = handler; + } } diff --git a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java index 4971e1525..076211716 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java @@ -134,7 +134,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab /** * List of components that will set their properties to the same as the current component */ - protected final List configListeners = new LinkedList<>(); + protected List configListeners = new LinkedList<>(); /** @@ -426,6 +426,20 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab mutex.unlock("copyWithOriginalID"); } } + + @Override + public RocketComponent clone() throws CloneNotSupportedException { + RocketComponent clone = (RocketComponent) super.clone(); + // Make sure the InsideColorComponentHandler is cloned + if (clone instanceof InsideColorComponent && this instanceof InsideColorComponent) { + InsideColorComponentHandler icch = new InsideColorComponentHandler(clone); + icch.copyFrom(((InsideColorComponent) this).getInsideColorComponentHandler()); + ((InsideColorComponent) clone).setInsideColorComponentHandler(icch); + } + // Make sure the config listeners aren't cloned + clone.configListeners = new LinkedList<>(); + return clone; + } /** * Return true if any of this component's children are a RecoveryDevice @@ -1983,7 +1997,7 @@ 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) { + if (listener == null || configListeners.contains(listener) || listener == this) { return false; } configListeners.add(listener); @@ -2247,6 +2261,12 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab this.id = src.id; this.displayOrder_side = src.displayOrder_side; this.displayOrder_back = src.displayOrder_back; + this.configListeners = new LinkedList<>(); + if (this instanceof InsideColorComponent && src instanceof InsideColorComponent) { + InsideColorComponentHandler icch = new InsideColorComponentHandler(this); + icch.copyFrom(((InsideColorComponent) src).getInsideColorComponentHandler()); + ((InsideColorComponent) this).setInsideColorComponentHandler(icch); + } // Add source components to invalidation tree for (RocketComponent c : src) { diff --git a/core/src/net/sf/openrocket/rocketcomponent/Transition.java b/core/src/net/sf/openrocket/rocketcomponent/Transition.java index b7320d4cb..c9f0f9b16 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/Transition.java +++ b/core/src/net/sf/openrocket/rocketcomponent/Transition.java @@ -44,7 +44,7 @@ public class Transition extends SymmetricComponent implements InsideColorCompone // Used to cache the clip length private double clipLength = -1; - private final InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this); + private InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this); public Transition() { super(); @@ -732,6 +732,11 @@ public class Transition extends SymmetricComponent implements InsideColorCompone return this.insideColorComponentHandler; } + @Override + public void setInsideColorComponentHandler(InsideColorComponentHandler handler) { + this.insideColorComponentHandler = handler; + } + /** * An enumeration listing the possible shapes of transitions. * diff --git a/core/src/net/sf/openrocket/rocketcomponent/TubeFinSet.java b/core/src/net/sf/openrocket/rocketcomponent/TubeFinSet.java index 0cba415ce..992a8935a 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/TubeFinSet.java +++ b/core/src/net/sf/openrocket/rocketcomponent/TubeFinSet.java @@ -28,7 +28,7 @@ public class TubeFinSet extends Tube implements AxialPositionable, BoxBounded, R private AngleMethod angleMethod = AngleMethod.FIXED; protected RadiusMethod radiusMethod = RadiusMethod.RELATIVE; - private final InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this); + private InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this); /** * Rotation angle of the first fin. Zero corresponds to the positive y-axis. @@ -523,4 +523,9 @@ public class TubeFinSet extends Tube implements AxialPositionable, BoxBounded, R public InsideColorComponentHandler getInsideColorComponentHandler() { return this.insideColorComponentHandler; } + + @Override + public void setInsideColorComponentHandler(InsideColorComponentHandler handler) { + this.insideColorComponentHandler = handler; + } } diff --git a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java index 3a435b329..a42969b13 100644 --- a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -345,6 +345,7 @@ public class BasicFrame extends JFrame { c.clearConfigListeners(); for (int i = 1; i < paths.length; i++) { RocketComponent listener = (RocketComponent) paths[i].getLastPathComponent(); + listener.clearConfigListeners(); c.addConfigListener(listener); } ComponentConfigDialog.showDialog(BasicFrame.this, BasicFrame.this.document, c); diff --git a/swing/src/net/sf/openrocket/gui/main/RocketActions.java b/swing/src/net/sf/openrocket/gui/main/RocketActions.java index 3292af843..fb1c28bd9 100644 --- a/swing/src/net/sf/openrocket/gui/main/RocketActions.java +++ b/swing/src/net/sf/openrocket/gui/main/RocketActions.java @@ -791,7 +791,8 @@ public class RocketActions { Simulation[] sims = selectionModel.getSelectedSimulations(); if (isCopyable(components)) { - ComponentConfigDialog.disposeDialog(); + if (ComponentConfigDialog.isDialogVisible()) + ComponentConfigDialog.disposeDialog(); List copiedComponents = copyComponentsMaintainParent(components); OpenRocketClipboard.setClipboard(copiedComponents); @@ -885,7 +886,9 @@ public class RocketActions { RocketComponent component = components.get(0); if (components.size() > 1) { for (int i = 1; i < components.size(); i++) { - component.addConfigListener(components.get(i)); + RocketComponent listener = components.get(i); + listener.clearConfigListeners(); // Make sure all the listeners are cleared (should not be possible, but just in case) + component.addConfigListener(listener); } } ComponentConfigDialog.showDialog(parentFrame, document, component);