// 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 Lesser 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 Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser 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.Windows.Forms; using FormsTreeView = System.Windows.Forms.TreeView; namespace Core.Common.Controls.TreeView { public class DragDropHandler { private int dropAtLocation; private Point lastDragOverPoint; private PlaceholderLocation lastPlaceholderLocation; private TreeNode nodeDropTarget; private TreeNode lastPlaceholderNode; private Graphics placeHolderGraphics; public void HandleDragDrop(TreeViewControl treeViewControl, FormsTreeView treeView, DragEventArgs e, Func getTreeNodeInfoForData) { ClearPlaceHolders(treeView); Point point = treeView.PointToClient(new Point(e.X, e.Y)); var nodeOver = treeView.GetNodeAt(point); var nodeDragging = e.Data.GetData(typeof(TreeNode)) as TreeNode; if (nodeOver == null || nodeDragging == null) { if (nodeOver != null) { e.Effect = DragDropEffects.All; } return; } // Handle dragged items which were originally higher up in the tree under the same parent (all indices shift by one) if (e.Effect.Equals(DragDropEffects.Move) && nodeDragging.Parent == nodeDropTarget && nodeOver.Index > nodeDragging.Index && dropAtLocation > 0) { dropAtLocation--; } // Ensure the drop location is never < 0 if (dropAtLocation < 0) { dropAtLocation = 0; } var treeNodeInfo = getTreeNodeInfoForData(nodeDropTarget.Tag); var previousParentNode = nodeDragging.Parent; previousParentNode.Nodes.Remove(nodeDragging); nodeDropTarget.Nodes.Insert(dropAtLocation, nodeDragging); // Ensure the dragged node is visible afterwards nodeDragging.EnsureVisible(); // Restore any lost selection treeView.SelectedNode = nodeDragging; if (treeNodeInfo.OnDrop != null) { treeNodeInfo.OnDrop(nodeDragging.Tag, nodeDragging.Parent.Tag, previousParentNode.Tag, ToDragOperation(e.Effect), dropAtLocation, treeViewControl); } } public void HandleDragOver(FormsTreeView treeView, object sender, DragEventArgs e, Func getTreeNodeInfoForData) { if (lastDragOverPoint.X == e.X && lastDragOverPoint.Y == e.Y) { return; } lastDragOverPoint = new Point(e.X, e.Y); var point = treeView.PointToClient(lastDragOverPoint); var nodeOver = treeView.GetNodeAt(point); var nodeDragging = e.Data.GetData(typeof(TreeNode)) as TreeNode; if (nodeOver == null || nodeDragging == null || nodeOver == nodeDragging || IsChildOf(nodeOver, nodeDragging)) { ClearPlaceHolders(treeView); return; } ScrollIntoView(point, nodeOver, sender); PlaceholderLocation placeholderLocation = GetPlaceHoldersLocation(treeView, nodeDragging, nodeOver, e, getTreeNodeInfoForData); if (null == nodeDropTarget) { return; } var treeNodeInfo = getTreeNodeInfoForData(nodeDropTarget.Tag); DragOperations allowedOperations = treeNodeInfo.CanDrop != null ? treeNodeInfo.CanDrop(nodeDragging.Tag, nodeDropTarget.Tag, ToDragOperation(e.AllowedEffect)) : DragOperations.None; e.Effect = ToDragDropEffects(allowedOperations); if (PlaceholderLocation.None == placeholderLocation) { return; } // Determine whether or not the node can be dropped based on the allowed operations. // A node can also be a valid drop target if it is the root item (nodeDragging.Parent == null). var dragOperations = treeNodeInfo.CanDrop != null ? treeNodeInfo.CanDrop(nodeDragging.Tag, nodeDropTarget.Tag, allowedOperations) : DragOperations.None; if (DragOperations.None != dragOperations) { DrawPlaceholder(treeView, nodeOver, placeholderLocation); } else { ClearPlaceHolders(treeView); e.Effect = DragDropEffects.None; } } public void HandleItemDrag(FormsTreeView treeView, ItemDragEventArgs e, Func getTreeNodeInfoForData) { // gather allowed effects for the current item. var sourceNode = (TreeNode) e.Item; var treeNodeInfo = getTreeNodeInfoForData(sourceNode.Tag); var parentTag = sourceNode.Parent != null ? sourceNode.Parent.Tag : null; var canDrag = treeNodeInfo.CanDrag != null && treeNodeInfo.CanDrag(sourceNode.Tag, parentTag); if (!canDrag) { return; } // store both treenode and tag of treenode in dataobject // to be dragged. IDataObject dataObject = new DataObject(); dataObject.SetData(typeof(TreeNode), sourceNode); if (sourceNode.Tag != null) { dataObject.SetData(sourceNode.Tag.GetType(), sourceNode.Tag); } treeView.DoDragDrop(dataObject, DragDropEffects.Move); } public void HandleDragLeave(FormsTreeView treeView) { ClearPlaceHolders(treeView); } private void DrawPlaceholder(FormsTreeView treeView, TreeNode node, PlaceholderLocation location) { if (lastPlaceholderNode == node && lastPlaceholderLocation == location) { return; } ClearPlaceHolders(treeView); lastPlaceholderNode = node; lastPlaceholderLocation = location; placeHolderGraphics = treeView.CreateGraphics(); node.DrawPlaceHolder(location, treeView.CreateGraphics()); } private void ClearPlaceHolders(FormsTreeView treeView) { if (placeHolderGraphics != null) { lastPlaceholderNode = null; treeView.Refresh(); placeHolderGraphics.Dispose(); placeHolderGraphics = null; } } private DragOperations ToDragOperation(DragDropEffects dragDropEffects) { return (DragOperations) Enum.Parse(typeof(DragOperations), dragDropEffects.ToString()); } private bool IsChildOf(TreeNode childNode, TreeNode node) { while (childNode != null && childNode.Parent != null) { if (childNode.Parent.Equals(node)) { return true; } childNode = childNode.Parent; // Walk up the tree } return false; } private static void ScrollIntoView(Point point, TreeNode nodeOver, object sender) { var treeView = sender as FormsTreeView; if (treeView == null) { return; } int delta = treeView.Height - point.Y; if ((delta < treeView.Height/2) && (delta > 0)) { var nextVisibleNode = nodeOver.NextVisibleNode; if (nextVisibleNode != null) { nextVisibleNode.EnsureVisible(); } } if ((delta > treeView.Height/2) && (delta < treeView.Height)) { var previousVisibleNode = nodeOver.PrevVisibleNode; if (previousVisibleNode != null) { previousVisibleNode.EnsureVisible(); } } } private PlaceholderLocation GetPlaceHoldersLocation(FormsTreeView treeView, TreeNode nodeDragging, TreeNode nodeOver, DragEventArgs e, Func getTreeNodeInfoForData) { var loc = PlaceholderLocation.None; int offsetY = treeView.PointToClient(Cursor.Position).Y - nodeOver.Bounds.Top; if (offsetY < nodeOver.Bounds.Height/3 && nodeDragging.NextNode != nodeOver) { if (nodeOver.Parent != null) { var treeNodeInfo = getTreeNodeInfoForData(nodeOver.Parent.Tag); if (treeNodeInfo.CanInsert != null && treeNodeInfo.CanInsert(nodeDragging.Tag, nodeOver.Tag)) { nodeDropTarget = nodeOver.Parent; dropAtLocation = nodeOver.Parent == null ? 0 : nodeOver.Parent.Nodes.IndexOf(nodeOver); loc = PlaceholderLocation.Top; } else { nodeDropTarget = nodeOver; dropAtLocation = 0; loc = PlaceholderLocation.Middle; } } else { nodeDropTarget = nodeOver; dropAtLocation = 0; loc = PlaceholderLocation.Middle; } } else if ((nodeOver.Parent != null) && (offsetY > nodeOver.Bounds.Height - nodeOver.Bounds.Height/3) && nodeDragging.PrevNode != nodeOver) { var treeNodeInfo = getTreeNodeInfoForData(nodeOver.Parent.Tag); if (treeNodeInfo.CanInsert != null && treeNodeInfo.CanInsert(nodeDragging.Tag, nodeOver.Tag)) { nodeDropTarget = nodeOver.Parent; dropAtLocation = nodeOver.Parent == null ? 0 : nodeOver.Parent.Nodes.IndexOf(nodeOver) + 1; loc = PlaceholderLocation.Bottom; } else { nodeDropTarget = nodeOver; dropAtLocation = 0; loc = PlaceholderLocation.Middle; } } else if (nodeDragging != nodeOver && offsetY < nodeOver.Bounds.Height - nodeOver.Bounds.Height/4 && offsetY > nodeOver.Bounds.Height/4) { nodeDropTarget = nodeOver; dropAtLocation = 0; loc = PlaceholderLocation.Middle; } if (loc == PlaceholderLocation.None || (loc == PlaceholderLocation.Middle && nodeDropTarget == nodeDragging.Parent)) { ClearPlaceHolders(treeView); e.Effect = DragDropEffects.None; } return loc; } private DragDropEffects ToDragDropEffects(DragOperations operation) { return (DragDropEffects) Enum.Parse(typeof(DragDropEffects), operation.ToString()); } } }