|
|
|
@ -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
|
|
|
|
|
} |
|
|
|
|