diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj index a7ab64f5bb..369d9f51d1 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj @@ -168,8 +168,11 @@ + + + @@ -192,6 +195,8 @@ + + diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/CanvasLocationAdapter.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/CanvasLocationAdapter.cs new file mode 100644 index 0000000000..4549f46345 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/CanvasLocationAdapter.cs @@ -0,0 +1,35 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Windows; +using System.Windows.Controls; + +namespace Debugger.AddIn.Visualizers.Graph.Drawing +{ + /// + /// Provides LocationProperty, which enables animating Canvas.LeftProperty and Canvas.TopProperty + /// at the same time using PointAnimation. + /// + public class CanvasLocationAdapter : UIElement + { + /// + /// Add this dependency property to UIElement object and animate it using PointAnimation. + /// + public static DependencyProperty LocationProperty = DependencyProperty.RegisterAttached("Location", typeof(Point), typeof(Canvas), new FrameworkPropertyMetadata(new Point(0, 0), new PropertyChangedCallback(locationChanged)), new ValidateValueCallback(IsPointValid)); + + private static void locationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + Canvas.SetLeft((UIElement)d, ((Point)e.NewValue).X); + Canvas.SetTop((UIElement)d, ((Point)e.NewValue).Y); + } + + private static bool IsPointValid(object o) + { + return true; + } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/GraphDrawer.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/GraphDrawer.cs index 4f583503d0..97ffd5bbae 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/GraphDrawer.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/GraphDrawer.cs @@ -12,6 +12,7 @@ using System.Windows.Controls; using System.Windows.Media; using System.Windows; using System.Windows.Shapes; +using System.Windows.Media.Animation; using Debugger.AddIn.Visualizers.Graph.Drawing; using Debugger.AddIn.Visualizers.Graph.Layout; @@ -20,98 +21,179 @@ namespace Debugger.AddIn.Visualizers.Graph /// /// Draws on Canvas. /// - public class GraphDrawer - { - public GraphDrawer() - { - } - - /// - /// Draws on Canvas. - /// - /// Graph to draw. - /// Destination Canvas. - public static void Draw(PositionedGraph posGraph, Canvas canvas) - { - canvas.Children.Clear(); - - // draw nodes - foreach (PositionedNode node in posGraph.Nodes) - { - canvas.Children.Add(node.NodeVisualControl); - Canvas.SetLeft(node.NodeVisualControl, node.Left); - Canvas.SetTop(node.NodeVisualControl, node.Top); - } - - // draw edges - foreach (PositionedEdge edge in posGraph.Edges) - { - Path edgePath = createEdgeWithArrow(edge); - canvas.Children.Add(edgePath); - } - } - - private static Path createEdgeWithArrow(PositionedEdge edge) - { - Path path = new Path(); - path.Stroke = Brushes.Black; - path.Fill = Brushes.Black; - path.StrokeThickness = 1; + public class GraphDrawer + { + Canvas canvas; + + public GraphDrawer(Canvas canvas) + { + this.canvas = canvas; + } + + /// + /// Starts animation from oldGraph to newGraph. + /// + /// + /// + /// + public void StartAnimation(PositionedGraph oldGraph, PositionedGraph newGraph, GraphDiff diff) + { + if (oldGraph == null) + { + Draw(newGraph); + return; + } + + double seconds = 1; + var durationMove = new Duration(TimeSpan.FromSeconds(seconds)); + var durationFade = durationMove; + + DoubleAnimation fadeOutAnim = new DoubleAnimation(1.0, 0.0, durationFade); + DoubleAnimation fadeInAnim = new DoubleAnimation(0.0, 1.0, durationFade); + + foreach (UIElement drawing in canvas.Children) + { + var arrow = drawing as Path; + if (arrow != null) + { + arrow.BeginAnimation(UIElement.OpacityProperty, fadeOutAnim); + } + } + + foreach (PositionedEdge edge in newGraph.Edges) + { + addEdgeToCanvas(edge).BeginAnimation(UIElement.OpacityProperty, fadeInAnim); + } + + foreach (PositionedNode removedNode in diff.RemovedNodes) + { + removedNode.NodeVisualControl.BeginAnimation(UIElement.OpacityProperty, fadeOutAnim); + } + + foreach (PositionedNode addedNode in diff.AddedNodes) + { + addNodeToCanvas(addedNode).BeginAnimation(UIElement.OpacityProperty, fadeInAnim); + } + + bool first = true; + foreach (PositionedNode node in diff.ChangedNodes) + { + var newNode = diff.GetMatchingNewNode(node); + + PointAnimation anim = new PointAnimation(); + if (first) + { + anim.Completed += new EventHandler((o, e) => { Draw(newGraph); }); + first = false; + } + //anim.From = new Point(Canvas.GetLeft(node.NodeVisualControl), Canvas.GetTop(node.NodeVisualControl)); + anim.From = node.LeftTop; + + anim.To = newNode.LeftTop; + anim.DecelerationRatio = 0.3; + anim.AccelerationRatio = 0.3; + anim.Duration = durationMove; + node.NodeVisualControl.BeginAnimation(CanvasLocationAdapter.LocationProperty, anim); + } + } + + /// + /// Draws on Canvas. + /// + /// Graph to draw. + /// Destination Canvas. + public void Draw(PositionedGraph posGraph) + { + canvas.Children.Clear(); + + // draw nodes + foreach (PositionedNode node in posGraph.Nodes) + { + addNodeToCanvas(node); + } + + // draw edges + foreach (PositionedEdge edge in posGraph.Edges) + { + addEdgeToCanvas(edge); + } + } + + private NodeControl addNodeToCanvas(PositionedNode node) + { + canvas.Children.Add(node.NodeVisualControl); + Canvas.SetLeft(node.NodeVisualControl, node.Left); + Canvas.SetTop(node.NodeVisualControl, node.Top); + return node.NodeVisualControl; + } + + private Path addEdgeToCanvas(PositionedEdge edge) + { + Path edgePath = createEdgeWithArrow(edge); + canvas.Children.Add(edgePath); + return edgePath; + } + + private Path createEdgeWithArrow(PositionedEdge edge) + { + Path path = new Path(); + path.Stroke = Brushes.Black; + path.Fill = Brushes.Black; + path.StrokeThickness = 1; - PathGeometry geometry = new PathGeometry(); + PathGeometry geometry = new PathGeometry(); - geometry.Figures.Add(createEdgeSpline(edge)); - geometry.Figures.Add(createEdgeArrow(edge)); - - path.Data = geometry; - return path; - } - - private static PathFigure createEdgeSpline(PositionedEdge edge) - { - PathFigure figure = new PathFigure(); - figure.IsClosed = false; - figure.IsFilled = false; - - figure.StartPoint = edge.SplinePoints[0]; - for (int i = 1; i < edge.SplinePoints.Count; i += 3) - { - figure.Segments.Add(new BezierSegment(edge.SplinePoints[i], edge.SplinePoints[i + 1], edge.SplinePoints[i + 2], true)); - } - - return figure; - } - - private static PathFigure createEdgeArrow(PositionedEdge edge) - { + geometry.Figures.Add(createEdgeSpline(edge)); + geometry.Figures.Add(createEdgeArrow(edge)); + + path.Data = geometry; + return path; + } + + private PathFigure createEdgeSpline(PositionedEdge edge) + { + PathFigure figure = new PathFigure(); + figure.IsClosed = false; + figure.IsFilled = false; + + figure.StartPoint = edge.SplinePoints[0]; + for (int i = 1; i < edge.SplinePoints.Count; i += 3) + { + figure.Segments.Add(new BezierSegment(edge.SplinePoints[i], edge.SplinePoints[i + 1], edge.SplinePoints[i + 2], true)); + } + + return figure; + } + + private PathFigure createEdgeArrow(PositionedEdge edge) + { + Point splineEndPoint = edge.SplinePoints[edge.SplinePoints.Count - 1]; + Point splineEndHandlePoint = edge.SplinePoints[edge.SplinePoints.Count - 2]; + + Vector tangent = splineEndPoint - splineEndHandlePoint; + tangent.Normalize(); + tangent = tangent * 20; + Point basePoint = splineEndPoint - 0.2 * tangent; + PathFigure arrowFigure = new PathFigure(); - arrowFigure.IsClosed = true; - arrowFigure.IsFilled = true; - - Point endPoint = edge.SplinePoints[edge.SplinePoints.Count - 1]; - Point endHandlePoint = edge.SplinePoints[edge.SplinePoints.Count - 2]; - - Vector tangent = endPoint - endHandlePoint; - tangent.Normalize(); - tangent = tangent * 20; - Point basePoint = endPoint - 0.2 * tangent; + arrowFigure.IsClosed = true; + arrowFigure.IsFilled = true; - arrowFigure.StartPoint = basePoint + tangent * 0.4; // arrow tip - - Vector tangent2 = rotate90(tangent); - arrowFigure.Segments.Add(new LineSegment(basePoint + tangent2 * 0.15, true)); - arrowFigure.Segments.Add(new LineSegment(basePoint - tangent2 * 0.15, true)); - + arrowFigure.StartPoint = basePoint + tangent * 0.4; // arrow tip + 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) - { - // (x, y) -> (y, -x) - double t = v.X; - v.X = v.Y; - v.Y = -t; - return v; - } - } + } + + private static Vector rotate90(Vector v) + { + // (x, y) -> (y, -x) + double t = v.X; + v.X = v.Y; + v.Y = -t; + return v; + } + } } diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/GraphDiff.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/GraphDiff.cs new file mode 100644 index 0000000000..0b4952f7b7 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/GraphDiff.cs @@ -0,0 +1,82 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Debugger.AddIn.Visualizers.Graph.Utils; + +namespace Debugger.AddIn.Visualizers.Graph.Layout +{ + /// + /// Describes changes between 2 s. + /// + public class GraphDiff + { + private List addedNodes = new List(); + private List deletedNodes = new List(); + private List changedNodes = new List(); + private Dictionary matching = new Dictionary(); + + /// + /// Nodes in the new graph that were added. + /// + public IList AddedNodes + { + get { return addedNodes.AsReadOnly(); } + } + + /// + /// Nodes in the old graph that were removed. + /// + public IList RemovedNodes + { + get { return deletedNodes.AsReadOnly(); } + } + + /// + /// Nodes in the old graph that were chaged. + /// These have matching new nodes, which can be obtained by . + /// + public IList ChangedNodes + { + get { return changedNodes.AsReadOnly(); } + } + + public PositionedNode GetMatchingNewNode(PositionedNode oldNode) + { + return matching.GetValue(oldNode); + } + + internal void SetAdded(PositionedNode addedNode) + { + addedNodes.Add(addedNode); + } + + internal void SetRemoved(PositionedNode removeddNode) + { + deletedNodes.Add(removeddNode); + } + + internal void SetMatching(PositionedNode matchFrom, PositionedNode matchTo) + { + matching[matchFrom] = matchTo; + changedNodes.Add(matchFrom); + } + + public GraphDiff() + { + + } + + /*public void MakeReadOnly() + { + addedNodes = ((List)addedNodes).AsReadOnly(); + deletedNodes = ((List)deletedNodes).AsReadOnly(); + changedNodes = ((List)changedNodes).AsReadOnly(); + }*/ + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/GraphMatcher.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/GraphMatcher.cs new file mode 100644 index 0000000000..e8a583c588 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/GraphMatcher.cs @@ -0,0 +1,100 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; +using Debugger.AddIn.Visualizers.Graph.Utils; + +namespace Debugger.AddIn.Visualizers.Graph.Layout +{ + /// + /// Calculates diff of 2 s. + /// + public class GraphMatcher + { + public GraphMatcher() + { + } + + public GraphDiff MatchGraphs(PositionedGraph oldGraph, PositionedGraph newGraph) + { + // handle any of the graphs null + if (oldGraph == null) + { + if (newGraph == null) + { + return new GraphDiff(); + } + else + { + GraphDiff addAllDiff = new GraphDiff(); + foreach (PositionedNode newNode in newGraph.Nodes) + { + addAllDiff.SetAdded(newNode); + } + return addAllDiff; + } + } + + // both graph are not null + GraphDiff diff = new GraphDiff(); + + Dictionary newNodeForHashCode = buildHashToNodeMap(newGraph); + Dictionary newNodeMatched = new Dictionary(); + + foreach (PositionedNode oldNode in oldGraph.Nodes) + { + PositionedNode matchingNode = matchNode(oldNode, newNodeForHashCode); + if (matchingNode != null) + { + diff.SetMatching(oldNode, matchingNode); + newNodeMatched[matchingNode] = true; + } + else + { + diff.SetRemoved(oldNode); + } + } + foreach (PositionedNode newNode in newGraph.Nodes) + { + if (!newNodeMatched.ContainsKey(newNode)) + { + diff.SetAdded(newNode); + } + } + + return diff; + } + + private Dictionary buildHashToNodeMap(PositionedGraph graph) + { + var hashToNodeMap = new Dictionary(); + foreach (PositionedNode node in graph.Nodes) + { + hashToNodeMap[node.ObjectNode.HashCode] = node; + } + return hashToNodeMap; + } + + private PositionedNode matchNode(PositionedNode oldNode, Dictionary newNodeMap) + { + PositionedNode newNodeFound = newNodeMap.GetValue(oldNode.ObjectNode.HashCode); + if ((newNodeFound != null) && isSameAddress(oldNode, newNodeFound)) + { + return newNodeFound; + } + else + { + return null; + } + } + + private bool isSameAddress(PositionedNode node1, PositionedNode node2) + { + return node1.ObjectNode.PermanentReference.GetObjectAddress() == node2.ObjectNode.PermanentReference.GetObjectAddress(); + } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedNode.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedNode.cs index 54373693a8..85084708cb 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedNode.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedNode.cs @@ -12,13 +12,20 @@ using System.Windows; namespace Debugger.AddIn.Visualizers.Graph.Layout { /// - /// Node with position information. + /// Node with added position information. /// public class PositionedNode { - public PositionedNode(NodeControl nodeVisualControl) + private ObjectNode objectNode; + public ObjectNode ObjectNode + { + get { return objectNode; } + } + + public PositionedNode(NodeControl nodeVisualControl, ObjectNode objectNode) { this.nodeVisualControl = nodeVisualControl; + this.objectNode = objectNode; } public double Left { get; set; } diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeLayouter.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeLayouter.cs index 77ad5044e4..36b01e9bf5 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeLayouter.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeLayouter.cs @@ -31,11 +31,6 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout { } - private TreeNode createTreeNode(NodeControl nodeVisualControl) - { - return TreeNode.Create(this.layoutDirection, nodeVisualControl); - } - /// /// Calculates layout for given . /// @@ -66,7 +61,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout nodeVisualControl.GraphNode = objectGraphNode; nodeVisualControl.Measure(new Size(500, 500)); - TreeNode newTreeNode = createTreeNode(nodeVisualControl); + TreeNode newTreeNode = TreeNode.Create(this.layoutDirection, nodeVisualControl, objectGraphNode); newTreeNode.HorizontalMargin = horizNodeMargin; newTreeNode.VerticalMargin = vertNodeMargin; resultGraph.nodes.Add(newTreeNode); @@ -104,7 +99,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout { // center this node double subtreeSize = node.Childs.Sum(child => child.SubtreeSize); - double center = node.Childs.Count() == 0 ? 0 : 0.5 * (subtreeSize - (node.LateralSize + node.LateralMargin)); + double center = node.ChildEdges.Count() == 0 ? 0 : 0.5 * (subtreeSize - (node.LateralSize + node.LateralMargin)); // design alternatives // node.MainPos += center; // used this diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNode.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNode.cs index 4be7d73e92..4cb9271d64 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNode.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNode.cs @@ -18,11 +18,11 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout /// public abstract class TreeNode : PositionedNode { - public static TreeNode Create(LayoutDirection direction, NodeControl nodeVisualControl) + public static TreeNode Create(LayoutDirection direction, NodeControl nodeVisualControl, ObjectNode objectNode) { switch (direction) { - case LayoutDirection.TopBottom: return new TreeNodeTB(nodeVisualControl); - case LayoutDirection.LeftRight: return new TreeNodeLR(nodeVisualControl); + case LayoutDirection.TopBottom: return new TreeNodeTB(nodeVisualControl, objectNode); + case LayoutDirection.LeftRight: return new TreeNodeLR(nodeVisualControl, objectNode); default: throw new DebuggerVisualizerException("Unsupported layout direction: " + direction.ToString()); } } @@ -30,7 +30,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout public double HorizontalMargin { get; set; } public double VerticalMargin { get; set; } - protected TreeNode(NodeControl nodeVisualControl) : base(nodeVisualControl) + protected TreeNode(NodeControl nodeVisualControl, ObjectNode objectNode) : base(nodeVisualControl, objectNode) { } diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeLR.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeLR.cs index 50efa075a1..0b668d5f44 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeLR.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeLR.cs @@ -10,11 +10,11 @@ using Debugger.AddIn.Visualizers.Graph.Drawing; namespace Debugger.AddIn.Visualizers.Graph.Layout { /// - /// Description of TreeNodeLR. + /// TreeNode used in LR layout mode. /// public class TreeNodeLR: TreeNode { - public TreeNodeLR(NodeControl nodeControl) : base(nodeControl) + public TreeNodeLR(NodeControl nodeControl, ObjectNode objectNode) : base(nodeControl, objectNode) { } diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeTB.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeTB.cs index 5c5a323ed3..ca68132f69 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeTB.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeTB.cs @@ -10,11 +10,11 @@ using Debugger.AddIn.Visualizers.Graph.Drawing; namespace Debugger.AddIn.Visualizers.Graph.Layout { /// - /// Description of TreeNodeTB. + /// TreeNode used in TB layout mode. /// public class TreeNodeTB : TreeNode { - public TreeNodeTB(NodeControl nodeControl) : base(nodeControl) + public TreeNodeTB(NodeControl nodeControl, ObjectNode objectNode) : base(nodeControl, objectNode) { } diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphBuilder.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphBuilder.cs index de649cd769..9b91f6f303 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphBuilder.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphBuilder.cs @@ -9,7 +9,6 @@ using System.Collections.Generic; using ICSharpCode.SharpDevelop.Services; using Debugger; using Debugger.MetaData; -using Debugger.Wrappers.CorDebug; using Debugger.Expressions; using Debugger.AddIn.Visualizers.Graph.Utils; @@ -159,10 +158,11 @@ namespace Debugger.AddIn.Visualizers.Graph private ObjectNode createNewNode(Value permanentReference) { ObjectNode newNode = new ObjectNode(); + newNode.HashCode = invokeGetHashCode(permanentReference); resultGraph.AddNode(newNode); // remember this node's hashcode for quick lookup - objectNodesForHashCode.Add(invokeGetHashCode(permanentReference), newNode); + objectNodesForHashCode.Add(newNode.HashCode, newNode); // permanent reference to the object this node represents is useful for graph building, // and matching nodes in animations @@ -189,9 +189,9 @@ namespace Debugger.AddIn.Visualizers.Graph { // if there is a node with same hash code, check if it has also the same address // (hash codes are not uniqe - http://stackoverflow.com/questions/750947/-net-unique-object-identifier) - ulong objectAddress = getObjectValue(value); + ulong objectAddress = value.GetObjectAddress(); ObjectNode nodeWithSameAddress = nodesWithSameHashCode.Find( - node => { return objectAddress == getObjectValue(node.PermanentReference); } ); + node => { return objectAddress == node.PermanentReference.GetObjectAddress(); } ); return nodeWithSameAddress; } } @@ -239,7 +239,7 @@ namespace Debugger.AddIn.Visualizers.Graph return (!typeOfValue.IsClass) || typeOfValue.IsString; } - #region helpers + #region Expression helpers private Value getPermanentReference(Expression expr) { @@ -258,13 +258,7 @@ namespace Debugger.AddIn.Visualizers.Graph private ulong getObjectAddress(Expression expr) { - return getObjectValue(expr.Evaluate(debuggerService.DebuggedProcess)); - } - - private ulong getObjectValue(Value val) - { - ICorDebugReferenceValue refVal = val.CorValue.CastTo(); - return refVal.Value; + return expr.Evaluate(debuggerService.DebuggedProcess).GetObjectAddress(); } #endregion diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Utils/DebuggerHelpers.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Utils/DebuggerHelpers.cs new file mode 100644 index 0000000000..49886eac20 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Utils/DebuggerHelpers.cs @@ -0,0 +1,23 @@ +// +// +// +// +// $Revision$ +// +using System; +using Debugger.Wrappers.CorDebug; + +namespace Debugger.AddIn.Visualizers.Graph.Utils +{ + public static class DebuggerHelpers + { + /// + /// Gets underlying address of object in the debuggee. + /// + public static ulong GetObjectAddress(this Value val) + { + ICorDebugReferenceValue refVal = val.CorValue.CastTo(); + return refVal.Value; + } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Utils/DictionaryExtensions.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Utils/DictionaryExtensions.cs new file mode 100644 index 0000000000..cc91a85fd5 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Utils/DictionaryExtensions.cs @@ -0,0 +1,30 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; + +namespace Debugger.AddIn.Visualizers.Graph.Utils +{ + public static class DictionaryExtensions + { + /// + /// Gets value from Dictionary, returns null if not found. + /// + public static TValue GetValue(this Dictionary dictionary, TKey key) where TValue : class + { + TValue outValue; + if (dictionary.TryGetValue(key, out outValue)) + { + return outValue; + } + else + { + return null; + } + } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml.cs index 8d8743fe27..f17911cb25 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml.cs @@ -28,30 +28,36 @@ namespace Debugger.AddIn.Visualizers.Graph /// public partial class VisualizerWPFWindow : Window { - private WindowsDebugger _debuggerService; + private WindowsDebugger debuggerService; private EnumViewModel layoutViewModel; private ObjectGraph objectGraph; + + private PositionedGraph oldGraph; + private PositionedGraph currentGraph; + private GraphDrawer graphDrawer; public VisualizerWPFWindow() { InitializeComponent(); - _debuggerService = DebuggerService.CurrentDebugger as WindowsDebugger; - if (_debuggerService == null) + debuggerService = DebuggerService.CurrentDebugger as WindowsDebugger; + if (debuggerService == null) throw new ApplicationException("Only windows debugger is currently supported"); - _debuggerService.IsProcessRunningChanged += new EventHandler(debuggerService_IsProcessRunningChanged); - _debuggerService.DebugStopped += new EventHandler(_debuggerService_DebugStopped); + debuggerService.IsProcessRunningChanged += new EventHandler(debuggerService_IsProcessRunningChanged); + debuggerService.DebugStopped += new EventHandler(_debuggerService_DebugStopped); this.layoutViewModel = new EnumViewModel(); this.layoutViewModel.PropertyChanged += new PropertyChangedEventHandler(layoutViewModel_PropertyChanged); this.DataContext = this.layoutViewModel; + + this.graphDrawer = new GraphDrawer(this.canvas); } public void debuggerService_IsProcessRunningChanged(object sender, EventArgs e) { // on step or breakpoint hit - if (!_debuggerService.IsProcessRunning) + if (!debuggerService.IsProcessRunning) { refreshGraph(); } @@ -59,7 +65,7 @@ namespace Debugger.AddIn.Visualizers.Graph public void _debuggerService_DebugStopped(object sender, EventArgs e) { - _debuggerService.IsProcessRunningChanged -= new EventHandler(debuggerService_IsProcessRunningChanged); + debuggerService.IsProcessRunningChanged -= new EventHandler(debuggerService_IsProcessRunningChanged); this.Close(); } @@ -78,11 +84,12 @@ namespace Debugger.AddIn.Visualizers.Graph void refreshGraph() { - ObjectGraphBuilder graphBuilder = new ObjectGraphBuilder(_debuggerService); + ObjectGraphBuilder graphBuilder = new ObjectGraphBuilder(debuggerService); this.objectGraph = null; try { + ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Building graph for expression: " + txtExpression.Text); this.objectGraph = graphBuilder.BuildGraphForExpression(txtExpression.Text); } catch(DebuggerVisualizerException ex) @@ -104,10 +111,15 @@ namespace Debugger.AddIn.Visualizers.Graph void layoutGraph(ObjectGraph graph) { + ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Calculating graph layout"); Layout.TreeLayouter layouter = new Layout.TreeLayouter(); - Layout.PositionedGraph posGraph = layouter.CalculateLayout(graph, layoutViewModel.SelectedEnumValue); + + this.oldGraph = this.currentGraph; + this.currentGraph = layouter.CalculateLayout(graph, layoutViewModel.SelectedEnumValue); - GraphDrawer.Draw(posGraph, canvas); + var graphDiff = new GraphMatcher().MatchGraphs(oldGraph, currentGraph); + this.graphDrawer.StartAnimation(oldGraph, currentGraph, graphDiff); + //this.graphDrawer.Draw(currentGraph); } void guiHandleException(System.Exception ex)