// 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;
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
}
}