using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using Core.Common.Base.Service;
using Core.Common.Controls;
using Core.Common.Controls.Swf.TreeViewControls;
using Core.Common.Gui;
using Core.Common.Gui.ContextMenu;
using Ringtoets.Common.Forms.Helpers;
using Ringtoets.Common.Forms.NodePresenters;
using Ringtoets.Piping.Data;
using Ringtoets.Piping.Data.Properties;
using Ringtoets.Piping.Forms.PresentationObjects;
using Ringtoets.Piping.Service;
using RingtoetsFormsResources = Ringtoets.Common.Forms.Properties.Resources;
using PipingFormsResources = Ringtoets.Piping.Forms.Properties.Resources;
using BaseResources = Core.Common.Base.Properties.Resources;
namespace Ringtoets.Piping.Forms.NodePresenters
{
///
/// Node presenter class for instances.
///
public class PipingCalculationGroupContextNodePresenter : RingtoetsNodePresenterBase
{
///
/// Creates a new instance of , which uses the
/// to create and bind its .
///
/// The
/// to use for building a .
/// Thrown when no was provided.
public PipingCalculationGroupContextNodePresenter(IContextMenuBuilderProvider contextMenuBuilderProvider) : base(contextMenuBuilderProvider) {}
///
/// Injection points for a method to cause an to be scheduled for execution.
///
public Action> RunActivitiesAction { private get; set; }
public override DragOperations CanDrop(object item, ITreeNode sourceNode, ITreeNode targetNode, DragOperations validOperations)
{
if (GetAsIPipingCalculationItem(item) != null && NodesHaveSameParentFailureMechanism(sourceNode, targetNode))
{
return validOperations;
}
return base.CanDrop(item, sourceNode, targetNode, validOperations);
}
public override bool CanInsert(object item, ITreeNode sourceNode, ITreeNode targetNode)
{
return GetAsIPipingCalculationItem(item) != null && NodesHaveSameParentFailureMechanism(sourceNode, targetNode);
}
public override bool CanRenameNode(ITreeNode node)
{
return node.Parent == null || !(node.Parent.Tag is PipingFailureMechanism);
}
public override bool CanRenameNodeTo(ITreeNode node, string newName)
{
return true;
}
protected override void UpdateNode(ITreeNode parentNode, ITreeNode node, PipingCalculationGroupContext nodeData)
{
node.Text = nodeData.WrappedData.Name;
node.Image = PipingFormsResources.FolderIcon;
node.ForegroundColor = Color.FromKnownColor(KnownColor.ControlText);
}
protected override void OnNodeRenamed(PipingCalculationGroupContext nodeData, string newName)
{
nodeData.WrappedData.Name = newName;
nodeData.NotifyObservers();
}
protected override bool CanRemove(object parentNodeData, PipingCalculationGroupContext nodeData)
{
var group = parentNodeData as PipingCalculationGroupContext;
if (group != null)
{
return group.WrappedData.Children.Contains(nodeData.WrappedData);
}
return base.CanRemove(parentNodeData, nodeData);
}
protected override bool RemoveNodeData(object parentNodeData, PipingCalculationGroupContext nodeData)
{
var group = parentNodeData as PipingCalculationGroupContext;
if (group != null)
{
var removeNodeData = group.WrappedData.Children.Remove(nodeData.WrappedData);
group.NotifyObservers();
return removeNodeData;
}
return base.RemoveNodeData(parentNodeData, nodeData);
}
protected override ContextMenuStrip GetContextMenu(ITreeNode node, PipingCalculationGroupContext nodeData)
{
var group = nodeData.WrappedData;
var addCalculationGroupItem = new StrictContextMenuItem(
PipingFormsResources.PipingCalculationGroup_Add_PipingCalculationGroup,
PipingFormsResources.PipingCalculationGroup_Add_PipingCalculationGroup_ToolTip,
PipingFormsResources.AddFolderIcon, (o, args) =>
{
var newGroup = new PipingCalculationGroup
{
Name = NamingHelper.GetUniqueName(group.Children, Resources.PipingCalculationGroup_DefaultName, c => c.Name)
};
group.Children.Add(newGroup);
nodeData.NotifyObservers();
SelectNewlyAddedItemInTreeView(node);
});
var addCalculationItem = new StrictContextMenuItem(
PipingFormsResources.PipingCalculationGroup_Add_PipingCalculation,
PipingFormsResources.PipingCalculationGroup_Add_PipingCalculation_ToolTip,
PipingFormsResources.PipingIcon, (o, args) =>
{
var calculation = new PipingCalculation
{
Name = NamingHelper.GetUniqueName(group.Children, Resources.PipingCalculation_DefaultName, c => c.Name)
};
group.Children.Add(calculation);
nodeData.NotifyObservers();
SelectNewlyAddedItemInTreeView(node);
});
var validateAllItem = new StrictContextMenuItem(
PipingFormsResources.PipingCalculationItem_Validate,
PipingFormsResources.PipingCalculationGroup_Validate_ToolTip,
PipingFormsResources.ValidationIcon, (o, args) =>
{
foreach (PipingCalculation calculation in group.Children.GetPipingCalculations())
{
PipingCalculationService.Validate(calculation);
}
});
var calculateAllItem = new StrictContextMenuItem(
RingtoetsFormsResources.Calculate_all,
PipingFormsResources.PipingCalculationGroup_CalculateAll_ToolTip,
RingtoetsFormsResources.CalculateAllIcon, (o, args) => { RunActivitiesAction(group.GetPipingCalculations().Select(pc => new PipingCalculationActivity(pc))); });
var clearAllItem = new StrictContextMenuItem(
RingtoetsFormsResources.Clear_all_output,
PipingFormsResources.PipingCalculationGroup_ClearOutput_ToolTip,
RingtoetsFormsResources.ClearIcon, (o, args) =>
{
if (MessageBox.Show(PipingFormsResources.PipingCalculationGroupContextNodePresenter_GetContextMenu_Are_you_sure_clear_all_output, BaseResources.Confirm, MessageBoxButtons.OKCancel) != DialogResult.OK)
{
return;
}
foreach (PipingCalculation calc in group.GetPipingCalculations().Where(c => c.HasOutput))
{
calc.ClearOutput();
calc.NotifyObservers();
}
});
if (!nodeData.WrappedData.GetPipingCalculations().Any(c => c.HasOutput))
{
clearAllItem.Enabled = false;
clearAllItem.ToolTipText = PipingFormsResources.PipingCalculationGroup_ClearOutput_No_calculation_with_output_to_clear;
}
return contextMenuBuilderProvider.Get(node)
.AddCustomItem(addCalculationGroupItem)
.AddCustomItem(addCalculationItem)
.AddSeparator()
.AddCustomItem(validateAllItem)
.AddCustomItem(calculateAllItem)
.AddCustomItem(clearAllItem)
.AddSeparator()
.AddDeleteItem()
.AddSeparator()
.AddImportItem()
.AddExportItem()
.AddSeparator()
.AddExpandAllItem()
.AddCollapseAllItem()
.AddSeparator()
.AddPropertiesItem()
.Build();
}
protected override IEnumerable GetChildNodeObjects(PipingCalculationGroupContext nodeData)
{
foreach (IPipingCalculationItem item in nodeData.WrappedData.Children)
{
var calculation = item as PipingCalculation;
var group = item as PipingCalculationGroup;
if (calculation != null)
{
yield return new PipingCalculationContext(calculation,
nodeData.AvailablePipingSurfaceLines,
nodeData.AvailablePipingSoilProfiles);
}
else if (group != null)
{
yield return new PipingCalculationGroupContext(group,
nodeData.AvailablePipingSurfaceLines,
nodeData.AvailablePipingSoilProfiles);
}
else
{
yield return item;
}
}
}
protected override DragOperations CanDrag(PipingCalculationGroupContext nodeData)
{
if (TreeView.GetNodeByTag(nodeData).Parent.Tag is PipingFailureMechanism)
{
return DragOperations.None;
}
return DragOperations.Move;
}
protected override void OnDragDrop(object item, object itemParent, PipingCalculationGroupContext target, DragOperations operation, int position)
{
IPipingCalculationItem pipingCalculationItem = GetAsIPipingCalculationItem(item);
var originalOwnerContext = itemParent as PipingCalculationGroupContext;
if (pipingCalculationItem != null && originalOwnerContext != null)
{
var isMoveWithinSameContainer = ReferenceEquals(target, originalOwnerContext);
DroppingPipingCalculationInContainerStrategy dropHandler = GetDragDropStrategy(isMoveWithinSameContainer, originalOwnerContext, target);
dropHandler.Execute(item, pipingCalculationItem, position);
}
else
{
base.OnDragDrop(item, itemParent, target, operation, position);
}
}
private static IPipingCalculationItem GetAsIPipingCalculationItem(object item)
{
var calculationContext = item as PipingCalculationContext;
if (calculationContext != null)
{
return calculationContext.WrappedData;
}
var groupContext = item as PipingCalculationGroupContext;
if (groupContext != null)
{
return groupContext.WrappedData;
}
return null;
}
private DroppingPipingCalculationInContainerStrategy GetDragDropStrategy(bool isMoveWithinSameContainer, PipingCalculationGroupContext originalOwnerContext, PipingCalculationGroupContext target)
{
if (isMoveWithinSameContainer)
{
return new DroppingPipingCalculationWithinSameContainer(TreeView, originalOwnerContext, target);
}
return new DroppingPipingCalculationToNewContainer(TreeView, originalOwnerContext, target);
}
private bool NodesHaveSameParentFailureMechanism(ITreeNode sourceNode, ITreeNode targetNode)
{
var sourceFailureMechanism = GetParentFailureMechanism(sourceNode);
var targetFailureMechanism = GetParentFailureMechanism(targetNode);
return ReferenceEquals(sourceFailureMechanism, targetFailureMechanism);
}
private static PipingFailureMechanism GetParentFailureMechanism(ITreeNode sourceNode)
{
PipingFailureMechanism sourceFailureMechanism;
var node = sourceNode;
while ((sourceFailureMechanism = node.Tag as PipingFailureMechanism) == null)
{
// No parent found, go search higher up hierarchy!
node = node.Parent;
if (node == null)
{
break;
}
}
return sourceFailureMechanism;
}
private void SelectNewlyAddedItemInTreeView(ITreeNode node)
{
// Expand parent of 'newItem' to ensure its selected state is visible.
if (!node.IsExpanded)
{
node.Expand();
}
ITreeNode newlyAppendedNodeForNewItem = node.Nodes.Last();
TreeView.SelectedNode = newlyAppendedNodeForNewItem;
}
#region Nested Types: DroppingPipingCalculationInContainerStrategy and implementations
///
/// Strategy pattern implementation for dealing with drag & dropping a
/// onto data.
///
private abstract class DroppingPipingCalculationInContainerStrategy
{
protected readonly ITreeView treeView;
protected readonly PipingCalculationGroupContext target;
private readonly PipingCalculationGroupContext originalOwnerContext;
protected DroppingPipingCalculationInContainerStrategy(ITreeView treeView, PipingCalculationGroupContext originalOwnerContext, PipingCalculationGroupContext target)
{
this.treeView = treeView;
this.originalOwnerContext = originalOwnerContext;
this.target = target;
}
///
/// Perform the drag & drop operation.
///
/// The actual dragged data object.
/// The piping calculation item corresponding with .
/// The index of the new position within the new owner's collection.
public virtual void Execute(object draggedDataObject, IPipingCalculationItem pipingCalculationItem, int newPosition)
{
var targetRecordedNodeState = new TreeNodeExpandCollapseState(treeView.GetNodeByTag(target));
MoveCalculationItemToNewOwner(pipingCalculationItem, newPosition);
NotifyObservers();
ITreeNode draggedNode = treeView.GetNodeByTag(draggedDataObject);
UpdateTreeView(draggedNode, targetRecordedNodeState);
}
///
/// Moves the instance to its new location.
///
/// The instance to be relocated.
/// The index in the new
/// owner within its .
protected void MoveCalculationItemToNewOwner(IPipingCalculationItem pipingCalculationItem, int position)
{
originalOwnerContext.WrappedData.Children.Remove(pipingCalculationItem);
target.WrappedData.Children.Insert(position, pipingCalculationItem);
}
///
/// Notifies observers of the change in state.
///
protected virtual void NotifyObservers()
{
originalOwnerContext.NotifyObservers();
}
///
/// Updates the where the drag & drop
/// operation took place.
///
/// The dragged node.
/// Recorded state of the target node
/// before the drag & drop operation.
protected virtual void UpdateTreeView(ITreeNode draggedNode, TreeNodeExpandCollapseState targetRecordedNodeState)
{
HandlePostDragExpandCollapseOfNewOwner(draggedNode.Parent, targetRecordedNodeState);
treeView.SelectedNode = draggedNode;
}
private static void HandlePostDragExpandCollapseOfNewOwner(ITreeNode parentOfDraggedNode, TreeNodeExpandCollapseState newOwnerRecordedNodeState)
{
newOwnerRecordedNodeState.Restore(parentOfDraggedNode);
// Expand parent of 'draggedNode' to ensure 'draggedNode' is visible.
if (!parentOfDraggedNode.IsExpanded)
{
parentOfDraggedNode.Expand();
}
}
}
///
/// Strategy implementation for rearranging the order of an
/// within a through a drag & drop action.
///
private class DroppingPipingCalculationWithinSameContainer : DroppingPipingCalculationInContainerStrategy
{
///
/// Initializes a new instance of the class.
///
/// The tree view where the drag & drop operation occurs.
/// The calculation group context that is
/// the original owner of the dragged item.
/// The calculation group context that is the target
/// of the drag & drop operation.
public DroppingPipingCalculationWithinSameContainer(ITreeView treeView, PipingCalculationGroupContext originalOwnerContext, PipingCalculationGroupContext target) :
base(treeView, originalOwnerContext, target) {}
}
///
/// Strategy implementation for moving an from
/// one to another using a drag & drop action.
///
private class DroppingPipingCalculationToNewContainer : DroppingPipingCalculationInContainerStrategy
{
private TreeNodeExpandCollapseState recordedNodeState;
private bool renamed;
///
/// Initializes a new instance of the class.
///
/// The tree view where the drag & drop operation occurs.
/// The calculation group context that is
/// the original owner of the dragged item.
/// The calculation group context that is the target
/// of the drag & drop operation.
public DroppingPipingCalculationToNewContainer(ITreeView treeView, PipingCalculationGroupContext originalOwnerContext, PipingCalculationGroupContext target) :
base(treeView, originalOwnerContext, target) {}
public override void Execute(object draggedDataObject, IPipingCalculationItem pipingCalculationItem, int newPosition)
{
var targetRecordedNodeState = new TreeNodeExpandCollapseState(treeView.GetNodeByTag(target));
recordedNodeState = new TreeNodeExpandCollapseState(treeView.GetNodeByTag(draggedDataObject));
EnsureDraggedNodeHasUniqueNameInNewOwner(pipingCalculationItem, target);
MoveCalculationItemToNewOwner(pipingCalculationItem, newPosition);
NotifyObservers();
ITreeNode draggedNode = treeView.GetNodeByTag(draggedDataObject);
UpdateTreeView(draggedNode, targetRecordedNodeState);
}
protected override void UpdateTreeView(ITreeNode draggedNode, TreeNodeExpandCollapseState targetRecordedNodeState)
{
base.UpdateTreeView(draggedNode, targetRecordedNodeState);
recordedNodeState.Restore(draggedNode);
if (renamed)
{
treeView.StartLabelEdit();
}
}
protected override void NotifyObservers()
{
base.NotifyObservers();
target.NotifyObservers();
}
private void EnsureDraggedNodeHasUniqueNameInNewOwner(IPipingCalculationItem pipingCalculationItem, PipingCalculationGroupContext newOwner)
{
renamed = false;
string uniqueName = NamingHelper.GetUniqueName(newOwner.WrappedData.Children, pipingCalculationItem.Name, pci => pci.Name);
if (!pipingCalculationItem.Name.Equals(uniqueName))
{
renamed = TryRenameTo(pipingCalculationItem, uniqueName);
}
}
private static bool TryRenameTo(IPipingCalculationItem pipingCalculationItem, string newName)
{
var calculation = pipingCalculationItem as PipingCalculation;
if (calculation != null)
{
calculation.Name = newName;
return true;
}
var group = pipingCalculationItem as PipingCalculationGroup;
if (group != null && group.IsNameEditable)
{
group.Name = newName;
return true;
}
return false;
}
}
#endregion
}
}