Browse Source

SharpTreeView: implemented node removal.

pull/10/head
Daniel Grunwald 15 years ago
parent
commit
c942248aaa
  1. 190
      SharpTreeView/FlatListTreeNode.cs
  2. 24
      SharpTreeView/SharpTreeNode.cs
  3. 51
      SharpTreeView/SharpTreeNodeCollection.cs

190
SharpTreeView/FlatListTreeNode.cs

@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace ICSharpCode.TreeView
@ -32,6 +33,21 @@ namespace ICSharpCode.TreeView @@ -32,6 +33,21 @@ namespace ICSharpCode.TreeView
return node != null ? node.height : 0;
}
SharpTreeNode GetListRoot()
{
SharpTreeNode node = this;
while (node.listParent != null)
node = node.listParent;
return node;
}
#region Debugging
[Conditional("DEBUG")]
void CheckRootInvariants()
{
GetListRoot().CheckInvariants();
}
[Conditional("DEBUG")]
void CheckInvariants()
{
@ -44,13 +60,26 @@ namespace ICSharpCode.TreeView @@ -44,13 +60,26 @@ namespace ICSharpCode.TreeView
if (right != null) right.CheckInvariants();
}
SharpTreeNode GetListRoot()
[Conditional("DEBUG")]
static void DumpTree(SharpTreeNode node)
{
SharpTreeNode node = this;
while (node.listParent != null)
node = node.listParent;
return node;
node.GetListRoot().DumpTree();
}
[Conditional("DEBUG")]
void DumpTree()
{
Debug.Indent();
if (left != null)
left.DumpTree();
Debug.Unindent();
Debug.WriteLine("{0}, totalListLength={1}, height={2}, Balance={3}, isVisible={4}", ToString(), totalListLength, height, Balance, isVisible);
Debug.Indent();
if (right != null)
right.DumpTree();
Debug.Unindent();
}
#endregion
#region GetNodeByVisibleIndex / GetVisibleIndexForNode
internal static SharpTreeNode GetNodeByVisibleIndex(SharpTreeNode root, int index)
@ -189,6 +218,28 @@ namespace ICSharpCode.TreeView @@ -189,6 +218,28 @@ namespace ICSharpCode.TreeView
newTop.right = Rebalance(this);
return newTop;
}
static void RebalanceUntilRoot(SharpTreeNode pos)
{
while (pos.listParent != null) {
if (pos == pos.listParent.left) {
pos = pos.listParent.left = Rebalance(pos);
} else {
Debug.Assert(pos == pos.listParent.right);
pos = pos.listParent.right = Rebalance(pos);
}
pos = pos.listParent;
}
SharpTreeNode newRoot = Rebalance(pos);
if (newRoot != pos && pos.treeFlattener != null) {
Debug.Assert(newRoot.treeFlattener == null);
newRoot.treeFlattener = pos.treeFlattener;
pos.treeFlattener = null;
newRoot.treeFlattener.root = newRoot;
}
Debug.Assert(newRoot.listParent == null);
newRoot.CheckInvariants();
}
#endregion
#region Insertion
@ -210,47 +261,116 @@ namespace ICSharpCode.TreeView @@ -210,47 +261,116 @@ namespace ICSharpCode.TreeView
}
RebalanceUntilRoot(pos);
}
#endregion
static void RebalanceUntilRoot(SharpTreeNode pos)
#region Removal
void RemoveNodes(SharpTreeNode start, SharpTreeNode end)
{
while (pos.listParent != null) {
if (pos == pos.listParent.left) {
pos = pos.listParent.left = Rebalance(pos);
} else {
Debug.Assert(pos == pos.listParent.right);
pos = pos.listParent.right = Rebalance(pos);
// Removes all nodes from start to end (inclusive)
// All removed nodes will be reorganized in a separate tree, do not delete
// regions that don't belong together in the tree model!
List<SharpTreeNode> removedSubtrees = new List<SharpTreeNode>();
SharpTreeNode oldPos;
SharpTreeNode pos = start;
do {
// recalculate the endAncestors every time, because the tree might have been rebalanced
HashSet<SharpTreeNode> endAncestors = new HashSet<SharpTreeNode>();
for (SharpTreeNode tmp = end; tmp != null; tmp = tmp.listParent)
endAncestors.Add(tmp);
removedSubtrees.Add(pos);
if (!endAncestors.Contains(pos)) {
// we can remove pos' right subtree in a single step:
if (pos.right != null) {
removedSubtrees.Add(pos.right);
pos.right.listParent = null;
pos.right = null;
}
pos = pos.listParent;
}
SharpTreeNode newRoot = Rebalance(pos);
if (newRoot != pos && pos.treeFlattener != null) {
Debug.Assert(newRoot.treeFlattener == null);
newRoot.treeFlattener = pos.treeFlattener;
pos.treeFlattener = null;
newRoot.treeFlattener.root = newRoot;
DeleteNode(pos); // this will also rebalance out the deletion of the right subtree
oldPos = pos;
pos = pos.Successor();
} while (oldPos != end);
// merge back together the removed subtrees:
}
Debug.Assert(newRoot.listParent == null);
newRoot.CheckInvariants();
SharpTreeNode Successor()
{
SharpTreeNode node = this;
SharpTreeNode oldNode;
do {
oldNode = node;
node = node.listParent;
// loop while we are on the way up from the right part
} while (node != null && node.right == oldNode);
return node;
}
[Conditional("DEBUG")]
static void DumpTree(SharpTreeNode node)
static void DeleteNode(SharpTreeNode node)
{
node.GetListRoot().DumpTree();
SharpTreeNode balancingNode;
if (node.left == null) {
balancingNode = node.listParent;
node.ReplaceWith(node.right);
node.right = null;
} else if (node.right == null) {
balancingNode = node.listParent;
node.ReplaceWith(node.left);
node.left = null;
} else {
SharpTreeNode tmp = node.right;
while (tmp.left != null)
tmp = tmp.left;
// First replace tmp with tmp.right
balancingNode = tmp.listParent;
tmp.ReplaceWith(tmp.right);
tmp.right = null;
Debug.Assert(tmp.left == null);
Debug.Assert(tmp.listParent == null);
// Now move node's children to tmp:
tmp.left = node.left; node.left = null;
tmp.right = node.right; node.right = null;
if (tmp.left != null) tmp.left.listParent = tmp;
if (tmp.right != null) tmp.right.listParent = tmp;
// Then replace node with tmp
node.ReplaceWith(tmp);
if (balancingNode == node)
balancingNode = tmp;
}
Debug.Assert(node.listParent == null);
Debug.Assert(node.left == null);
Debug.Assert(node.right == null);
if (balancingNode != null)
RebalanceUntilRoot(balancingNode);
}
[Conditional("DEBUG")]
void DumpTree()
void ReplaceWith(SharpTreeNode node)
{
Debug.Indent();
if (left != null)
left.DumpTree();
Debug.Unindent();
Debug.WriteLine("{0}, totalListLength={1}, height={2}, Balance={3}, isVisible={4}", ToString(), totalListLength, height, Balance, isVisible);
Debug.Indent();
if (right != null)
right.DumpTree();
Debug.Unindent();
if (listParent != null) {
if (listParent.left == this) {
listParent.left = node;
} else {
Debug.Assert(listParent.right == this);
listParent.right = node;
}
if (node != null)
node.listParent = listParent;
listParent = null;
} else {
// this was a root node
Debug.Assert(node != null); // cannot delete the only node in the tree
node.listParent = null;
if (treeFlattener != null) {
Debug.Assert(node.treeFlattener == null);
node.treeFlattener = this.treeFlattener;
this.treeFlattener = null;
node.treeFlattener.root = node;
}
}
}
#endregion
}

24
SharpTreeView/SharpTreeNode.cs

@ -42,7 +42,7 @@ namespace ICSharpCode.TreeView @@ -42,7 +42,7 @@ namespace ICSharpCode.TreeView
// Validate our invariants:
if (updateFlattener)
GetListRoot().CheckInvariants();
CheckRootInvariants();
// Tell the flattener about the removed nodes:
if (removedNodes != null) {
@ -154,9 +154,28 @@ namespace ICSharpCode.TreeView @@ -154,9 +154,28 @@ namespace ICSharpCode.TreeView
{
if (e.OldItems != null) {
foreach (SharpTreeNode node in e.OldItems) {
throw new NotImplementedException();
Debug.Assert(node.modelParent == this);
node.modelParent = null;
Debug.WriteLine("Removing {0} from {1}", node, this);
SharpTreeNode removeEnd = node;
while (removeEnd.modelChildren != null && removeEnd.modelChildren.Count > 0)
removeEnd = removeEnd.modelChildren.Last();
List<SharpTreeNode> removedNodes = null;
int visibleIndexOfRemoval = 0;
if (node.isVisible) {
visibleIndexOfRemoval = GetVisibleIndexForNode(node);
removedNodes = node.VisibleDescendantsAndSelf().ToList();
}
RemoveNodes(node, removeEnd);
if (removedNodes != null) {
var flattener = GetListRoot().treeFlattener;
if (flattener != null) {
flattener.NodesRemoved(visibleIndexOfRemoval, removedNodes);
}
}
}
}
if (e.NewItems != null) {
@ -165,6 +184,7 @@ namespace ICSharpCode.TreeView @@ -165,6 +184,7 @@ namespace ICSharpCode.TreeView
insertionPos = null;
else
insertionPos = modelChildren[e.NewStartingIndex - 1];
foreach (SharpTreeNode node in e.NewItems) {
Debug.Assert(node.modelParent == null);
node.modelParent = this;

51
SharpTreeView/SharpTreeNodeCollection.cs

@ -93,6 +93,19 @@ namespace ICSharpCode.TreeView @@ -93,6 +93,19 @@ namespace ICSharpCode.TreeView
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, node, index));
}
public void InsertRange(int index, IEnumerable<SharpTreeNode> nodes)
{
if (nodes == null)
throw new ArgumentNullException("nodes");
ThrowOnReentrancy();
List<SharpTreeNode> newNodes = nodes.ToList();
foreach (SharpTreeNode node in newNodes) {
ThrowIfValueIsNullOrHasParent(node);
}
list.InsertRange(index, newNodes);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newNodes, index));
}
public void RemoveAt(int index)
{
ThrowOnReentrancy();
@ -101,6 +114,14 @@ namespace ICSharpCode.TreeView @@ -101,6 +114,14 @@ namespace ICSharpCode.TreeView
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItem, index));
}
public void RemoveRange(int index, int count)
{
ThrowOnReentrancy();
var oldItems = list.GetRange(index, count);
list.RemoveRange(index, count);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItems, index));
}
public void Add(SharpTreeNode node)
{
ThrowOnReentrancy();
@ -109,6 +130,11 @@ namespace ICSharpCode.TreeView @@ -109,6 +130,11 @@ namespace ICSharpCode.TreeView
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, node, list.Count - 1));
}
public void AddRange(IEnumerable<SharpTreeNode> nodes)
{
InsertRange(this.Count, nodes);
}
public void Clear()
{
ThrowOnReentrancy();
@ -147,5 +173,30 @@ namespace ICSharpCode.TreeView @@ -147,5 +173,30 @@ namespace ICSharpCode.TreeView
{
return list.GetEnumerator();
}
public void BindToObservableCollection<T>(ObservableCollection<T> collection) where T : SharpTreeNode
{
Clear();
AddRange(collection);
collection.CollectionChanged += delegate(object sender, NotifyCollectionChangedEventArgs e) {
switch (e.Action) {
case NotifyCollectionChangedAction.Add:
InsertRange(e.NewStartingIndex, e.NewItems.Cast<SharpTreeNode>());
break;
case NotifyCollectionChangedAction.Remove:
RemoveRange(e.OldStartingIndex, e.OldItems.Count);
break;
case NotifyCollectionChangedAction.Replace:
case NotifyCollectionChangedAction.Move:
throw new NotImplementedException();
case NotifyCollectionChangedAction.Reset:
Clear();
AddRange(collection);
break;
default:
throw new NotSupportedException("Invalid value for NotifyCollectionChangedAction");
}
};
}
}
}

Loading…
Cancel
Save