diff --git a/core/src/main/java/edu/wpi/grip/core/events/ConnectionAddedEvent.java b/core/src/main/java/edu/wpi/grip/core/events/ConnectionAddedEvent.java index 93380a21bf..bfd0469034 100644 --- a/core/src/main/java/edu/wpi/grip/core/events/ConnectionAddedEvent.java +++ b/core/src/main/java/edu/wpi/grip/core/events/ConnectionAddedEvent.java @@ -9,7 +9,7 @@ * An event that occurs when a new connection is added to the pipeline. This is triggered by the user adding a * connection with the GUI. */ -public class ConnectionAddedEvent implements RunPipelineEvent { +public class ConnectionAddedEvent implements RunPipelineEvent, DirtiesSaveEvent { private final Connection connection; /** diff --git a/core/src/main/java/edu/wpi/grip/core/events/ConnectionRemovedEvent.java b/core/src/main/java/edu/wpi/grip/core/events/ConnectionRemovedEvent.java index 2351ef3e84..b6f963f32f 100644 --- a/core/src/main/java/edu/wpi/grip/core/events/ConnectionRemovedEvent.java +++ b/core/src/main/java/edu/wpi/grip/core/events/ConnectionRemovedEvent.java @@ -9,7 +9,7 @@ * An event that occurs when a connection is removed from the pipeline. This is triggered by the user deleting a * connection with the GUI. */ -public class ConnectionRemovedEvent { +public class ConnectionRemovedEvent implements DirtiesSaveEvent { private final Connection connection; /** diff --git a/core/src/main/java/edu/wpi/grip/core/events/DirtiesSaveEvent.java b/core/src/main/java/edu/wpi/grip/core/events/DirtiesSaveEvent.java new file mode 100644 index 0000000000..b48e2c496b --- /dev/null +++ b/core/src/main/java/edu/wpi/grip/core/events/DirtiesSaveEvent.java @@ -0,0 +1,18 @@ +package edu.wpi.grip.core.events; + +/** + * An event that can potentially dirty the save file. + * These events ensure that anything that changes causes the save file to be flagged as dirty and in need of being + * saved for the project to be deemed "clean" again. + */ +public interface DirtiesSaveEvent { + + /** + * Some events may have more logic regarding whether they make the save dirty or not. + * + * @return True if this event should dirty the project save + */ + default boolean doesDirtySave() { + return true; + } +} diff --git a/core/src/main/java/edu/wpi/grip/core/events/SocketChangedEvent.java b/core/src/main/java/edu/wpi/grip/core/events/SocketChangedEvent.java index 295e2ae08a..c07533afc4 100644 --- a/core/src/main/java/edu/wpi/grip/core/events/SocketChangedEvent.java +++ b/core/src/main/java/edu/wpi/grip/core/events/SocketChangedEvent.java @@ -9,7 +9,7 @@ * An event that occurs when the value stored in a socket changes. This can happen, for example, as the result of an * operation completing, or as a response to user input. */ -public class SocketChangedEvent implements RunPipelineEvent { +public class SocketChangedEvent implements RunPipelineEvent, DirtiesSaveEvent { private final Socket socket; /** @@ -27,6 +27,18 @@ public Socket getSocket() { return this.socket; } + /** + * This event will only dirty the save if the InputSocket does not have connections. + * Thus the value can only have been changed by a UI component. + * If the socket has connections then the value change is triggered by another socket's change. + * + * @return True if this should dirty the save. + */ + @Override + public boolean doesDirtySave() { + return socket.getDirection() == Socket.Direction.INPUT && socket.getConnections().isEmpty(); + } + @Override public boolean pipelineShouldRun() { /* diff --git a/core/src/main/java/edu/wpi/grip/core/events/SocketPreviewChangedEvent.java b/core/src/main/java/edu/wpi/grip/core/events/SocketPreviewChangedEvent.java index 2ce165c858..f99002bd1d 100644 --- a/core/src/main/java/edu/wpi/grip/core/events/SocketPreviewChangedEvent.java +++ b/core/src/main/java/edu/wpi/grip/core/events/SocketPreviewChangedEvent.java @@ -9,7 +9,7 @@ * An event that occurs when a {@link OutputSocket} is set to be either previewed or not previewed. The GUI listens for these events * so it knows which sockets to show previews for. */ -public class SocketPreviewChangedEvent { +public class SocketPreviewChangedEvent implements DirtiesSaveEvent { private OutputSocket socket; /** diff --git a/core/src/main/java/edu/wpi/grip/core/events/SourceAddedEvent.java b/core/src/main/java/edu/wpi/grip/core/events/SourceAddedEvent.java index 48b089fbd2..ca943e118b 100644 --- a/core/src/main/java/edu/wpi/grip/core/events/SourceAddedEvent.java +++ b/core/src/main/java/edu/wpi/grip/core/events/SourceAddedEvent.java @@ -12,7 +12,7 @@ * * @see Source */ -public class SourceAddedEvent { +public class SourceAddedEvent implements DirtiesSaveEvent { private final Source source; /** diff --git a/core/src/main/java/edu/wpi/grip/core/events/SourceRemovedEvent.java b/core/src/main/java/edu/wpi/grip/core/events/SourceRemovedEvent.java index b2da3c99e5..0126fa2007 100644 --- a/core/src/main/java/edu/wpi/grip/core/events/SourceRemovedEvent.java +++ b/core/src/main/java/edu/wpi/grip/core/events/SourceRemovedEvent.java @@ -12,7 +12,7 @@ * * @see Source */ -public class SourceRemovedEvent { +public class SourceRemovedEvent implements DirtiesSaveEvent { private final Source source; /** diff --git a/core/src/main/java/edu/wpi/grip/core/events/StepAddedEvent.java b/core/src/main/java/edu/wpi/grip/core/events/StepAddedEvent.java index 7910314653..7e915c8515 100644 --- a/core/src/main/java/edu/wpi/grip/core/events/StepAddedEvent.java +++ b/core/src/main/java/edu/wpi/grip/core/events/StepAddedEvent.java @@ -13,7 +13,7 @@ * An event that occurs when a new step is added to the pipeline. This is triggered by the user adding a step with the * GUI. */ -public class StepAddedEvent { +public class StepAddedEvent implements DirtiesSaveEvent { private final Step step; private final OptionalInt index; diff --git a/core/src/main/java/edu/wpi/grip/core/events/StepMovedEvent.java b/core/src/main/java/edu/wpi/grip/core/events/StepMovedEvent.java index 1d75f78841..c56350c8a2 100644 --- a/core/src/main/java/edu/wpi/grip/core/events/StepMovedEvent.java +++ b/core/src/main/java/edu/wpi/grip/core/events/StepMovedEvent.java @@ -8,7 +8,7 @@ /** * An event that occurs when a new step is moved from one position to another in the pipeline */ -public class StepMovedEvent { +public class StepMovedEvent implements DirtiesSaveEvent { private final Step step; private final int distance; diff --git a/core/src/main/java/edu/wpi/grip/core/events/StepRemovedEvent.java b/core/src/main/java/edu/wpi/grip/core/events/StepRemovedEvent.java index e5eebf2db1..0d577ebd49 100644 --- a/core/src/main/java/edu/wpi/grip/core/events/StepRemovedEvent.java +++ b/core/src/main/java/edu/wpi/grip/core/events/StepRemovedEvent.java @@ -9,7 +9,7 @@ * An event that occurs when a new step is removed from the pipeline. This is triggered by the user deleting a step * from the GUI. */ -public class StepRemovedEvent { +public class StepRemovedEvent implements DirtiesSaveEvent { private final Step step; /** diff --git a/core/src/main/java/edu/wpi/grip/core/serialization/Project.java b/core/src/main/java/edu/wpi/grip/core/serialization/Project.java index 196984e9be..b224067951 100644 --- a/core/src/main/java/edu/wpi/grip/core/serialization/Project.java +++ b/core/src/main/java/edu/wpi/grip/core/serialization/Project.java @@ -1,9 +1,13 @@ package edu.wpi.grip.core.serialization; import com.google.common.annotations.VisibleForTesting; +import com.google.common.eventbus.Subscribe; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider; -import edu.wpi.grip.core.*; +import edu.wpi.grip.core.Connection; +import edu.wpi.grip.core.Pipeline; +import edu.wpi.grip.core.Step; +import edu.wpi.grip.core.events.DirtiesSaveEvent; import edu.wpi.grip.core.sockets.InputSocket; import edu.wpi.grip.core.sockets.OutputSocket; import edu.wpi.grip.core.sources.CameraSource; @@ -27,6 +31,7 @@ public class Project { protected final XStream xstream = new XStream(new PureJavaReflectionProvider()); private Optional file = Optional.empty(); + private boolean saveIsDirty = false; @Inject public void initialize(StepConverter stepConverter, @@ -69,6 +74,7 @@ public void open(File file) throws IOException { void open(Reader reader) { this.pipeline.clear(); this.xstream.fromXML(reader); + saveIsDirty = false; } /** @@ -81,7 +87,22 @@ public void save(File file) throws IOException { this.file = Optional.of(file); } + public void save(Writer writer) { this.xstream.toXML(this.pipeline, writer); + saveIsDirty = false; + } + + public boolean isSaveDirty() { + return saveIsDirty; + } + + @Subscribe + public void onDirtiesSaveEvent(DirtiesSaveEvent dirtySaveEvent) { + // Only update the flag the save isn't already dirty + // We don't need to be redundantly checking if the event dirties the save. + if (!saveIsDirty && dirtySaveEvent.doesDirtySave()) { + saveIsDirty = true; + } } } diff --git a/ui/src/main/java/edu/wpi/grip/ui/MainWindowController.java b/ui/src/main/java/edu/wpi/grip/ui/MainWindowController.java index 81376b77dc..4988148dc8 100644 --- a/ui/src/main/java/edu/wpi/grip/ui/MainWindowController.java +++ b/ui/src/main/java/edu/wpi/grip/ui/MainWindowController.java @@ -85,7 +85,7 @@ public void initialize() { * @return true If the user has not chosen to */ private boolean showConfirmationDialogAndWait() { - if (!pipeline.getSteps().isEmpty()) { + if (!pipeline.getSteps().isEmpty() && project.isSaveDirty()) { final ButtonType save = new ButtonType("Save"); final ButtonType dontSave = ButtonType.NO; final ButtonType cancel = ButtonType.CANCEL;