Browse Source

Object graph - simplified layout code.

pull/15/head
mkonicek 15 years ago
parent
commit
550e862c4d
  1. 8
      src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj
  2. 74
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Drawing/GraphDrawer.cs
  3. 6
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/ContentNode.cs
  4. 2
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/ContentPropertyNode.cs
  5. 22
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/GraphDiff.cs
  6. 26
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/GraphMatcher.cs
  7. 2
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/PositionedEdge.cs
  8. 10
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/PositionedGraph.cs
  9. 53
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/PositionedNode.cs
  10. 6
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/PositionedNodeProperty.cs
  11. 21
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeEdge.cs
  12. 75
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeGraphNode.cs
  13. 27
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeGraphNodeLR.cs
  14. 27
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeGraphNodeTB.cs
  15. 187
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeLayout.cs
  16. 137
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeLayouter.cs
  17. 27
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/ObjectGraph/ObjectGraph.cs
  18. 126
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/ObjectGraphControl.xaml.cs

8
src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj

@ -315,15 +315,11 @@ @@ -315,15 +315,11 @@
<Compile Include="Visualizers\Graph\Layout\GraphMatcher.cs" />
<Compile Include="Visualizers\Graph\Layout\PositionedEdge.cs" />
<Compile Include="Visualizers\Graph\Layout\PositionedGraph.cs" />
<Compile Include="Visualizers\Graph\Layout\PositionedGraphNode.cs" />
<Compile Include="Visualizers\Graph\Layout\PositionedNode.cs" />
<Compile Include="Visualizers\Graph\Layout\PositionedNodeProperty.cs" />
<Compile Include="Visualizers\Graph\Layout\PositionedPropertyEventArgs.cs" />
<Compile Include="Visualizers\Graph\Layout\Tree\LayoutDirection.cs" />
<Compile Include="Visualizers\Graph\Layout\Tree\TreeEdge.cs" />
<Compile Include="Visualizers\Graph\Layout\Tree\TreeLayouter.cs" />
<Compile Include="Visualizers\Graph\Layout\Tree\TreeGraphNode.cs" />
<Compile Include="Visualizers\Graph\Layout\Tree\TreeGraphNodeLR.cs" />
<Compile Include="Visualizers\Graph\Layout\Tree\TreeGraphNodeTB.cs" />
<Compile Include="Visualizers\Graph\Layout\Tree\TreeLayout.cs" />
<Compile Include="Visualizers\Graph\ObjectGraph\ObjectGraph.cs" />
<Compile Include="Visualizers\Graph\ObjectGraph\ObjectGraphBuilder.cs" />
<Compile Include="Visualizers\Graph\ObjectGraph\ObjectGraphNode.cs" />

74
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Drawing/GraphDrawer.cs

