// Copyright (C) Stichting Deltares 2016. All rights reserved. // // This file is part of Ringtoets. // // Ringtoets is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // // All names, logos, and references to "Deltares" are registered trademarks of // Stichting Deltares and remain full property of Stichting Deltares at all times. // All rights reserved. using System; using System.Drawing; using System.Linq; using System.Windows.Forms; using Core.Common.Controls.TreeView; using Ringtoets.Common.Data.Calculation; using Ringtoets.Common.Data.FailureMechanism; using Ringtoets.Common.Data.Probability; using Ringtoets.Common.Forms.PresentationObjects; using Ringtoets.Common.Forms.Properties; using RingtoetsCommonDataResources = Ringtoets.Common.Data.Properties.Resources; using BaseResources = Core.Common.Base.Properties.Resources; namespace Ringtoets.Common.Forms.TreeNodeInfos { /// /// Factory for creating calculation related objects. /// public static class RingtoetsTreeNodeInfoFactory { /// /// Creates a object for a calculation group context of the type . /// /// The type of calculation group context to create a object for. /// The function for obtaining the child node objects. /// The function for obtaining the context menu strip. /// The action to perform on removing a node. /// A object. public static TreeNodeInfo CreateCalculationGroupContextTreeNodeInfo( Func childNodeObjects, Func contextMenuStrip, Action onNodeRemoved) where TCalculationGroupContext : ICalculationContext { return new TreeNodeInfo { Text = context => context.WrappedData.Name, Image = context => Resources.GeneralFolderIcon, EnsureVisibleOnCreate = (context, parent) => (parent is ICalculationContext), ChildNodeObjects = childNodeObjects, ContextMenuStrip = contextMenuStrip, CanRename = (context, parentData) => IsNestedGroup(parentData), OnNodeRenamed = (context, newName) => { context.WrappedData.Name = newName; context.NotifyObservers(); }, CanRemove = (context, parentData) => IsNestedGroup(parentData), OnNodeRemoved = onNodeRemoved, CanDrag = (context, parentData) => IsNestedGroup(parentData), CanInsert = CalculationGroupCanDropOrInsert, CanDrop = CalculationGroupCanDropOrInsert, OnDrop = CalculationGroupOnDrop }; } /// /// Creates a object for a calculation context of the type . /// /// The type of calculation context to create a object for. /// The function for obtaining the child node objects. /// The function for obtaining the context menu strip. /// The action to perform on removing a node. /// A object. public static TreeNodeInfo CreateCalculationContextTreeNodeInfo( Func childNodeObjects, Func contextMenuStrip, Action onNodeRemoved) where TCalculationContext : ICalculationContext { return new TreeNodeInfo { Text = context => context.WrappedData.Name, Image = context => Resources.CalculationIcon, EnsureVisibleOnCreate = (context, parent) => true, ChildNodeObjects = childNodeObjects, ContextMenuStrip = contextMenuStrip, CanRename = (context, parent) => true, OnNodeRenamed = (context, newName) => { context.WrappedData.Name = newName; context.WrappedData.NotifyObservers(); }, CanRemove = (context, parentData) => CalculationContextCanRemove(context, parentData), OnNodeRemoved = onNodeRemoved, CanDrag = (context, parentData) => true }; } /// /// Creates a object for a failure mechanism context of the type . /// /// The type of failure mechanism context to create a object for. /// The function for obtaining the child node objects when is true. /// The function for obtaining the child node objects when is false. /// The function for obtaining the context menu strip when is true. /// The function for obtaining the context menu strip when is false. /// A object. public static TreeNodeInfo CreateFailureMechanismContextTreeNodeInfo( Func enabledChildNodeObjects, Func disabledChildNodeObjects, Func enabledContextMenuStrip, Func disabledContextMenuStrip) where TFailureMechanismContext : IFailureMechanismContext { return new TreeNodeInfo { Text = context => context.WrappedData.Name, ForeColor = context => context.WrappedData.IsRelevant ? Color.FromKnownColor(KnownColor.ControlText) : Color.FromKnownColor(KnownColor.GrayText), Image = context => Resources.FailureMechanismIcon, ChildNodeObjects = context => context.WrappedData.IsRelevant ? enabledChildNodeObjects(context) : disabledChildNodeObjects(context), ContextMenuStrip = (context, parentData, treeViewControl) => context.WrappedData.IsRelevant ? enabledContextMenuStrip(context, parentData, treeViewControl) : disabledContextMenuStrip(context, parentData, treeViewControl) }; } /// /// Creates a object for an empty probability assessment output. /// /// The function for obtaining the context menu strip. /// A object. public static TreeNodeInfo CreateEmptyProbabilityAssessmentOutputTreeNodeInfo( Func contextMenuStrip) { return new TreeNodeInfo { Text = emptyOutput => Resources.CalculationOutput_DisplayName, Image = emptyOutput => Resources.GeneralOutputIcon, ForeColor = emptyOutput => Color.FromKnownColor(KnownColor.GrayText), ContextMenuStrip = contextMenuStrip }; } #region Helper methods for CreateCalculationContextTreeNodeInfo private static bool CalculationContextCanRemove(ICalculationContext calculationContext, object parentNodeData) { var calculationGroupContext = parentNodeData as ICalculationContext; return calculationGroupContext != null && calculationGroupContext.WrappedData.Children.Contains(calculationContext.WrappedData); } #endregion # region Helper methods for CreateCalculationGroupContextTreeNodeInfo private static bool IsNestedGroup(object parentData) { return parentData is ICalculationContext; } private static bool CalculationGroupCanDropOrInsert(object draggedData, object targetData) { var calculationContext = draggedData as ICalculationContext; return calculationContext != null && ReferenceEquals(calculationContext.FailureMechanism, ((ICalculationContext) targetData).FailureMechanism); } private static void CalculationGroupOnDrop(object droppedData, object newParentData, object oldParentData, int position, TreeViewControl treeViewControl) { ICalculationBase calculationItem = ((ICalculationContext) droppedData).WrappedData; var originalOwnerContext = oldParentData as ICalculationContext; var targetContext = newParentData as ICalculationContext; if (calculationItem != null && originalOwnerContext != null && targetContext != null) { var sourceCalculationGroup = originalOwnerContext.WrappedData; var targetCalculationGroup = targetContext.WrappedData; var isMoveWithinSameContainer = ReferenceEquals(sourceCalculationGroup, targetCalculationGroup); DroppingCalculationInContainerStrategy dropHandler = GetDragDropStrategy(isMoveWithinSameContainer, sourceCalculationGroup, targetCalculationGroup); dropHandler.Execute(droppedData, calculationItem, position, treeViewControl); } } private static DroppingCalculationInContainerStrategy GetDragDropStrategy(bool isMoveWithinSameContainer, CalculationGroup sourceCalculationGroup, CalculationGroup targetCalculationGroup) { if (isMoveWithinSameContainer) { return new DroppingCalculationWithinSameContainer(sourceCalculationGroup, targetCalculationGroup); } return new DroppingCalculationToNewContainer(sourceCalculationGroup, targetCalculationGroup); } # region Nested types: DroppingCalculationInContainerStrategy and implementations /// /// Strategy pattern implementation for dealing with drag and drop of a /// onto data. /// private abstract class DroppingCalculationInContainerStrategy { protected readonly CalculationGroup TargetCalculationGroup; private readonly CalculationGroup sourceCalculationGroup; protected DroppingCalculationInContainerStrategy(CalculationGroup sourceCalculationGroup, CalculationGroup targetCalculationGroup) { this.sourceCalculationGroup = sourceCalculationGroup; TargetCalculationGroup = targetCalculationGroup; } /// /// Performs the drag and drop operation. /// /// The dragged data. /// The calculation item wrapped by . /// The index of the new position within the new owner's collection. /// The tree view control in which the drag and drop operation is performed. public virtual void Execute(object draggedData, ICalculationBase calculationBase, int newPosition, TreeViewControl treeViewControl) { MoveCalculationItemToNewOwner(calculationBase, newPosition); NotifyObservers(); } /// /// Moves the instance to its new location. /// /// The instance to be relocated. /// The index in the new /// owner within its . protected void MoveCalculationItemToNewOwner(ICalculationBase calculationBase, int position) { sourceCalculationGroup.Children.Remove(calculationBase); TargetCalculationGroup.Children.Insert(position, calculationBase); } /// /// Notifies observers of the change in state. /// protected virtual void NotifyObservers() { sourceCalculationGroup.NotifyObservers(); } } /// /// Strategy implementation for rearranging the order of an /// within a through a drag and drop action. /// private class DroppingCalculationWithinSameContainer : DroppingCalculationInContainerStrategy { /// /// Initializes a new instance of the class. /// /// The calculation group that is the target of the drag and drop operation. /// The calculation group that is the original owner of the dragged item. public DroppingCalculationWithinSameContainer(CalculationGroup sourceCalculationGroup, CalculationGroup targetCalculationGroup) : base(sourceCalculationGroup, targetCalculationGroup) {} } /// /// Strategy implementation for moving an from /// one to another using a drag and drop action. /// private class DroppingCalculationToNewContainer : DroppingCalculationInContainerStrategy { /// /// Initializes a new instance of the class. /// /// The calculation group that is the original owner of the dragged item. /// The calculation group that is the target of the drag and drop operation. public DroppingCalculationToNewContainer(CalculationGroup sourceCalculationGroup, CalculationGroup targetCalculationGroup) : base(sourceCalculationGroup, targetCalculationGroup) {} public override void Execute(object draggedData, ICalculationBase calculationBase, int newPosition, TreeViewControl treeViewControl) { MoveCalculationItemToNewOwner(calculationBase, newPosition); NotifyObservers(); // Try to start a name edit action when an item with the same name was already present if (TargetCalculationGroup.Children.Except(new[] { calculationBase }).Any(c => c.Name.Equals(calculationBase.Name))) { treeViewControl.TryRenameNodeForData(draggedData); } } protected override void NotifyObservers() { base.NotifyObservers(); TargetCalculationGroup.NotifyObservers(); } } # endregion # endregion } }