Index: Ringtoets/Piping/src/Ringtoets.Piping.Plugin/PipingPlugin.cs =================================================================== diff -u -r277048d204ab4cfad4aa5de4d57ef38b56642c25 -r53cf7e65c1ee70a2ef0143eff830b650785bdf79 --- Ringtoets/Piping/src/Ringtoets.Piping.Plugin/PipingPlugin.cs (.../PipingPlugin.cs) (revision 277048d204ab4cfad4aa5de4d57ef38b56642c25) +++ Ringtoets/Piping/src/Ringtoets.Piping.Plugin/PipingPlugin.cs (.../PipingPlugin.cs) (revision 53cf7e65c1ee70a2ef0143eff830b650785bdf79) @@ -25,6 +25,7 @@ using System.Linq; using System.Windows.Forms; using Core.Common.Base; +using Core.Common.Base.Data; using Core.Common.Controls.TreeView; using Core.Common.Gui; using Core.Common.Gui.ContextMenu; @@ -38,6 +39,7 @@ using Ringtoets.Common.Forms.Helpers; using Ringtoets.Common.Forms.PresentationObjects; using Ringtoets.Common.Forms.TreeNodeInfos; +using Ringtoets.Common.Service; using Ringtoets.Piping.Data; using Ringtoets.Piping.Forms; using Ringtoets.Piping.Forms.PresentationObjects; @@ -742,7 +744,10 @@ PipingCalculation calculation = nodeData.WrappedData; + StrictContextMenuItem updateEntryAndExitPoint = CreateUpdateEntryAndExitPointItem(nodeData); + return builder.AddRenameItem() + .AddCustomItem(updateEntryAndExitPoint) .AddValidateCalculationItem( nodeData, Validate, @@ -824,6 +829,51 @@ context.FailureMechanism.Contribution)); } + private static StrictContextMenuItem CreateUpdateEntryAndExitPointItem(PipingCalculationScenarioContext context) + { + bool hasSurfaceLine = context.WrappedData.InputParameters.SurfaceLine != null; + + var updateEntryAndExitPointItem = new StrictContextMenuItem( + "Bijwerken intrede- en uittredepunt", + "", // TODO WTI-1076: update tooltip + null, // TODO WTI-1076: update icon + (o, args) => { UpdateSurfaceLineDependentData(context.WrappedData); }) + { + Enabled = hasSurfaceLine + }; + return updateEntryAndExitPointItem; + } + + private static void UpdateSurfaceLineDependentData(PipingCalculation scenario) + { + PipingInput inputParameters = scenario.InputParameters; + RingtoetsPipingSurfaceLine currentSurfaceLine = inputParameters.SurfaceLine; + RoundedDouble oldEntryPointL = inputParameters.EntryPointL; + RoundedDouble oldExitPointL = inputParameters.ExitPointL; + + inputParameters.SurfaceLine = currentSurfaceLine; + + var affectedObjects = new List(); + if (AreEntryAndExitPointsUpdated(oldEntryPointL, oldExitPointL, inputParameters)) + { + affectedObjects.Add(inputParameters); + affectedObjects.AddRange(RingtoetsCommonDataSynchronizationService.ClearCalculationOutput(scenario)); + } + + foreach (IObservable affectedObject in affectedObjects) + { + affectedObject.NotifyObservers(); + } + } + + private static bool AreEntryAndExitPointsUpdated(RoundedDouble oldEntryPointL, + RoundedDouble oldExitPointL, + PipingInput inputParameters) + { + return !(oldEntryPointL == inputParameters.EntryPointL) + || !(oldExitPointL == inputParameters.ExitPointL); + } + #endregion #region PipingCalculationGroupContext TreeNodeInfo Index: Ringtoets/Piping/test/Ringtoets.Piping.Plugin.Test/TreeNodeInfos/PipingCalculationScenarioContextTreeNodeInfoTest.cs =================================================================== diff -u -r6ce3cfa19ef59b12462bae4a77e9a7ee5a05e28c -r53cf7e65c1ee70a2ef0143eff830b650785bdf79 --- Ringtoets/Piping/test/Ringtoets.Piping.Plugin.Test/TreeNodeInfos/PipingCalculationScenarioContextTreeNodeInfoTest.cs (.../PipingCalculationScenarioContextTreeNodeInfoTest.cs) (revision 6ce3cfa19ef59b12462bae4a77e9a7ee5a05e28c) +++ Ringtoets/Piping/test/Ringtoets.Piping.Plugin.Test/TreeNodeInfos/PipingCalculationScenarioContextTreeNodeInfoTest.cs (.../PipingCalculationScenarioContextTreeNodeInfoTest.cs) (revision 53cf7e65c1ee70a2ef0143eff830b650785bdf79) @@ -23,6 +23,7 @@ using System.Linq; using System.Windows.Forms; using Core.Common.Base; +using Core.Common.Base.Data; using Core.Common.Base.Geometry; using Core.Common.Controls.TreeView; using Core.Common.Gui; @@ -50,10 +51,12 @@ [TestFixture] public class PipingCalculationScenarioContextTreeNodeInfoTest : NUnitFormTest { - private const int contextMenuValidateIndex = 1; - private const int contextMenuCalculateIndex = 2; - private const int contextMenuClearIndex = 4; + private const int contextMenuUpdateEntryAndExitPointIndex = 1; + private const int contextMenuValidateIndex = 2; + private const int contextMenuCalculateIndex = 3; + private const int contextMenuClearIndex = 5; + private MockRepository mocks; private PipingPlugin plugin; private TreeNodeInfo info; @@ -358,6 +361,7 @@ menuBuilderMock.Expect(mb => mb.AddRenameItem()).Return(menuBuilderMock); menuBuilderMock.Expect(mb => mb.AddCustomItem(null)).IgnoreArguments().Return(menuBuilderMock); menuBuilderMock.Expect(mb => mb.AddCustomItem(null)).IgnoreArguments().Return(menuBuilderMock); + menuBuilderMock.Expect(mb => mb.AddCustomItem(null)).IgnoreArguments().Return(menuBuilderMock); menuBuilderMock.Expect(mb => mb.AddSeparator()).Return(menuBuilderMock); menuBuilderMock.Expect(mb => mb.AddCustomItem(null)).IgnoreArguments().Return(menuBuilderMock); menuBuilderMock.Expect(mb => mb.AddDeleteItem()).Return(menuBuilderMock); @@ -384,6 +388,298 @@ } [Test] + public void UpdateEntryAndExitPoint_CalculationHasNoSurfaceLine_ItemDisabled() + { + // Setup + using (var treeViewControl = new TreeViewControl()) + { + var calculation = new PipingCalculationScenario(new GeneralPipingInput()); + var pipingFailureMechanism = new TestPipingFailureMechanism(); + var assessmentSection = mocks.Stub(); + var nodeData = new PipingCalculationScenarioContext(calculation, + Enumerable.Empty(), + Enumerable.Empty(), + pipingFailureMechanism, + assessmentSection); + + var gui = mocks.Stub(); + gui.Stub(cmp => cmp.Get(nodeData, treeViewControl)).Return(new CustomItemsOnlyContextMenuBuilder()); + mocks.ReplayAll(); + + plugin.Gui = gui; + + // Call + using (ContextMenuStrip contextMenu = info.ContextMenuStrip(nodeData, null, treeViewControl)) + { + // Assert + TestHelper.AssertContextMenuStripContainsItem(contextMenu, + contextMenuUpdateEntryAndExitPointIndex, + "Bijwerken intrede- en uittredepunt", + "", // TODO WTI-1076: update tooltip + null, // TODO WTI-1076: update icon + false); + } + } + } + + [Test] + public void UpdateEntryAndExitPoint_CalculationHasSurfaceLine_ItemEnabled() + { + // Setup + using (var treeViewControl = new TreeViewControl()) + { + var surfaceLine = new RingtoetsPipingSurfaceLine(); + surfaceLine.SetGeometry(new[] + { + new Point3D(1, 2, 3), + new Point3D(4, 5, 6) + }); + var calculation = new PipingCalculationScenario(new GeneralPipingInput()) + { + InputParameters = + { + SurfaceLine = surfaceLine + } + }; + var pipingFailureMechanism = new TestPipingFailureMechanism(); + var assessmentSection = mocks.Stub(); + var nodeData = new PipingCalculationScenarioContext(calculation, + Enumerable.Empty(), + Enumerable.Empty(), + pipingFailureMechanism, + assessmentSection); + + var gui = mocks.Stub(); + gui.Stub(cmp => cmp.Get(nodeData, treeViewControl)).Return(new CustomItemsOnlyContextMenuBuilder()); + mocks.ReplayAll(); + + plugin.Gui = gui; + + // Call + using (ContextMenuStrip contextMenu = info.ContextMenuStrip(nodeData, null, treeViewControl)) + { + // Assert + TestHelper.AssertContextMenuStripContainsItem(contextMenu, + contextMenuUpdateEntryAndExitPointIndex, + "Bijwerken intrede- en uittredepunt", + "", // TODO WTI-1076: update tooltip + null); // TODO WTI-1076: update icon + } + } + } + + [Test] + public void GivenCalculationWithSurfaceLine_WhenSurfaceLineUpdatedAndUpdateEntryAndExitPointClicked__ThenPointsUpdatedAndObserversNotified() + { + using (var treeViewControl = new TreeViewControl()) + { + // Given + var surfaceLine = new RingtoetsPipingSurfaceLine(); + surfaceLine.SetGeometry(new[] + { + new Point3D(1, 2, 3), + new Point3D(4, 5, 6) + }); + var calculation = new PipingCalculationScenario(new GeneralPipingInput()) + { + InputParameters = + { + SurfaceLine = surfaceLine, + EntryPointL = (RoundedDouble) 0, + ExitPointL = (RoundedDouble) 1 + } + }; + + var pipingFailureMechanism = new TestPipingFailureMechanism(); + var assessmentSection = mocks.Stub(); + var nodeData = new PipingCalculationScenarioContext(calculation, + Enumerable.Empty(), + Enumerable.Empty(), + pipingFailureMechanism, + assessmentSection); + + var inputObserver = mocks.StrictMock(); + inputObserver.Expect(obs => obs.UpdateObserver()); + nodeData.WrappedData.InputParameters.Attach(inputObserver); + + var gui = mocks.Stub(); + gui.Stub(cmp => cmp.Get(nodeData, treeViewControl)).Return(new CustomItemsOnlyContextMenuBuilder()); + mocks.ReplayAll(); + + plugin.Gui = gui; + + using (ContextMenuStrip contextMenuStrip = info.ContextMenuStrip(nodeData, null, treeViewControl)) + { + // When + surfaceLine.SetGeometry(new[] + { + new Point3D(0, 0, 0), + new Point3D(1, 0, 2), + new Point3D(2, 0, 3), + new Point3D(3, 0, 0), + new Point3D(4, 0, 2), + new Point3D(5, 0, 3) + }); + surfaceLine.SetDikeToeAtRiverAt(new Point3D(2, 0, 3)); + surfaceLine.SetDikeToeAtPolderAt(new Point3D(3, 0, 0)); + + contextMenuStrip.Items[contextMenuUpdateEntryAndExitPointIndex].PerformClick(); + + // Then + PipingInput inputParameters = calculation.InputParameters; + Assert.AreEqual(new RoundedDouble(2, 2), inputParameters.EntryPointL); + Assert.AreEqual(new RoundedDouble(3, 3), inputParameters.ExitPointL); + + // Note: observer assertions are verified in Teardown + } + } + } + + [Test] + public void GivenCalculationWithSurfaceLineAndOutput_WhenSurfaceLineUpdatedAndUpdateEntryAndExitPointClicked__ThenPointsUpdatedOutputsRemovedAndObserversNotified() + { + using (var treeViewControl = new TreeViewControl()) + { + // Given + var surfaceLine = new RingtoetsPipingSurfaceLine(); + surfaceLine.SetGeometry(new[] + { + new Point3D(1, 2, 3), + new Point3D(4, 5, 6) + }); + var calculation = new PipingCalculationScenario(new GeneralPipingInput()) + { + InputParameters = + { + SurfaceLine = surfaceLine, + EntryPointL = (RoundedDouble) 0, + ExitPointL = (RoundedDouble) 1 + }, + Output = new TestPipingOutput() + }; + + var pipingFailureMechanism = new TestPipingFailureMechanism(); + var assessmentSection = mocks.Stub(); + var nodeData = new PipingCalculationScenarioContext(calculation, + Enumerable.Empty(), + Enumerable.Empty(), + pipingFailureMechanism, + assessmentSection); + + var inputObserver = mocks.StrictMock(); + inputObserver.Expect(obs => obs.UpdateObserver()); + nodeData.WrappedData.InputParameters.Attach(inputObserver); + + var calculationObserver = mocks.StrictMock(); + calculationObserver.Expect(obs => obs.UpdateObserver()); + nodeData.WrappedData.Attach(calculationObserver); + + var gui = mocks.Stub(); + gui.Stub(cmp => cmp.Get(nodeData, treeViewControl)).Return(new CustomItemsOnlyContextMenuBuilder()); + mocks.ReplayAll(); + + plugin.Gui = gui; + + using (ContextMenuStrip contextMenuStrip = info.ContextMenuStrip(nodeData, null, treeViewControl)) + { + // When + surfaceLine.SetGeometry(new[] + { + new Point3D(0, 0, 0), + new Point3D(1, 0, 2), + new Point3D(2, 0, 3), + new Point3D(3, 0, 0), + new Point3D(4, 0, 2), + new Point3D(5, 0, 3) + }); + surfaceLine.SetDikeToeAtRiverAt(new Point3D(2, 0, 3)); + surfaceLine.SetDikeToeAtPolderAt(new Point3D(3, 0, 0)); + + contextMenuStrip.Items[contextMenuUpdateEntryAndExitPointIndex].PerformClick(); + + // Then + PipingInput inputParameters = calculation.InputParameters; + Assert.AreEqual(new RoundedDouble(2, 2), inputParameters.EntryPointL); + Assert.AreEqual(new RoundedDouble(3, 3), inputParameters.ExitPointL); + Assert.IsFalse(calculation.HasOutput); + + // Note: observer assertions are verified in Teardown + } + } + } + + [Test] + public void GivenCalculationWithSurfaceLineAndOutput_WhenUpdatedSurfaceLineHasNoChangeAndUpdateEntryAndExitPointClicked__ThenOutputNotRemovedAndObserversNotNotified() + { + using (var treeViewControl = new TreeViewControl()) + { + // Given + var surfaceLine = new RingtoetsPipingSurfaceLine(); + surfaceLine.SetGeometry(new[] + { + new Point3D(1, 2, 3), + new Point3D(4, 5, 6) + }); + var calculation = new PipingCalculationScenario(new GeneralPipingInput()) + { + InputParameters = + { + SurfaceLine = surfaceLine, + EntryPointL = (RoundedDouble) 2, + ExitPointL = (RoundedDouble) 3 + }, + Output = new TestPipingOutput() + }; + + var pipingFailureMechanism = new TestPipingFailureMechanism(); + var assessmentSection = mocks.Stub(); + var nodeData = new PipingCalculationScenarioContext(calculation, + Enumerable.Empty(), + Enumerable.Empty(), + pipingFailureMechanism, + assessmentSection); + + var inputObserver = mocks.StrictMock(); + nodeData.WrappedData.InputParameters.Attach(inputObserver); + + var calculationObserver = mocks.StrictMock(); + nodeData.WrappedData.Attach(calculationObserver); + + var gui = mocks.Stub(); + gui.Stub(cmp => cmp.Get(nodeData, treeViewControl)).Return(new CustomItemsOnlyContextMenuBuilder()); + mocks.ReplayAll(); + + plugin.Gui = gui; + + using (ContextMenuStrip contextMenuStrip = info.ContextMenuStrip(nodeData, null, treeViewControl)) + { + // When + surfaceLine.SetGeometry(new[] + { + new Point3D(0, 0, 0), + new Point3D(1, 0, 2), + new Point3D(2, 0, 3), + new Point3D(3, 0, 0), + new Point3D(4, 0, 2), + new Point3D(5, 0, 3) + }); + surfaceLine.SetDikeToeAtRiverAt(new Point3D(2, 0, 3)); + surfaceLine.SetDikeToeAtPolderAt(new Point3D(3, 0, 0)); + + contextMenuStrip.Items[contextMenuUpdateEntryAndExitPointIndex].PerformClick(); + + // Then + PipingInput inputParameters = calculation.InputParameters; + Assert.AreEqual(new RoundedDouble(2, 2), inputParameters.EntryPointL); + Assert.AreEqual(new RoundedDouble(3, 3), inputParameters.ExitPointL); + Assert.IsTrue(calculation.HasOutput); + + // Note: observer assertions are verified in Teardown + } + } + } + + [Test] [TestCase(true)] [TestCase(false)] public void OnNodeRemoved_ParentIsPipingCalculationGroupContext_RemoveCalculationFromGroup(bool groupNameEditable)