Browse Source

Reimplemented expanding/collapsing.

pull/10/head
Daniel Grunwald 14 years ago
parent
commit
54e170cae5
  1. 1
      ILSpy/AssemblyList.cs
  2. 4
      ILSpy/TreeNodes/AssemblyListTreeNode.cs
  3. 3
      ILSpy/TreeNodes/BaseTypesTreeNode.cs
  4. 3
      ILSpy/TreeNodes/ReferenceFolderTreeNode.cs
  5. 3
      ILSpy/TreeNodes/ResourceListTreeNode.cs
  6. 73
      SharpTreeView/FlatListTreeNode.cs
  7. 80
      SharpTreeView/SharpTreeNode.cs
  8. 2
      SharpTreeView/SharpTreeNodeView.cs
  9. 2
      SharpTreeView/SharpTreeView.cs
  10. 25
      SharpTreeView/TreeFlattener.cs

1
ILSpy/AssemblyList.cs

@ -134,6 +134,7 @@ namespace ICSharpCode.ILSpy
{ {
if (def == null) if (def == null)
return null; return null;
App.Current.Dispatcher.VerifyAccess();
if (def.DeclaringType != null) { if (def.DeclaringType != null) {
TypeTreeNode decl = FindTypeNode(def.DeclaringType); TypeTreeNode decl = FindTypeNode(def.DeclaringType);
if (decl != null) { if (decl != null) {

4
ILSpy/TreeNodes/AssemblyListTreeNode.cs

@ -45,6 +45,10 @@ namespace ICSharpCode.ILSpy.TreeNodes
this.assemblyList = assemblyList; this.assemblyList = assemblyList;
} }
public override object Text {
get { return assemblyList.ListName; }
}
/* /*
public override DropEffect CanDrop(IDataObject data, DropEffect requestedEffect) public override DropEffect CanDrop(IDataObject data, DropEffect requestedEffect)
{ {

3
ILSpy/TreeNodes/BaseTypesTreeNode.cs

@ -19,6 +19,7 @@
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Windows.Threading;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.TreeView; using ICSharpCode.TreeView;
@ -63,7 +64,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
EnsureLazyChildren(); App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren));
foreach (var child in this.Children) { foreach (var child in this.Children) {
child.Decompile(language, output, options); child.Decompile(language, output, options);
} }

3
ILSpy/TreeNodes/ReferenceFolderTreeNode.cs

@ -17,6 +17,7 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Windows.Threading;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.TreeView; using ICSharpCode.TreeView;
using Mono.Cecil; using Mono.Cecil;
@ -60,7 +61,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
EnsureLazyChildren(); App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren));
foreach (var child in this.Children) { foreach (var child in this.Children) {
child.Decompile(language, output, options); child.Decompile(language, output, options);
} }

3
ILSpy/TreeNodes/ResourceListTreeNode.cs

@ -4,6 +4,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Windows.Threading;
using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
@ -51,7 +52,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
EnsureLazyChildren(); App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren));
foreach (var child in this.Children) { foreach (var child in this.Children) {
child.Decompile(language, output, options); child.Decompile(language, output, options);
output.WriteLine(); output.WriteLine();

73
SharpTreeView/FlatListTreeNode.cs

@ -20,8 +20,8 @@ namespace ICSharpCode.TreeView
/// <summary>Subtree height in the flat list tree</summary> /// <summary>Subtree height in the flat list tree</summary>
byte height = 1; byte height = 1;
/// <summary>Length in the flat list, including children (children within the flat list).</summary> /// <summary>Length in the flat list, including children (children within the flat list). -1 = invalidated</summary>
internal int totalListLength = 1; int totalListLength = -1;
int Balance { int Balance {
get { return Height(right) - Height(left); } get { return Height(right) - Height(left); }
@ -39,14 +39,23 @@ namespace ICSharpCode.TreeView
Debug.Assert(right == null || right.listParent == this); Debug.Assert(right == null || right.listParent == this);
Debug.Assert(height == 1 + Math.Max(Height(left), Height(right))); Debug.Assert(height == 1 + Math.Max(Height(left), Height(right)));
Debug.Assert(Math.Abs(this.Balance) <= 1); Debug.Assert(Math.Abs(this.Balance) <= 1);
Debug.Assert(totalListLength == (left != null ? left.totalListLength : 0) + (isVisible ? 1 : 0) + (right != null ? right.totalListLength : 0)); Debug.Assert(totalListLength == -1 || totalListLength == (left != null ? left.totalListLength : 0) + (isVisible ? 1 : 0) + (right != null ? right.totalListLength : 0));
if (left != null) left.CheckInvariants(); if (left != null) left.CheckInvariants();
if (right != null) right.CheckInvariants(); if (right != null) right.CheckInvariants();
} }
SharpTreeNode GetListRoot()
{
SharpTreeNode node = this;
while (node.listParent != null)
node = node.listParent;
return node;
}
#region GetNodeByVisibleIndex / GetVisibleIndexForNode #region GetNodeByVisibleIndex / GetVisibleIndexForNode
internal static SharpTreeNode GetNodeByVisibleIndex(SharpTreeNode root, int index) internal static SharpTreeNode GetNodeByVisibleIndex(SharpTreeNode root, int index)
{ {
root.GetTotalListLength(); // ensure all list lengths are calculated
Debug.Assert(index >= 0); Debug.Assert(index >= 0);
Debug.Assert(index < root.totalListLength); Debug.Assert(index < root.totalListLength);
SharpTreeNode node = root; SharpTreeNode node = root;
@ -69,12 +78,12 @@ namespace ICSharpCode.TreeView
internal static int GetVisibleIndexForNode(SharpTreeNode node) internal static int GetVisibleIndexForNode(SharpTreeNode node)
{ {
int index = 0; int index = node.left != null ? node.left.GetTotalListLength() : 0;
while (node.listParent != null) { while (node.listParent != null) {
if (node == node.listParent.right) { if (node == node.listParent.right) {
if (node.listParent.left != null) if (node.listParent.left != null)
index += node.listParent.left.totalListLength; index += node.listParent.left.GetTotalListLength();
if (node.isVisible) if (node.listParent.isVisible)
index++; index++;
} }
node = node.listParent; node = node.listParent;
@ -91,6 +100,8 @@ namespace ICSharpCode.TreeView
/// <returns>The new root node</returns> /// <returns>The new root node</returns>
static SharpTreeNode Rebalance(SharpTreeNode node) static SharpTreeNode Rebalance(SharpTreeNode node)
{ {
Debug.Assert(node.left == null || Math.Abs(node.left.Balance) <= 1);
Debug.Assert(node.right == null || Math.Abs(node.right.Balance) <= 1);
// Keep looping until it's balanced. Not sure if this is stricly required; this is based on // Keep looping until it's balanced. Not sure if this is stricly required; this is based on
// the Rope code where node merging made this necessary. // the Rope code where node merging made this necessary.
while (Math.Abs(node.Balance) > 1) { while (Math.Abs(node.Balance) > 1) {
@ -100,7 +111,6 @@ namespace ICSharpCode.TreeView
if (node.Balance > 1) { if (node.Balance > 1) {
if (node.right.Balance < 0) { if (node.right.Balance < 0) {
node.right = node.right.RotateRight(); node.right = node.right.RotateRight();
node.right.RecalculateAugmentedData();
} }
node = node.RotateLeft(); node = node.RotateLeft();
// If 'node' was unbalanced by more than 2, we've shifted some of the inbalance to the left node; so rebalance that. // If 'node' was unbalanced by more than 2, we've shifted some of the inbalance to the left node; so rebalance that.
@ -108,24 +118,31 @@ namespace ICSharpCode.TreeView
} else if (node.Balance < -1) { } else if (node.Balance < -1) {
if (node.left.Balance > 0) { if (node.left.Balance > 0) {
node.left = node.left.RotateLeft(); node.left = node.left.RotateLeft();
node.left.RecalculateAugmentedData();
} }
node = node.RotateRight(); node = node.RotateRight();
// If 'node' was unbalanced by more than 2, we've shifted some of the inbalance to the right node; so rebalance that. // If 'node' was unbalanced by more than 2, we've shifted some of the inbalance to the right node; so rebalance that.
node.right = Rebalance(node.right); node.right = Rebalance(node.right);
} }
} }
node.RecalculateAugmentedData(); Debug.Assert(Math.Abs(node.Balance) <= 1);
node.height = (byte)(1 + Math.Max(Height(node.left), Height(node.right)));
node.totalListLength = -1; // mark for recalculation
// since balancing checks the whole tree up to the root, the whole path will get marked as invalid
return node; return node;
} }
void RecalculateAugmentedData() internal int GetTotalListLength()
{ {
Debug.Assert(Math.Abs(this.Balance) <= 1); if (totalListLength >= 0)
this.height = (byte)(1 + Math.Max(Height(this.left), Height(this.right))); return totalListLength;
this.totalListLength = (isVisible ? 1 : 0) int length = (isVisible ? 1 : 0);
+ (left != null ? left.totalListLength : 0) if (left != null) {
+ (right != null ? right.totalListLength : 0); length += left.GetTotalListLength();
}
if (right != null) {
length += right.GetTotalListLength();
}
return totalListLength = length;
} }
SharpTreeNode RotateLeft() SharpTreeNode RotateLeft()
@ -145,8 +162,10 @@ namespace ICSharpCode.TreeView
this.right = b; this.right = b;
newTop.left = this; newTop.left = this;
newTop.listParent = this.listParent; newTop.listParent = this.listParent;
RecalculateAugmentedData(); this.listParent = newTop;
return this.listParent = newTop; // rebalance the 'this' node - this is necessary in some bulk insertion cases:
newTop.left = Rebalance(this);
return newTop;
} }
SharpTreeNode RotateRight() SharpTreeNode RotateRight()
@ -166,15 +185,17 @@ namespace ICSharpCode.TreeView
this.left = b; this.left = b;
newTop.right = this; newTop.right = this;
newTop.listParent = this.listParent; newTop.listParent = this.listParent;
RecalculateAugmentedData(); this.listParent = newTop;
return this.listParent = newTop; newTop.right = Rebalance(this);
return newTop;
} }
#endregion #endregion
#region Insertion #region Insertion
static void InsertNodeAfter(SharpTreeNode pos, SharpTreeNode newNode) static void InsertNodeAfter(SharpTreeNode pos, SharpTreeNode newNode)
{ {
Debug.Assert(newNode.listParent == null); // newNode might be the model root of a whole subtree, so go to the list root of that subtree:
newNode = newNode.GetListRoot();
if (pos.right == null) { if (pos.right == null) {
pos.right = newNode; pos.right = newNode;
newNode.listParent = pos; newNode.listParent = pos;
@ -194,10 +215,10 @@ namespace ICSharpCode.TreeView
{ {
while (pos.listParent != null) { while (pos.listParent != null) {
if (pos == pos.listParent.left) { if (pos == pos.listParent.left) {
pos.listParent.left = Rebalance(pos); pos = pos.listParent.left = Rebalance(pos);
} else { } else {
Debug.Assert(pos == pos.listParent.right); Debug.Assert(pos == pos.listParent.right);
pos.listParent.right = Rebalance(pos); pos = pos.listParent.right = Rebalance(pos);
} }
pos = pos.listParent; pos = pos.listParent;
} }
@ -208,14 +229,14 @@ namespace ICSharpCode.TreeView
pos.treeFlattener = null; pos.treeFlattener = null;
newRoot.treeFlattener.root = newRoot; newRoot.treeFlattener.root = newRoot;
} }
Debug.Assert(newRoot.listParent == null);
newRoot.CheckInvariants();
} }
[Conditional("DEBUG")] [Conditional("DEBUG")]
static void DumpTree(SharpTreeNode node) static void DumpTree(SharpTreeNode node)
{ {
while (node.listParent != null) node.GetListRoot().DumpTree();
node = node.listParent;
node.DumpTree();
} }
[Conditional("DEBUG")] [Conditional("DEBUG")]
@ -225,7 +246,7 @@ namespace ICSharpCode.TreeView
if (left != null) if (left != null)
left.DumpTree(); left.DumpTree();
Debug.Unindent(); Debug.Unindent();
Debug.WriteLine("{0}, totalListLength={1}, height={2}", ToString(), totalListLength, height); Debug.WriteLine("{0}, totalListLength={1}, height={2}, Balance={3}, isVisible={4}", ToString(), totalListLength, height, Balance, isVisible);
Debug.Indent(); Debug.Indent();
if (right != null) if (right != null)
right.DumpTree(); right.DumpTree();

80
SharpTreeView/SharpTreeNode.cs

@ -21,6 +21,56 @@ namespace ICSharpCode.TreeView
internal SharpTreeNode modelParent; internal SharpTreeNode modelParent;
bool isVisible = true; bool isVisible = true;
void UpdateIsVisible(bool parentIsVisible, bool updateFlattener)
{
bool newIsVisible = parentIsVisible && !isHidden;
if (isVisible != newIsVisible) {
isVisible = newIsVisible;
// invalidate the augmented data
SharpTreeNode node = this;
while (node != null && node.totalListLength >= 0) {
node.totalListLength = -1;
node = node.listParent;
}
// Remember the removed nodes:
List<SharpTreeNode> removedNodes = null;
if (updateFlattener && !newIsVisible) {
removedNodes = VisibleDescendantsAndSelf().ToList();
}
// also update the model children:
UpdateChildIsVisible(false);
// Validate our invariants:
if (updateFlattener)
GetListRoot().CheckInvariants();
// Tell the flattener about the removed nodes:
if (removedNodes != null) {
var flattener = GetListRoot().treeFlattener;
if (flattener != null) {
flattener.NodesRemoved(GetVisibleIndexForNode(this), removedNodes);
}
}
// Tell the flattener about the new nodes:
if (updateFlattener && newIsVisible) {
var flattener = GetListRoot().treeFlattener;
if (flattener != null) {
flattener.NodesInserted(GetVisibleIndexForNode(this), VisibleDescendantsAndSelf());
}
}
}
}
void UpdateChildIsVisible(bool updateFlattener)
{
if (modelChildren != null && modelChildren.Count > 0) {
bool showChildren = isVisible && isExpanded;
foreach (SharpTreeNode child in modelChildren) {
child.UpdateIsVisible(showChildren, updateFlattener);
}
}
}
#region Main #region Main
public SharpTreeNode() public SharpTreeNode()
@ -72,6 +122,8 @@ namespace ICSharpCode.TreeView
set { set {
if (isHidden != value) { if (isHidden != value) {
isHidden = value; isHidden = value;
if (modelParent != null)
UpdateIsVisible(modelParent.isVisible && modelParent.isExpanded, true);
RaisePropertyChanged("IsHidden"); RaisePropertyChanged("IsHidden");
} }
} }
@ -110,17 +162,28 @@ namespace ICSharpCode.TreeView
if (e.NewItems != null) { if (e.NewItems != null) {
SharpTreeNode insertionPos; SharpTreeNode insertionPos;
if (e.NewStartingIndex == 0) if (e.NewStartingIndex == 0)
insertionPos = this; insertionPos = null;
else else
insertionPos = modelChildren[e.NewStartingIndex - 1]; insertionPos = modelChildren[e.NewStartingIndex - 1];
foreach (SharpTreeNode node in e.NewItems) { foreach (SharpTreeNode node in e.NewItems) {
Debug.Assert(node.modelParent == null); Debug.Assert(node.modelParent == null);
node.modelParent = this; node.modelParent = this;
Debug.WriteLine("Inserting {0} after {1}", node.ToString(), insertionPos.ToString()); node.UpdateIsVisible(isVisible && isExpanded, false);
InsertNodeAfter(insertionPos, node); Debug.WriteLine("Inserting {0} after {1}", node, insertionPos);
while (insertionPos != null && insertionPos.modelChildren != null && insertionPos.modelChildren.Count > 0) {
insertionPos = insertionPos.modelChildren.Last();
}
InsertNodeAfter(insertionPos ?? this, node);
insertionPos = node; insertionPos = node;
if (node.isVisible) {
var flattener = GetListRoot().treeFlattener;
if (flattener != null) {
flattener.NodesInserted(GetVisibleIndexForNode(node), node.VisibleDescendantsAndSelf());
}
}
} }
DumpTree(this);
} }
RaisePropertyChanged("ShowExpander"); RaisePropertyChanged("ShowExpander");
@ -149,6 +212,7 @@ namespace ICSharpCode.TreeView
{ {
if (isExpanded != value) { if (isExpanded != value) {
isExpanded = value; isExpanded = value;
UpdateChildIsVisible(true);
if (isExpanded) { if (isExpanded) {
EnsureLazyChildren(); EnsureLazyChildren();
} }
@ -208,14 +272,14 @@ namespace ICSharpCode.TreeView
return TreeTraversal.PreOrder(this, n => n.Children); return TreeTraversal.PreOrder(this, n => n.Children);
} }
public IEnumerable<SharpTreeNode> ExpandedDescendants() internal IEnumerable<SharpTreeNode> VisibleDescendants()
{ {
return TreeTraversal.PreOrder(this.Children, n => n.IsExpanded ? n.Children : null); return TreeTraversal.PreOrder(this.Children.Where(c => c.isVisible), n => n.Children.Where(c => c.isVisible));
} }
public IEnumerable<SharpTreeNode> ExpandedDescendantsAndSelf() internal IEnumerable<SharpTreeNode> VisibleDescendantsAndSelf()
{ {
return TreeTraversal.PreOrder(this, n => n.IsExpanded ? n.Children : null); return TreeTraversal.PreOrder(this, n => n.Children.Where(c => c.isVisible));
} }
public IEnumerable<SharpTreeNode> Ancestors() public IEnumerable<SharpTreeNode> Ancestors()

2
SharpTreeView/SharpTreeNodeView.cs

@ -87,7 +87,7 @@ namespace ICSharpCode.TreeView
OnIsEditingChanged(); OnIsEditingChanged();
} else if (e.PropertyName == "IsLast") { } else if (e.PropertyName == "IsLast") {
if (ParentTreeView.ShowLines) { if (ParentTreeView.ShowLines) {
foreach (var child in Node.ExpandedDescendantsAndSelf()) { foreach (var child in Node.VisibleDescendantsAndSelf()) {
var container = ParentTreeView.ItemContainerGenerator.ContainerFromItem(child) as SharpTreeViewItem; var container = ParentTreeView.ItemContainerGenerator.ContainerFromItem(child) as SharpTreeViewItem;
if (container != null) { if (container != null) {
container.NodeView.LinesRenderer.InvalidateVisual(); container.NodeView.LinesRenderer.InvalidateVisual();

2
SharpTreeView/SharpTreeView.cs

@ -153,7 +153,7 @@ namespace ICSharpCode.TreeView
internal void HandleCollapsing(SharpTreeNode Node) internal void HandleCollapsing(SharpTreeNode Node)
{ {
var selectedChilds = Node.ExpandedDescendants().Where(n => n.IsSelected); var selectedChilds = Node.VisibleDescendants().Where(n => n.IsSelected);
if (selectedChilds.Any()) { if (selectedChilds.Any()) {
var list = SelectedItems.Cast<SharpTreeNode>().Except(selectedChilds).ToList(); var list = SelectedItems.Cast<SharpTreeNode>().Except(selectedChilds).ToList();
list.AddOnce(Node); list.AddOnce(Node);

25
SharpTreeView/TreeFlattener.cs

@ -3,6 +3,7 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
@ -32,6 +33,28 @@ namespace ICSharpCode.TreeView
public event NotifyCollectionChangedEventHandler CollectionChanged; public event NotifyCollectionChangedEventHandler CollectionChanged;
public void RaiseCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null)
CollectionChanged(this, e);
}
public void NodesInserted(int index, IEnumerable<SharpTreeNode> nodes)
{
if (!includeRoot) index--;
foreach (SharpTreeNode node in nodes) {
RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, node, index++));
}
}
public void NodesRemoved(int index, IEnumerable<SharpTreeNode> nodes)
{
if (!includeRoot) index--;
foreach (SharpTreeNode node in nodes) {
RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, node, index));
}
}
public void Stop() public void Stop()
{ {
Debug.Assert(root.treeFlattener == this); Debug.Assert(root.treeFlattener == this);
@ -51,7 +74,7 @@ namespace ICSharpCode.TreeView
public int Count { public int Count {
get { get {
return includeRoot ? root.totalListLength : root.totalListLength - 1; return includeRoot ? root.GetTotalListLength() : root.GetTotalListLength() - 1;
} }
} }

Loading…
Cancel
Save