// 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.Linq;
using System.Windows.Forms;
using Core.Common.Controls.TreeView;
using Core.Common.Gui;
using Core.Common.Gui.ContextMenu;
using Ringtoets.Common.Data.Calculation;
using Ringtoets.Common.Data.FailureMechanism;
using Ringtoets.Common.Forms.Helpers;
using Ringtoets.Common.Forms.PresentationObjects;
using Ringtoets.Common.Forms.Properties;
using RingtoetsCommonDataResources = Ringtoets.Common.Data.Properties.Resources;
namespace Ringtoets.Common.Forms.TreeNodeInfos
{
///
/// Factory for creating objects.
///
public static class TreeNodeInfoFactory
{
///
/// 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 child node objects.
/// The action for adding a calculation to the calculation group.
/// The gui to use.
/// A object.
public static TreeNodeInfo CreateCalculationGroupContextTreeNodeInfo(
Func childNodeObjects,
Action addCalculation,
IGui gui)
where TCalculationGroupContext : ICalculationContext
{
return new TreeNodeInfo
{
Text = context => context.WrappedData.Name,
Image = context => Resources.GeneralFolderIcon,
EnsureVisibleOnCreate = context => true,
ChildNodeObjects = childNodeObjects,
ContextMenuStrip = (context, parentData, treeViewControl) => ContextMenuStrip(context, parentData, treeViewControl, addCalculation, gui),
CanRename = (context, parentData) => IsNestedGroup(parentData),
OnNodeRenamed = (context, newName) =>
{
context.WrappedData.Name = newName;
context.NotifyObservers();
},
CanRemove = (context, parentData) => IsNestedGroup(parentData),
OnNodeRemoved = (context, parentData) =>
{
var parentGroup = (ICalculationContext) parentData;
parentGroup.WrappedData.Children.Remove(context.WrappedData);
parentGroup.NotifyObservers();
},
CanDrag = (context, parentData) => IsNestedGroup(parentData),
CanInsert = CanDropOrInsert,
CanDrop = CanDropOrInsert,
OnDrop = OnDrop
};
}
private static bool IsNestedGroup(object parentData)
{
return parentData is ICalculationContext;
}
private static ContextMenuStrip ContextMenuStrip(TCalculationGroupContext nodeData, object parentData, TreeViewControl treeViewControl, Action addCalculation, IGui gui) where TCalculationGroupContext : ICalculationContext
{
var group = nodeData.WrappedData;
var addCalculationGroupItem = new StrictContextMenuItem(
Resources.CalculationGroup_Add_CalculationGroup,
Resources.Add_calculation_group_to_calculation_group_tooltip,
Resources.AddFolderIcon,
(o, args) =>
{
var calculation = new CalculationGroup
{
Name = NamingHelper.GetUniqueName(group.Children, RingtoetsCommonDataResources.CalculationGroup_DefaultName, c => c.Name)
};
group.Children.Add(calculation);
nodeData.WrappedData.NotifyObservers();
});
var addCalculationItem = new StrictContextMenuItem(
Resources.CalculationGroup_Add_Calculation,
Resources.Add_calculation_to_calculation_group_tooltip,
Resources.FailureMechanismIcon,
(o, args) => { addCalculation(nodeData); });
var builder = gui.Get(nodeData, treeViewControl);
var isNestedGroup = IsNestedGroup(parentData);
if (!isNestedGroup)
{
builder
.AddOpenItem()
.AddSeparator();
}
builder
.AddCustomItem(addCalculationGroupItem)
.AddCustomItem(addCalculationItem)
.AddSeparator();
if (isNestedGroup)
{
builder.AddRenameItem();
builder.AddDeleteItem();
builder.AddSeparator();
}
return builder
.AddImportItem()
.AddExportItem()
.AddSeparator()
.AddExpandAllItem()
.AddCollapseAllItem()
.AddSeparator()
.AddPropertiesItem()
.Build();
}
private static bool CanDropOrInsert(object draggedData, object targetData)
{
var calculationContext = draggedData as ICalculationContext;
return calculationContext != null && ReferenceEquals(calculationContext.FailureMechanism, ((ICalculationContext)targetData).FailureMechanism);
}
private static void OnDrop(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)
{
return isMoveWithinSameContainer
? (DroppingCalculationInContainerStrategy)new DroppingCalculationWithinSameContainer(sourceCalculationGroup, targetCalculationGroup)
: new DroppingCalculationToNewContainer(sourceCalculationGroup, targetCalculationGroup);
}
#region Nested Types: DroppingPipingCalculationInContainerStrategy and implementations
///
/// Strategy pattern implementation for dealing with drag & dropping a
/// onto data.
///
private abstract class DroppingCalculationInContainerStrategy
{
private readonly CalculationGroup sourceCalculationGroup;
protected readonly CalculationGroup targetCalculationGroup;
protected DroppingCalculationInContainerStrategy(CalculationGroup sourceCalculationGroup, CalculationGroup targetCalculationGroup)
{
this.sourceCalculationGroup = sourceCalculationGroup;
this.targetCalculationGroup = targetCalculationGroup;
}
///
/// Perform the drag & 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 which is at stake.
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 & drop action.
///
private class DroppingCalculationWithinSameContainer : DroppingCalculationInContainerStrategy
{
///
/// Initializes a new instance of the class.
///
/// The calculation group that is the target of the drag & 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 & 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 & 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
}
}