@ -19,7 +19,8 @@ using Debugger.AddIn.Visualizers.Graph.Layout; @@ -19,7 +19,8 @@ using Debugger.AddIn.Visualizers.Graph.Layout;
namespace Debugger.AddIn.Visualizers.Graph
{
/// <summary>
/// Draws <see cref="PositionedGraph"></see> on Canvas.
/// Draws <see cref="PositionedGraph" /> on Canvas.
/// Keeps the last displayed graph and does a smooth transition into the new graph.
/// </summary>
public class GraphDrawer
{
@ -41,11 +42,10 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -41,11 +42,10 @@ namespace Debugger.AddIn.Visualizers.Graph
{
// account for that the visual controls could have been reused (we are not reusing controls now - NodeControlCache does nothing)
// this.canvas.Width = newGraph.BoundingRect.Width;
// this.canvas.Height = newGraph.BoundingRect.Height;
this.canvas.Width = newGraph.BoundingRect.Width;
this.canvas.Height = newGraph.BoundingRect.Height;
if (oldGraph == null)
{
if (oldGraph == null) {
Draw(newGraph);
return;
}
@ -57,38 +57,31 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -57,38 +57,31 @@ namespace Debugger.AddIn.Visualizers.Graph
DoubleAnimation fadeOutAnim = new DoubleAnimation(1.0, 0.0, durationFade);
DoubleAnimation fadeInAnim = new DoubleAnimation(0.0, 1.0, durationFade);
foreach (UIElement drawing in canvas.Children)
{
foreach (UIElement drawing in canvas.Children) {
var arrow = drawing as Path;
if (arrow != null)
{
if (arrow != null) {
arrow.BeginAnimation(UIElement.OpacityProperty, fadeOutAnim);
}
}
foreach (PositionedEdge edge in newGraph.Edges)
{
addEdgeToCanvas(edge).BeginAnimation(UIElement.OpacityProperty, fadeInAnim);
foreach (PositionedEdge edge in newGraph.Edges) {
AddEdgeToCanvas(edge).BeginAnimation(UIElement.OpacityProperty, fadeInAnim);
}
foreach (PositionedGraphNode removedNode in diff.RemovedNodes)
{
foreach (PositionedNode removedNode in diff.RemovedNodes) {
removedNode.NodeVisualControl.BeginAnimation(UIElement.OpacityProperty, fadeOutAnim);
}
foreach (PositionedGraphNode addedNode in diff.AddedNodes)
{
addNodeToCanvas(addedNode).BeginAnimation(UIElement.OpacityProperty, fadeInAnim);
foreach (PositionedNode addedNode in diff.AddedNodes) {
AddNodeToCanvas(addedNode).BeginAnimation(UIElement.OpacityProperty, fadeInAnim);
}
bool first = true;
foreach (PositionedGraphNode node in diff.ChangedNodes)
{
foreach (PositionedNode node in diff.ChangedNodes) {
var newNode = diff.GetMatchingNewNode(node);
PointAnimation anim = new PointAnimation();
if (first)
{
if (first) {
anim.Completed += new EventHandler((o, e) => { Draw(newGraph); });
first = false;
}
@ -111,29 +104,14 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -111,29 +104,14 @@ namespace Debugger.AddIn.Visualizers.Graph
{
canvas.Children.Clear();
/*try
{
// why do the controls disappear?
var n1 = posGraph.Nodes.First().NodeVisualControl;
var n2 = posGraph.Nodes.Skip(1).First().NodeVisualControl;
var n3 = posGraph.Nodes.Skip(2).First().NodeVisualControl;
if (n1 == n2 || n1 == n3 || n2 == n3)
{
ClearCanvas();
}
}
catch{}*/
// draw nodes
foreach (PositionedGraphNode node in posGraph.Nodes)
{
addNodeToCanvas(node);
foreach (PositionedNode node in posGraph.Nodes) {
AddNodeToCanvas(node);
}
// draw edges
foreach (PositionedEdge edge in posGraph.Edges)
{
addEdgeToCanvas(edge);
foreach (PositionedEdge edge in posGraph.Edges) {
AddEdgeToCanvas(edge);
}
edgeTooltip.Visibility = Visibility.Hidden;
@ -149,7 +127,7 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -149,7 +127,7 @@ namespace Debugger.AddIn.Visualizers.Graph
canvas.Children.Clear();
}
private PositionedGraphNodeControl addNodeToCanvas(PositionedGraphNode node)
PositionedGraphNodeControl AddNodeToCanvas(PositionedNode node)
{
canvas.Children.Add(node.NodeVisualControl);
Canvas.SetLeft(node.NodeVisualControl, node.Left);
@ -157,13 +135,13 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -157,13 +135,13 @@ namespace Debugger.AddIn.Visualizers.Graph
return node.NodeVisualControl;
}
private Path addEdgeToCanvas(PositionedEdge edge)
Path AddEdgeToCanvas(PositionedEdge edge)
{
PathFigure edgeSplineFigure = createEdgeSpline(edge);
PathFigure edgeSplineFigure = CreateEdgeSpline(edge);
PathGeometry geometryVisible = new PathGeometry();
geometryVisible.Figures.Add(edgeSplineFigure);
geometryVisible.Figures.Add(createEdgeArrow(edge));
geometryVisible.Figures.Add(CreateEdgeArrow(edge));
Path pathVisible = new Path();
pathVisible.Stroke = Brushes.Black;
@ -210,7 +188,7 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -210,7 +188,7 @@ namespace Debugger.AddIn.Visualizers.Graph
return pathVisible;
}
private PathFigure createEdgeSpline(PositionedEdge edge)
PathFigure CreateEdgeSpline(PositionedEdge edge)
{
PathFigure figure = new PathFigure();
figure.IsClosed = false;
@ -225,7 +203,7 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -225,7 +203,7 @@ namespace Debugger.AddIn.Visualizers.Graph
return figure;
}
private PathFigure createEdgeArrow(PositionedEdge edge)
PathFigure CreateEdgeArrow(PositionedEdge edge)
{
Point splineEndPoint = edge.SplinePoints[edge.SplinePoints.Count - 1];
Point splineEndHandlePoint = edge.SplinePoints[edge.SplinePoints.Count - 2];
@ -240,14 +218,14 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -240,14 +218,14 @@ namespace Debugger.AddIn.Visualizers.Graph
arrowFigure.IsFilled = true;
arrowFigure.StartPoint = splineEndPoint; // arrow tip
Vector tangent2 = rotate90(tangent);
Vector tangent2 = Rotate90(tangent);
arrowFigure.Segments.Add(new LineSegment(basePoint + tangent2 * 0.15, true));
arrowFigure.Segments.Add(new LineSegment(basePoint - tangent2 * 0.15, true));
return arrowFigure;
}
private static Vector rotate90(Vector v)
static Vector Rotate90(Vector v)
{
// (x, y) -> (y, -x)
double t = v.X;

6
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/ContentNode.cs

@ -15,7 +15,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -15,7 +15,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// </summary>
public class ContentNode : Utils.ITreeNode<ContentNode>
{
public ContentNode(PositionedGraphNode containingNode, ContentNode parent)
public ContentNode(PositionedNode containingNode, ContentNode parent)
{
if (containingNode == null)
throw new ArgumentNullException("containingNode");
@ -74,11 +74,11 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -74,11 +74,11 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
private List<ContentNode> children = new List<ContentNode>();
public List<ContentNode> Children { get { return this.children; } }
PositionedGraphNode containingNode;
PositionedNode containingNode;
/// <summary>
/// PositionedGraphNode that contains this PropertyNodeViewModel.
/// </summary>
public PositionedGraphNode ContainingNode
public PositionedNode ContainingNode
{
get { return this.containingNode; }
}

2
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/ContentPropertyNode.cs

@ -13,7 +13,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -13,7 +13,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
{
PositionedNodeProperty positionedProperty;
public ContentPropertyNode(PositionedGraphNode containingNode, ContentNode parent)
public ContentPropertyNode(PositionedNode containingNode, ContentNode parent)
: base(containingNode, parent)
{
}

22
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/GraphDiff.cs

@ -13,15 +13,15 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -13,15 +13,15 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// </summary>
public class GraphDiff
{
private List<PositionedGraphNode> addedNodes = new List<PositionedGraphNode>();
private List<PositionedGraphNode> deletedNodes = new List<PositionedGraphNode>();
private List<PositionedGraphNode> changedNodes = new List<PositionedGraphNode>();
private Dictionary<PositionedGraphNode, PositionedGraphNode> matching = new Dictionary<PositionedGraphNode, PositionedGraphNode>();
private List<PositionedNode> addedNodes = new List<PositionedNode>();
private List<PositionedNode> deletedNodes = new List<PositionedNode>();
private List<PositionedNode> changedNodes = new List<PositionedNode>();
private Dictionary<PositionedNode, PositionedNode> matching = new Dictionary<PositionedNode, PositionedNode>();
/// <summary>
/// Nodes in the new graph that were added.
/// </summary>
public IList<PositionedGraphNode> AddedNodes
public IList<PositionedNode> AddedNodes
{
get { return addedNodes.AsReadOnly(); }
}
@ -29,7 +29,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -29,7 +29,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// <summary>
/// Nodes in the old graph that were removed.
/// </summary>
public IList<PositionedGraphNode> RemovedNodes
public IList<PositionedNode> RemovedNodes
{
get { return deletedNodes.AsReadOnly(); }
}
@ -38,27 +38,27 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -38,27 +38,27 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// Nodes in the old graph that were chaged.
/// These have matching new nodes, which can be obtained by <see cref="GetMatchingNewNode"/>.
/// </summary>
public IList<PositionedGraphNode> ChangedNodes
public IList<PositionedNode> ChangedNodes
{
get { return changedNodes.AsReadOnly(); }
}
public PositionedGraphNode GetMatchingNewNode(PositionedGraphNode oldNode)
public PositionedNode GetMatchingNewNode(PositionedNode oldNode)
{
return matching.GetValue(oldNode);
}
internal void SetAdded(PositionedGraphNode addedNode)
internal void SetAdded(PositionedNode addedNode)
{
addedNodes.Add(addedNode);
}
internal void SetRemoved(PositionedGraphNode removeddNode)
internal void SetRemoved(PositionedNode removeddNode)
{
deletedNodes.Add(removeddNode);
}
internal void SetMatching(PositionedGraphNode matchFrom, PositionedGraphNode matchTo)
internal void SetMatching(PositionedNode matchFrom, PositionedNode matchTo)
{
matching[matchFrom] = matchTo;
changedNodes.Add(matchFrom);

26
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/GraphMatcher.cs

@ -27,7 +27,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -27,7 +27,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
else
{
GraphDiff addAllDiff = new GraphDiff();
foreach (PositionedGraphNode newNode in newGraph.Nodes)
foreach (PositionedNode newNode in newGraph.Nodes)
addAllDiff.SetAdded(newNode);
return addAllDiff;
}
@ -35,7 +35,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -35,7 +35,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
else if (newGraph == null)
{
GraphDiff removeAllDiff = new GraphDiff();
foreach (PositionedGraphNode oldNode in oldGraph.Nodes)
foreach (PositionedNode oldNode in oldGraph.Nodes)
removeAllDiff.SetRemoved(oldNode);
return removeAllDiff;
}
@ -43,12 +43,12 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -43,12 +43,12 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
// none of the graphs is null
GraphDiff diff = new GraphDiff();
Dictionary<int, PositionedGraphNode> newNodeForHashCode = buildHashToNodeMap(newGraph);
Dictionary<PositionedGraphNode, bool> newNodeMatched = new Dictionary<PositionedGraphNode, bool>();
Dictionary<int, PositionedNode> newNodeForHashCode = buildHashToNodeMap(newGraph);
Dictionary<PositionedNode, bool> newNodeMatched = new Dictionary<PositionedNode, bool>();
foreach (PositionedGraphNode oldNode in oldGraph.Nodes)
foreach (PositionedNode oldNode in oldGraph.Nodes)
{
PositionedGraphNode matchingNode = matchNode(oldNode, newNodeForHashCode);
PositionedNode matchingNode = matchNode(oldNode, newNodeForHashCode);
if (matchingNode != null)
{
diff.SetMatching(oldNode, matchingNode);
@ -59,7 +59,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -59,7 +59,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
diff.SetRemoved(oldNode);
}
}
foreach (PositionedGraphNode newNode in newGraph.Nodes)
foreach (PositionedNode newNode in newGraph.Nodes)
{
if (!newNodeMatched.ContainsKey(newNode))
{
@ -70,19 +70,19 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -70,19 +70,19 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
return diff;
}
private Dictionary<int, PositionedGraphNode> buildHashToNodeMap(PositionedGraph graph)
private Dictionary<int, PositionedNode> buildHashToNodeMap(PositionedGraph graph)
{
var hashToNodeMap = new Dictionary<int, PositionedGraphNode>();
foreach (PositionedGraphNode node in graph.Nodes)
var hashToNodeMap = new Dictionary<int, PositionedNode>();
foreach (PositionedNode node in graph.Nodes)
{
hashToNodeMap[node.ObjectNode.HashCode] = node;
}
return hashToNodeMap;
}
private PositionedGraphNode matchNode(PositionedGraphNode oldNode, Dictionary<int, PositionedGraphNode> newNodeMap)
private PositionedNode matchNode(PositionedNode oldNode, Dictionary<int, PositionedNode> newNodeMap)
{
PositionedGraphNode newNodeFound = newNodeMap.GetValue(oldNode.ObjectNode.HashCode);
PositionedNode newNodeFound = newNodeMap.GetValue(oldNode.ObjectNode.HashCode);
if ((newNodeFound != null) && isSameAddress(oldNode, newNodeFound))
{
return newNodeFound;
@ -93,7 +93,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -93,7 +93,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
}
}
private bool isSameAddress(PositionedGraphNode node1, PositionedGraphNode node2)
private bool isSameAddress(PositionedNode node1, PositionedNode node2)
{
return node1.ObjectNode.PermanentReference.GetObjectAddress() == node2.ObjectNode.PermanentReference.GetObjectAddress();
}

2
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/PositionedEdge.cs

@ -11,7 +11,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -11,7 +11,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// <summary>
/// Edge with position information.
/// </summary>
public class PositionedEdge : NamedEdge<PositionedNodeProperty, PositionedGraphNode>, SplineRouting.IEdge
public class PositionedEdge : NamedEdge<PositionedNodeProperty, PositionedNode>, SplineRouting.IEdge
{
private IList<Point> splinePoints = new List<Point>();
/// <summary>

10
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/PositionedGraph.cs

@ -13,9 +13,9 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -13,9 +13,9 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// </summary>
public class PositionedGraph
{
private List<PositionedGraphNode> nodes = new List<PositionedGraphNode>();
private List<PositionedNode> nodes = new List<PositionedNode>();
public PositionedGraphNode Root { get; set; }
public PositionedNode Root { get; set; }
public System.Windows.Rect BoundingRect
{
@ -33,12 +33,12 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -33,12 +33,12 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// <summary>
/// All nodes in the graph.
/// </summary>
public IEnumerable<PositionedGraphNode> Nodes
public IEnumerable<PositionedNode> Nodes
{
get { return nodes; }
}
internal void AddNode(PositionedGraphNode node)
internal void AddNode(PositionedNode node)
{
this.nodes.Add(node);
}
@ -50,7 +50,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -50,7 +50,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
{
get
{
foreach (PositionedGraphNode node in this.Nodes)
foreach (PositionedNode node in this.Nodes)
{
foreach (PositionedEdge edge in node.Edges)
{

53
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/PositionedGraphNode.cs → src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/PositionedNode.cs

@ -13,18 +13,16 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -13,18 +13,16 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// <summary>
/// ObjectNode with added position information.
/// </summary>
public class PositionedGraphNode : SplineRouting.IRect
public class PositionedNode : SplineRouting.IRect
{
public static readonly double MaxHeight = 300;
/// <summary>
/// Creates new PositionedNode.
/// </summary>
/// <param name="objectNode">Underlying ObjectNode.</param>
public PositionedGraphNode(ObjectGraphNode objectNode)
public PositionedNode(ObjectGraphNode objectNode)
{
this.objectNode = objectNode;
initVisualControl();
InitVisualControl();
}
public event EventHandler<PositionedPropertyEventArgs> PropertyExpanded;
@ -44,56 +42,45 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -44,56 +42,45 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// <summary>
/// Tree-of-properties content of this node.
/// </summary>
public ContentNode Content
{
get; set;
}
public ContentNode Content { get; set; }
/// <summary>
/// The size of the subtree of this node in the layout.
/// </summary>
public double SubtreeSize { get; set; }
private PositionedGraphNodeControl nodeVisualControl;
/// <summary>
/// Visual control to be shown for this node.
/// </summary>
public PositionedGraphNodeControl NodeVisualControl
{
get
{
return this.nodeVisualControl;
}
}
public PositionedGraphNodeControl NodeVisualControl { get; private set; }
public void InitContentFromObjectNode(Expanded expanded)
{
this.Content = new ContentNode(this, null);
this.Content.InitOverride(this.ObjectNode.Content, expanded);
this.nodeVisualControl.Root = this.Content;
this.NodeVisualControl.Root = this.Content;
}
private void initVisualControl()
private void InitVisualControl()
{
this.nodeVisualControl = NodeControlCache.Instance.GetNodeControl();
this.nodeVisualControl.MaxHeight = MaxHeight;
this.NodeVisualControl = NodeControlCache.Instance.GetNodeControl();
// propagate events from nodeVisualControl
this.nodeVisualControl.PropertyExpanded += new EventHandler<PositionedPropertyEventArgs>(NodeVisualControl_PropertyExpanded);
this.nodeVisualControl.PropertyCollapsed += new EventHandler<PositionedPropertyEventArgs>(NodeVisualControl_PropertyCollapsed);
this.nodeVisualControl.ContentNodeExpanded += new EventHandler<ContentNodeEventArgs>(NodeVisualControl_ContentNodeExpanded);
this.nodeVisualControl.ContentNodeCollapsed += new EventHandler<ContentNodeEventArgs>(NodeVisualControl_ContentNodeCollapsed);
this.NodeVisualControl.PropertyExpanded += new EventHandler<PositionedPropertyEventArgs>(NodeVisualControl_PropertyExpanded);
this.NodeVisualControl.PropertyCollapsed += new EventHandler<PositionedPropertyEventArgs>(NodeVisualControl_PropertyCollapsed);
this.NodeVisualControl.ContentNodeExpanded += new EventHandler<ContentNodeEventArgs>(NodeVisualControl_ContentNodeExpanded);
this.NodeVisualControl.ContentNodeCollapsed += new EventHandler<ContentNodeEventArgs>(NodeVisualControl_ContentNodeCollapsed);
}
public IEnumerable<PositionedNodeProperty> Properties
{
get
{
return this.Content.FlattenProperties();
}
get { return this.Content.FlattenProperties(); }
}
public virtual IEnumerable<PositionedEdge> Edges
{
get
{
foreach (PositionedNodeProperty property in this.Properties)
{
foreach (PositionedNodeProperty property in this.Properties) {
if (property.Edge != null)
yield return property.Edge;
}
@ -102,7 +89,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -102,7 +89,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
public void Measure()
{
this.nodeVisualControl.Measure(new Size(800, 800));
this.NodeVisualControl.Measure(new Size(800, 800));
}
public double Left { get; set; }

6
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/PositionedNodeProperty.cs

@ -16,7 +16,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -16,7 +16,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// Creates new PositionedNodeProperty.
/// </summary>
/// <param name="objectProperty">Underlying <see cref="ObjectProperty"/></param>
public PositionedNodeProperty(ObjectGraphProperty objectProperty, PositionedGraphNode containingNode, bool isPropertyExpanded)
public PositionedNodeProperty(ObjectGraphProperty objectProperty, PositionedNode containingNode, bool isPropertyExpanded)
{
if (containingNode == null)
throw new ArgumentNullException("containingNode");
@ -48,11 +48,11 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -48,11 +48,11 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
get { return this.objectGraphProperty; }
}
private PositionedGraphNode containingNode;
private PositionedNode containingNode;
/// <summary>
/// <see cref="PositionedNode"/> which contains this Property.
/// </summary>
public PositionedGraphNode ContainingNode
public PositionedNode ContainingNode
{
get { return this.containingNode; }
}

21
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeEdge.cs

@ -1,21 +0,0 @@ @@ -1,21 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
// TODO this class is almost not necessary, is used only for TreeLayouter purposes.
// TreeLayouter could remember the additional information in a Dictionary PositionedEdge -> bool
/// <summary>
/// Edge in the tree-layouted <see cref="PositionedGraph"/>.
/// </summary>
public class TreeGraphEdge : PositionedEdge
{
/// <summary>
/// Is this an edges making up the main tree?
/// </summary>
public bool IsTreeEdge { get; set; }
}
}

75
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeGraphNode.cs

@ -1,75 +0,0 @@ @@ -1,75 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System;
using System.Collections.Generic;
using Debugger.AddIn.Visualizers.Graph.Drawing;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// Node in tree-layouted <see cref="PositionedGraph"/>.
/// This is the abstract ancestor of TreeNodeLR and TreeNodeTB.
/// There are 2 dimensions - "main" and "lateral".
/// Main dimension is the dimension in which the graph depth grows (vertical when TB, horizontal when LR).
/// Lateral dimension is the other dimension. Siblings are placed next to each other in Lateral dimension.
/// </summary>
public abstract class TreeGraphNode : PositionedGraphNode
{
public static TreeGraphNode Create(LayoutDirection direction, ObjectGraphNode objectNode)
{
switch (direction) {
case LayoutDirection.TopBottom: return new TreeNodeTB(objectNode);
case LayoutDirection.LeftRight: return new TreeNodeLR(objectNode);
default: throw new DebuggerVisualizerException("Unsupported layout direction: " + direction.ToString());
}
}
public double HorizontalMargin { get; set; }
public double VerticalMargin { get; set; }
protected TreeGraphNode(ObjectGraphNode objectNode) : base(objectNode)
{
}
/// <summary>
/// Width or height of the subtree.
/// </summary>
public double SubtreeSize { get; set; }
public abstract double MainCoord { get; set; }
public abstract double LateralCoord { get; set; }
public abstract double MainSize { get; }
public abstract double LateralSize { get; }
public double MainSizeWithMargin { get { return MainSize + MainMargin; } }
public double LateralSizeWithMargin { get { return LateralSize + LateralMargin; } }
public abstract double MainMargin { get; }
public abstract double LateralMargin { get; }
public IEnumerable<PositionedEdge> ChildEdges
{
get
{
foreach (TreeGraphEdge childEdge in Edges)
{
if (childEdge.IsTreeEdge)
{
yield return childEdge;
}
}
}
}
public IEnumerable<TreeGraphNode> Childs
{
get
{
foreach (PositionedEdge outEdge in this.ChildEdges)
yield return (TreeGraphNode)outEdge.Target;
}
}
}
}

27
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeGraphNodeLR.cs

@ -1,27 +0,0 @@ @@ -1,27 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System;
using Debugger.AddIn.Visualizers.Graph.Drawing;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// TreeNode used in LR layout mode.
/// </summary>
public class TreeNodeLR: TreeGraphNode
{
public TreeNodeLR(ObjectGraphNode objectNode) : base(objectNode)
{
}
public override double MainSize { get { return this.Width; } }
public override double LateralSize { get { return this.Height ; } }
public override double MainCoord { get { return this.Left; } set { this.Left = value; } }
public override double LateralCoord { get { return this.Top; } set { this.Top = value; } }
public override double MainMargin { get { return this.HorizontalMargin; } }
public override double LateralMargin { get { return this.VerticalMargin; } }
}
}

27
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeGraphNodeTB.cs

@ -1,27 +0,0 @@ @@ -1,27 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System;
using Debugger.AddIn.Visualizers.Graph.Drawing;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// TreeNode used in TB layout mode.
/// </summary>
public class TreeNodeTB : TreeGraphNode
{
public TreeNodeTB(ObjectGraphNode objectNode) : base(objectNode)
{
}
public override double MainSize { get { return this.Height; } }
public override double LateralSize { get { return this.Width; } }
public override double MainCoord { get { return this.Top; } set { this.Top = value; } }
public override double LateralCoord { get { return this.Left; } set { this.Left = value; } }
public override double MainMargin { get { return this.VerticalMargin; } }
public override double LateralMargin { get { return this.HorizontalMargin; } }
}
}

187
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeLayout.cs

@ -0,0 +1,187 @@ @@ -0,0 +1,187 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System;
using System.Collections.Generic;
using System.Windows;
using Debugger.AddIn.Visualizers.Graph.Drawing;
using System.Linq;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// Calculates layout of <see cref="ObjectGraph" />, producing <see cref="PositionedGraph" />.
/// </summary>
public class TreeLayout
{
private static readonly double NodeMarginH = 30;
private static readonly double NodeMarginV = 30;
GraphEdgeRouter edgeRouter = new GraphEdgeRouter();
/// <summary>
/// The produced layout is either a horizontal or vertical tree.
/// </summary>
public LayoutDirection LayoutDirection { get; private set; }
public TreeLayout(LayoutDirection layoutDirection)
{
this.LayoutDirection = layoutDirection;
}
/// <summary>
/// Calculates layout for given <see cref="ObjectGraph" />.
/// </summary>
/// <param name="objectGraph"></param>
/// <returns></returns>
public PositionedGraph CalculateLayout(ObjectGraph objectGraph, Expanded expanded)
{
var positionedGraph = BuildPositionedGraph(objectGraph, expanded);
CalculateLayout(positionedGraph);
this.edgeRouter.RouteEdges(positionedGraph);
return positionedGraph;
}
// Expanded is passed so that the correct ContentNodes are expanded in the PositionedNode
PositionedGraph BuildPositionedGraph(ObjectGraph objectGraph, Expanded expanded)
{
var positionedNodeFor = new Dictionary<ObjectGraphNode, PositionedNode>();
var positionedGraph = new PositionedGraph();
// create empty PositionedNodes
foreach (ObjectGraphNode objectNode in objectGraph.ReachableNodes) {
var posNode = new PositionedNode(objectNode);
posNode.InitContentFromObjectNode(expanded);
positionedGraph.AddNode(posNode);
positionedNodeFor[objectNode] = posNode;
}
// create edges
foreach (PositionedNode posNode in positionedGraph.Nodes)
{
foreach (PositionedNodeProperty property in posNode.Properties) {
if (property.ObjectGraphProperty.TargetNode != null) {
ObjectGraphNode targetObjectNode = property.ObjectGraphProperty.TargetNode;
PositionedNode edgeTarget = positionedNodeFor[targetObjectNode];
property.Edge = new PositionedEdge {
Name = property.Name, Source = property, Target = edgeTarget
};
}
}
}
positionedGraph.Root = positionedNodeFor[objectGraph.Root];
return positionedGraph;
}
void CalculateLayout(PositionedGraph positionedGraph)
{
HashSet<PositionedNode> seenNodes = new HashSet<PositionedNode>();
HashSet<PositionedEdge> treeEdges = new HashSet<PositionedEdge>();
// first layout pass
CalculateSubtreeSizes(positionedGraph.Root, seenNodes, treeEdges);
// second layout pass
CalculateNodePosRecursive(positionedGraph.Root, treeEdges, 0, 0);
}
// determines which edges are tree edges, and calculates subtree size for each node
private void CalculateSubtreeSizes(PositionedNode root, HashSet<PositionedNode> seenNodes, HashSet<PositionedEdge> treeEdges)
{
seenNodes.Add(root);
double subtreeSize = 0;
foreach (var property in root.Properties) {
var edge = property.Edge;
if (edge != null) {
var targetNode = edge.Target;
if (!seenNodes.Contains(targetNode)) {
// when we come to a node for the first time, we declare the incoming edge a tree edge
treeEdges.Add(edge);
CalculateSubtreeSizes(targetNode, seenNodes, treeEdges);
subtreeSize += targetNode.SubtreeSize;
}
}
}
root.Measure();
root.SubtreeSize = Math.Max(GetLateralSizeWithMargin(root), subtreeSize);
}
/// <summary>
/// Given SubtreeSize for each node, positions the nodes, in a left-to-right or top-to-bottom layout.
/// </summary>
/// <param name="node"></param>
/// <param name="lateralStart"></param>
/// <param name="mainStart"></param>
void CalculateNodePosRecursive(PositionedNode node, HashSet<PositionedEdge> treeEdges, double lateralBase, double mainBase)
{
double childsSubtreeSize = TreeChildNodes(node, treeEdges).Sum(child => child.SubtreeSize);
double center = TreeEdges(node, treeEdges).Count() == 0 ? 0 : 0.5 * (childsSubtreeSize - (GetLateralSizeWithMargin(node)));
if (center < 0) {
// if root is larger than subtree, it would be shifted below lateralStart
// -> make whole layout start at lateralStart
lateralBase -= center;
}
SetLateral(node, GetLateral(node) + lateralBase + center);
SetMain(node, mainBase);
double childLateral = lateralBase;
double childsMainFixed = GetMain(node) + GetMainSizeWithMargin(node);
foreach (var child in TreeChildNodes(node, treeEdges)) {
CalculateNodePosRecursive(child, treeEdges, childLateral, childsMainFixed);
childLateral += child.SubtreeSize;
}
}
IEnumerable<PositionedEdge> TreeEdges(PositionedNode node, HashSet<PositionedEdge> treeEdges)
{
return node.Edges.Where(e => treeEdges.Contains(e));
}
IEnumerable<PositionedNode> TreeChildNodes(PositionedNode node, HashSet<PositionedEdge> treeEdges)
{
return TreeEdges(node, treeEdges).Select(e => e.Target);
}
#region Horizontal / vertical layout helpers
double GetMainSizeWithMargin(PositionedNode node)
{
return (this.LayoutDirection == LayoutDirection.LeftRight) ? node.Width + NodeMarginH : node.Height + NodeMarginV;
}
double GetLateralSizeWithMargin(PositionedNode node)
{
return (this.LayoutDirection == LayoutDirection.LeftRight) ? node.Height + NodeMarginV : node.Width + NodeMarginH;
}
double GetMain(PositionedNode node)
{
return (this.LayoutDirection == LayoutDirection.LeftRight) ? node.Left : node.Top;
}
double GetLateral(PositionedNode node)
{
return (this.LayoutDirection == LayoutDirection.LeftRight) ? node.Top : node.Left;
}
void SetMain(PositionedNode node, double value)
{
if (this.LayoutDirection == LayoutDirection.LeftRight) {
node.Left = value;
} else {
node.Top = value;
}
}
void SetLateral(PositionedNode node, double value)
{
if (this.LayoutDirection == LayoutDirection.LeftRight) {
node.Top = value;
} else {
node.Left = value;
}
}
#endregion
}
}

137
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeLayouter.cs

@ -1,137 +0,0 @@ @@ -1,137 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System;
using System.Collections.Generic;
using System.Windows;
using Debugger.AddIn.Visualizers.Graph.Drawing;
using System.Linq;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// Calculates layout of <see cref="ObjectGraph" />, producing <see cref="PositionedGraph" />.
/// </summary>
public class TreeLayouter
{
private static readonly double horizNodeMargin = 30;
private static readonly double vertNodeMargin = 30;
GraphEdgeRouter edgeRouter = new GraphEdgeRouter();
public TreeLayouter()
{
}
/// <summary>
/// Calculates layout for given <see cref="ObjectGraph" />.
/// </summary>
/// <param name="objectGraph"></param>
/// <returns></returns>
public PositionedGraph CalculateLayout(ObjectGraph objectGraph, LayoutDirection direction, Expanded expanded)
{
var positionedGraph = BuildPositionedGraph(objectGraph, direction, expanded);
CalculateLayout(positionedGraph);
this.edgeRouter.RouteEdges(positionedGraph);
return positionedGraph;
}
PositionedGraph BuildPositionedGraph(ObjectGraph objectGraph, LayoutDirection direction, Expanded expanded)// Expanded is passed so that the correct ContentNodes are expanded in the PositionedNode
{
var treeNodeFor = new Dictionary<ObjectGraphNode, PositionedGraphNode>();
var resultGraph = new PositionedGraph();
// create empty PositionedNodes
foreach (ObjectGraphNode objectGraphNode in objectGraph.ReachableNodes) {
TreeGraphNode posNode = TreeGraphNode.Create(direction, objectGraphNode);
posNode.InitContentFromObjectNode(expanded);
posNode.HorizontalMargin = horizNodeMargin;
posNode.VerticalMargin = vertNodeMargin;
resultGraph.AddNode(posNode);
treeNodeFor[objectGraphNode] = posNode;
}
// create edges
foreach (PositionedGraphNode posNode in resultGraph.Nodes)
{
foreach (PositionedNodeProperty property in posNode.Properties) {
if (property.ObjectGraphProperty.TargetNode != null) {
ObjectGraphNode targetObjectNode = property.ObjectGraphProperty.TargetNode;
PositionedGraphNode edgeTarget = treeNodeFor[targetObjectNode];
property.Edge = new TreeGraphEdge
{ IsTreeEdge = false, Name = property.Name, Source = property, Target = edgeTarget };
}
}
}
resultGraph.Root = treeNodeFor[objectGraph.Root];
return resultGraph;
}
void CalculateLayout(PositionedGraph resultGraph)
{
HashSet<PositionedGraphNode> seenNodes = new HashSet<PositionedGraphNode>();
// first layout pass
CalculateSubtreeSizes((TreeGraphNode)resultGraph.Root, seenNodes);
// second layout pass
CalculateNodePosRecursive((TreeGraphNode)resultGraph.Root, 0, 0);
}
// determines which edges are tree edges, and calculates subtree size for each node
private void CalculateSubtreeSizes(TreeGraphNode root, HashSet<PositionedGraphNode> seenNodes)
{
seenNodes.Add(root);
double subtreeSize = 0;
foreach (PositionedNodeProperty property in root.Properties) {
var edge = property.Edge as TreeGraphEdge; // we know that these egdes are TreeEdges
if (edge != null) {
var neigborNode = (TreeGraphNode)edge.Target;
if (seenNodes.Contains(neigborNode)) {
edge.IsTreeEdge = false;
} else {
edge.IsTreeEdge = true;
CalculateSubtreeSizes(neigborNode, seenNodes);
subtreeSize += neigborNode.SubtreeSize;
}
}
}
root.Measure();
root.SubtreeSize = Math.Max(root.LateralSizeWithMargin, subtreeSize);
}
/// <summary>
/// Given SubtreeSize for each node, positions the nodes, in a left-to-right or top-to-bottom fashion.
/// </summary>
/// <param name="node"></param>
/// <param name="lateralStart"></param>
/// <param name="mainStart"></param>
private void CalculateNodePosRecursive(TreeGraphNode node, double lateralStart, double mainStart)
{
double childsSubtreeSize = node.Childs.Sum(child => child.SubtreeSize);
// center this node
double center = node.ChildEdges.Count() == 0 ? 0 : 0.5 * (childsSubtreeSize - (node.LateralSizeWithMargin));
if (center < 0) {
// if root is larger than subtree, it would be shifted below lateralStart
// -> make whole layout start at lateralStart
lateralStart -= center;
}
// design alternatives
// node.MainPos += center; // used this
// Adapt(node).PosLateral += center; // TreeNodeAdapterLR + TreeNodeAdapterTB
// SetMainPos(node, GetMainPos(node) + 10) // TreeNodeAdapterLR + TreeNodeAdapterTB, no creation
node.LateralCoord += lateralStart + center;
node.MainCoord = mainStart;
double childLateral = lateralStart;
double childsMainFixed = node.MainCoord + node.MainSizeWithMargin;
foreach (TreeGraphNode child in node.Childs) {
CalculateNodePosRecursive(child, childLateral, childsMainFixed);
childLateral += child.SubtreeSize;
}
}
}
}

27
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/ObjectGraph/ObjectGraph.cs

@ -8,8 +8,11 @@ using System.Text; @@ -8,8 +8,11 @@ using System.Text;
namespace Debugger.AddIn.Visualizers.Graph
{
/// <summary>
/// Object graph built by <see cref="ObjectGraphBuilder"/>. The graph is never empty.
/// Object graph built by <see cref="ObjectGraphBuilder"/>.
/// </summary>
/// <remarks>
/// The graph is never empty.
/// </remarks>
public class ObjectGraph
{
/// <summary>
@ -23,16 +26,16 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -23,16 +26,16 @@ namespace Debugger.AddIn.Visualizers.Graph
/// <param name="node">node to be added</param>
internal void AddNode(ObjectGraphNode node)
{
_nodes.Add(node);
nodes.Add(node);
}
private List<ObjectGraphNode> _nodes = new List<ObjectGraphNode>();
private List<ObjectGraphNode> nodes = new List<ObjectGraphNode>();
/// <summary>
/// All nodes in the graph. Should always contain at least one node.
/// </summary>
public IEnumerable<ObjectGraphNode> Nodes
{
get { return _nodes; }
get { return nodes; }
}
// HACK to support expanding/collapsing, because expanding is done by modifying ObjectGraph and rebuiling PosGraph
@ -40,22 +43,20 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -40,22 +43,20 @@ namespace Debugger.AddIn.Visualizers.Graph
{
get
{
var seenNodes = new HashSet<ObjectGraphNode>();
determineReachableNodes(this.Root, seenNodes);
foreach (var node in seenNodes)
{
var reachableNodes = new HashSet<ObjectGraphNode>();
FindReachableNodesRecursive(this.Root, reachableNodes);
foreach (var node in reachableNodes) {
yield return node;
}
}
}
private void determineReachableNodes(ObjectGraphNode root, HashSet<ObjectGraphNode> seenNodes)
void FindReachableNodesRecursive(ObjectGraphNode root, HashSet<ObjectGraphNode> seenNodes)
{
seenNodes.Add(root);
foreach(var prop in root.Properties)
{
foreach(var prop in root.Properties) {
if (prop.TargetNode != null && !seenNodes.Contains(prop.TargetNode))
determineReachableNodes(prop.TargetNode, seenNodes);
FindReachableNodesRecursive(prop.TargetNode, seenNodes);
}
}
}

126
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/ObjectGraphControl.xaml.cs

@ -14,9 +14,11 @@ using System.Windows.Input; @@ -14,9 +14,11 @@ using System.Windows.Input;
using System.Windows.Media;
using Debugger.AddIn.Visualizers.Graph.Layout;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Services;
using ICSharpCode.NRefactory.Ast;
using Log = ICSharpCode.Core.LoggingService;
namespace Debugger.AddIn.Visualizers.Graph
{
@ -33,7 +35,6 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -33,7 +35,6 @@ namespace Debugger.AddIn.Visualizers.Graph
private PositionedGraph oldPosGraph;
private PositionedGraph currentPosGraph;
private GraphDrawer graphDrawer;
private Layout.TreeLayouter layouter;
/// <summary> Long-lived map telling which graph nodes and content nodes the user expanded. </summary>
private Expanded expanded = new Expanded();
@ -50,7 +51,6 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -50,7 +51,6 @@ namespace Debugger.AddIn.Visualizers.Graph
this.layoutViewModel.PropertyChanged += new PropertyChangedEventHandler(layoutViewModel_PropertyChanged);
this.cmbLayoutDirection.DataContext = this.layoutViewModel;
this.layouter = new TreeLayouter();
this.graphDrawer = new GraphDrawer(this.canvas);
}
@ -61,7 +61,40 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -61,7 +61,40 @@ namespace Debugger.AddIn.Visualizers.Graph
public void Refresh()
{
refreshGraph();
clearErrorMessage();
if (string.IsNullOrEmpty(txtExpression.Text))
{
this.graphDrawer.ClearCanvas();
return;
}
if (debuggerService.IsProcessRunning) // "Process not paused" exception still occurs
{
ErrorMessage("Cannot inspect when the process is running.");
return;
}
bool isSuccess = true;
try
{
this.objectGraph = RebuildGraph(txtExpression.Text);
}
catch(DebuggerVisualizerException ex)
{
isSuccess = false;
ErrorMessage(ex.Message);
}
catch(Debugger.GetValueException ex)
{
isSuccess = false;
ErrorMessage("Expression cannot be evaluated - " + ex.Message);
}
if (isSuccess)
{
LayoutGraph(this.objectGraph);
}
else
{
this.graphDrawer.ClearCanvas();
}
}
private ICSharpCode.NRefactory.Ast.Expression shownExpression;
@ -97,80 +130,38 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -97,80 +130,38 @@ namespace Debugger.AddIn.Visualizers.Graph
this.Refresh();
}
void refreshGraph()
{
clearErrorMessage();
if (string.IsNullOrEmpty(txtExpression.Text))
{
this.graphDrawer.ClearCanvas();
return;
}
if (debuggerService.IsProcessRunning) // "Process not paused" exception still occurs
{
showErrorMessage("Cannot inspect when the process is running.");
return;
}
bool graphBuiltOk = true;
try
{
this.objectGraph = rebuildGraph(txtExpression.Text);
}
catch(DebuggerVisualizerException ex)
{
graphBuiltOk = false;
showErrorMessage(ex.Message);
}
catch(Debugger.GetValueException ex)
{
graphBuiltOk = false;
showErrorMessage("Expression cannot be evaluated - " + ex.Message);
}
if (graphBuiltOk)
{
layoutGraph(this.objectGraph);
}
else
{
this.graphDrawer.ClearCanvas();
}
}
ObjectGraph rebuildGraph(string expression)
ObjectGraph RebuildGraph(string expression)
{
this.objectGraphBuilder = new ObjectGraphBuilder(debuggerService);
ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Building graph for expression: " + txtExpression.Text);
Log.Debug("Debugger visualizer: Building graph for expression: " + txtExpression.Text);
return this.objectGraphBuilder.BuildGraphForExpression(expression, this.expanded.Expressions);
}
void layoutGraph(ObjectGraph graph)
void LayoutGraph(ObjectGraph graph)
{
if (this.oldPosGraph != null)
{
foreach (var oldNode in this.oldPosGraph.Nodes)
{
if (this.oldPosGraph != null) {
foreach (var oldNode in this.oldPosGraph.Nodes) {
// controls from old graph would be garbage collected, reuse them
NodeControlCache.Instance.ReturnForReuse(oldNode.NodeVisualControl);
}
}
this.oldPosGraph = this.currentPosGraph;
ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Calculating graph layout");
this.currentPosGraph = this.layouter.CalculateLayout(graph, layoutViewModel.SelectedEnumValue, this.expanded);
ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Graph layout done");
registerExpandCollapseEvents(this.currentPosGraph);
Log.Debug("Debugger visualizer: Calculating graph layout");
var layoutDirection = layoutViewModel.SelectedEnumValue;
this.currentPosGraph = new TreeLayout(layoutDirection).CalculateLayout(graph, this.expanded);
Log.Debug("Debugger visualizer: Graph layout done");
RegisterExpandCollapseEvents(this.currentPosGraph);
var graphDiff = new GraphMatcher().MatchGraphs(oldPosGraph, currentPosGraph);
ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: starting graph animation");
Log.Debug("Debugger visualizer: starting graph animation");
this.graphDrawer.StartAnimation(oldPosGraph, currentPosGraph, graphDiff);
//this.graphDrawer.Draw(this.currentPosGraph); // buggy layout with NodeControlCache
}
void layoutViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "SelectedEnumValue") // TODO special event for enum value change
{
if (this.objectGraph != null)
{
layoutGraph(this.objectGraph);
if (e.PropertyName == "SelectedEnumValue") {
if (this.objectGraph != null) {
LayoutGraph(this.objectGraph);
}
}
}
@ -180,17 +171,16 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -180,17 +171,16 @@ namespace Debugger.AddIn.Visualizers.Graph
this.pnlError.Visibility = Visibility.Collapsed;
}
void showErrorMessage(string message)
void ErrorMessage(string message)
{
this.txtError.Text = message;
this.pnlError.Visibility = Visibility.Visible;
//MessageBox.Show(ex.Message, "Exception", MessageBoxButton.OK, MessageBoxImage.Error);
}
void registerExpandCollapseEvents(PositionedGraph posGraph)
void RegisterExpandCollapseEvents(PositionedGraph posGraph)
{
foreach (var node in posGraph.Nodes)
{
foreach (var node in posGraph.Nodes) {
node.PropertyExpanded += new EventHandler<PositionedPropertyEventArgs>(node_PropertyExpanded);
node.PropertyCollapsed += new EventHandler<PositionedPropertyEventArgs>(node_PropertyCollapsed);
node.ContentNodeExpanded += new EventHandler<ContentNodeEventArgs>(node_ContentNodeExpanded);
@ -201,13 +191,13 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -201,13 +191,13 @@ namespace Debugger.AddIn.Visualizers.Graph
void node_ContentNodeExpanded(object sender, ContentNodeEventArgs e)
{
expanded.ContentNodes.SetExpanded(e.Node);
layoutGraph(this.objectGraph);
LayoutGraph(this.objectGraph);
}
void node_ContentNodeCollapsed(object sender, ContentNodeEventArgs e)
{
expanded.ContentNodes.SetCollapsed(e.Node);
layoutGraph(this.objectGraph);
LayoutGraph(this.objectGraph);
}
void node_PropertyExpanded(object sender, PositionedPropertyEventArgs e)
@ -218,7 +208,7 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -218,7 +208,7 @@ namespace Debugger.AddIn.Visualizers.Graph
// add edge (+ possibly nodes) to underlying object graph (no need to fully rebuild)
// TODO can add more nodes if they are expanded - now this adds always one node
e.Property.ObjectGraphProperty.TargetNode = this.objectGraphBuilder.ObtainNodeForExpression(e.Property.Expression);
layoutGraph(this.objectGraph);
LayoutGraph(this.objectGraph);
}
void node_PropertyCollapsed(object sender, PositionedPropertyEventArgs e)
@ -228,7 +218,7 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -228,7 +218,7 @@ namespace Debugger.AddIn.Visualizers.Graph
// just remove edge from underlying object graph (no need to fully rebuild)
e.Property.ObjectGraphProperty.TargetNode = null;
layoutGraph(this.objectGraph);
LayoutGraph(this.objectGraph);
}
}
}

Loading…
Cancel
Save