diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.addin b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.addin index e90edc33e1..9319ba1da9 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.addin +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.addin @@ -149,4 +149,13 @@ class = "ICSharpCode.SharpDevelop.Gui.OptionPanels.DebuggingSymbolsPanel"/> + + + + + + + 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 e3ea509420..4fe7b61ce3 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj @@ -19,7 +19,7 @@ 4096 false Always - v3.0 + v3.5 "C:\Program Files\SharpDevelop\3.0\bin\..\AddIns\AddIns\Misc\SourceAnalysis\Settings.SourceAnalysis" C:\Dokumente und Einstellungen\HP\Anwendungsdaten\ICSharpCode/SharpDevelop3.0\Settings.SourceAnalysis http://localhost/Debugger.AddIn/ @@ -62,7 +62,13 @@ 3.0 + + 3.0 + + + 3.5 + @@ -140,6 +146,7 @@ EditBreakpointScriptForm.cs + Configuration\GlobalAssemblyInfo.cs @@ -160,6 +167,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -223,6 +257,15 @@ ICSharpCode.Core.WinForms False + + + + + + + + + diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/DebuggerVisualizerException.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/DebuggerVisualizerException.cs new file mode 100644 index 0000000000..cc86d867f2 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/DebuggerVisualizerException.cs @@ -0,0 +1,31 @@ +// +// +// +// +// $Revision$ +// +using System; + +namespace Debugger.AddIn.Visualizers.Graph +{ + /// + /// Description of DebuggerVisualizerException. + /// + public class DebuggerVisualizerException : ApplicationException + { + public DebuggerVisualizerException() + : base() + { + } + + public DebuggerVisualizerException(string message) + : base(message) + { + } + + public DebuggerVisualizerException(string message, System.Exception innerException) + : base(message, innerException) + { + } + } +} 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 new file mode 100644 index 0000000000..14610089d7 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/GraphDrawer.cs @@ -0,0 +1,43 @@ +using System; +// +// +// +// +// $Revision$ +// +using System.Collections.Generic; +using System.Text; +using Debugger.AddIn.Visualizers.Graph; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows; +using Debugger.AddIn.Visualizers.Graph.Drawing; +using Debugger.AddIn.Visualizers.Graph.Layout; + +namespace Debugger.AddIn.Visualizers.Graph +{ + /// + /// Draws ObjectGraph on a Canvas. + /// + public class GraphDrawer + { + private ObjectGraph graph; + + public GraphDrawer(ObjectGraph graph) + { + this.graph = graph; + } + + public static void Draw(PositionedGraph posGraph, Canvas canvas) + { + canvas.Children.Clear(); + + foreach (PositionedNode node in posGraph.Nodes) + { + canvas.Children.Add(node.NodeVisualControl); + Canvas.SetLeft(node.NodeVisualControl, node.Left); + Canvas.SetTop(node.NodeVisualControl, node.Top); + } + } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/NodeControl.xaml b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/NodeControl.xaml new file mode 100644 index 0000000000..e135828cca --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/NodeControl.xaml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/NodeControl.xaml.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/NodeControl.xaml.cs new file mode 100644 index 0000000000..165fafea46 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/NodeControl.xaml.cs @@ -0,0 +1,78 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using Debugger.AddIn.Visualizers.Graph; + +namespace Debugger.AddIn.Visualizers.Graph.Drawing +{ + /// + /// UserControl used to display ObjectNode. + /// + public partial class NodeControl : UserControl + { + public NodeControl() + { + InitializeComponent(); + } + + private ObjectNode node; + /// + /// ObjectNode that this control displays. + /// + public ObjectNode GraphNode + { + get + { + return node; + } + set + { + node = value; + int row = 0; + // dynamically create TextBlocks and insert them to the 2-column propertyGrid + foreach (var property in node.Properties) + { + propertyGrid.RowDefinitions.Add(new RowDefinition()); + + TextBlock txtName = createTextBlock(property.Name); + propertyGrid.Children.Add(txtName); + Grid.SetRow(txtName, row); + Grid.SetColumn(txtName, 0); + + TextBlock txtValue = createTextBlock(property.Value); + propertyGrid.Children.Add(txtValue); + Grid.SetRow(txtValue, row); + Grid.SetColumn(txtValue, 1); + + row++; + } + } + } + + /// + /// Creates TextBlock with given text. + /// + private TextBlock createTextBlock(string text) + { + TextBlock newTextblock = new TextBlock(); + newTextblock.Text = text; + newTextblock.Padding = new Thickness(4); + return newTextblock; + } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedEdge.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedEdge.cs new file mode 100644 index 0000000000..1480f03264 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedEdge.cs @@ -0,0 +1,17 @@ +// +// +// +// +// $Revision$ +// +using System; + +namespace Debugger.AddIn.Visualizers.Graph.Layout +{ + /// + /// Edge with position information. + /// + public class PositionedEdge : NamedEdge + { + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedGraph.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedGraph.cs new file mode 100644 index 0000000000..df8eec8431 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedGraph.cs @@ -0,0 +1,44 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; + +namespace Debugger.AddIn.Visualizers.Graph.Layout +{ + /// + /// Graph with positioned nodes and edges. + /// + public class PositionedGraph + { + internal List nodes = new List(); + + /// + /// All nodes in the graph. + /// + public IEnumerable Nodes + { + get { return nodes; } + } + + /// + /// All edges in the graph. + /// + public IEnumerable Edges + { + get + { + foreach (PositionedNode node in this.Nodes) + { + foreach (PositionedEdge edge in node.Edges) + { + yield return edge; + } + } + } + } + } +} 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 new file mode 100644 index 0000000000..cdbe66a642 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedNode.cs @@ -0,0 +1,54 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; +using Debugger.AddIn.Visualizers.Graph.Drawing; + +namespace Debugger.AddIn.Visualizers.Graph.Layout +{ + /// + /// Node with position information. + /// + public class PositionedNode + { + public PositionedNode(NodeControl nodeVisualControl) + { + this.nodeVisualControl = nodeVisualControl; + } + + public double Left { get; set; } + public double Top { get; set; } + public double Width + { + get { return NodeVisualControl.DesiredSize.Width; } + } + public double Height + { + get { return NodeVisualControl.DesiredSize.Height; } + } + + private NodeControl nodeVisualControl; + /// + /// Visual control to be shown for this node. + /// + public NodeControl NodeVisualControl + { + get + { + return this.nodeVisualControl; + } + } + + public virtual IEnumerable Edges + { + get + { + return new PositionedEdge[]{}; + } + } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/LayoutDirection.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/LayoutDirection.cs new file mode 100644 index 0000000000..0c09ac6313 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/LayoutDirection.cs @@ -0,0 +1,19 @@ +// +// +// +// +// $Revision$ +// +using System; + +namespace Debugger.AddIn.Visualizers.Graph.Layout +{ + /// + /// Direction of tree layout. + /// + public enum LayoutDirection + { + TopBottom, + LeftRight + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeEdge.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeEdge.cs new file mode 100644 index 0000000000..ae7f156e1d --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeEdge.cs @@ -0,0 +1,17 @@ +// +// +// +// +// $Revision$ +// +using System; + +namespace Debugger.AddIn.Visualizers.Graph.Layout +{ + /// + /// Description of TreeEdge. + /// + public class TreeEdge : PositionedEdge + { + } +} 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 new file mode 100644 index 0000000000..77685bd344 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeLayouter.cs @@ -0,0 +1,122 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; +using System.Windows; +using Debugger.AddIn.Visualizers.Graph.Drawing; +using System.Linq; + +namespace Debugger.AddIn.Visualizers.Graph.Layout +{ + /// + /// Calculates layout of , producing . + /// + public class TreeLayouter + { + private static readonly double horizNodeMargin = 20; + private static readonly double vertNodeMargin = 20; + + private LayoutDirection layoutDirection = LayoutDirection.TopBottom; + + PositionedGraph resultGraph = null; + + Dictionary treeNodeFor = new Dictionary(); + Dictionary seenNodes = new Dictionary(); + + public TreeLayouter() + { + } + + private TreeNode createTreeNode(NodeControl nodeVisualControl) + { + return TreeNode.Create(this.layoutDirection, nodeVisualControl); + } + + /// + /// Calculates layout for given . + /// + /// + /// + public PositionedGraph CalculateLayout(ObjectGraph objectGraph, LayoutDirection direction) + { + resultGraph = new PositionedGraph(); + layoutDirection = direction; + + var seenNodes = new Dictionary(); + + TreeNode tree = buildTreeRecursive(objectGraph.Root); + calculateNodePosRecursive(tree, 0, 0); + + return resultGraph; + } + + private TreeNode buildTreeRecursive(ObjectNode objectGraphNode) + { + seenNodes.Add(objectGraphNode, null); + + NodeControl nodeVisualControl = new NodeControl(); + nodeVisualControl.GraphNode = objectGraphNode; + nodeVisualControl.Measure(new Size(500, 500)); + + TreeNode newTreeNode = createTreeNode(nodeVisualControl); + newTreeNode.HorizontalMargin = horizNodeMargin; + newTreeNode.VerticalMargin = vertNodeMargin; + resultGraph.nodes.Add(newTreeNode); + treeNodeFor[objectGraphNode] = newTreeNode; + + double subtreeSize = 0; + foreach (ObjectEdge edge in objectGraphNode.Edges) + { + ObjectNode neighbor = edge.TargetNode; + if (seenNodes.ContainsKey(neighbor)) + { + newTreeNode.AdditionalNeighbors.Add(new TreeEdge { Name = edge.Name, SourceNode = newTreeNode, TargetNode = treeNodeFor[neighbor]}); + } + else + { + TreeNode newChild = buildTreeRecursive(neighbor); + newTreeNode.ChildEdges.Add(new TreeEdge { Name = edge.Name, SourceNode = newTreeNode, TargetNode = newChild}); + + subtreeSize += newChild.SubtreeSize; + } + } + subtreeSize = Math.Max(newTreeNode.LateralSize + newTreeNode.LateralMargin, subtreeSize); + newTreeNode.SubtreeSize = subtreeSize; + + return newTreeNode; + } + + /// + /// Given SubtreeSize for each node, positions the nodes, in a left-to-right or top-to-bottom fashion. + /// + /// + /// + /// + private void calculateNodePosRecursive(TreeNode node, double lateralStart, double mainStart) + { + // 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)); + + // 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.MainSize + node.MainMargin; + foreach (TreeNode child in node.Childs) + { + calculateNodePosRecursive(child, childLateral, childsMainFixed); + childLateral += child.SubtreeSize; + } + } + } +} 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 new file mode 100644 index 0000000000..4be7d73e92 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNode.cs @@ -0,0 +1,89 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; +using Debugger.AddIn.Visualizers.Graph.Drawing; + +namespace Debugger.AddIn.Visualizers.Graph.Layout +{ + /// + /// 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. + /// + public abstract class TreeNode : PositionedNode + { + public static TreeNode Create(LayoutDirection direction, NodeControl nodeVisualControl) + { + switch (direction) { + case LayoutDirection.TopBottom: return new TreeNodeTB(nodeVisualControl); + case LayoutDirection.LeftRight: return new TreeNodeLR(nodeVisualControl); + default: throw new DebuggerVisualizerException("Unsupported layout direction: " + direction.ToString()); + } + } + + public double HorizontalMargin { get; set; } + public double VerticalMargin { get; set; } + + protected TreeNode(NodeControl nodeVisualControl) : base(nodeVisualControl) + { + } + + /// + /// Width or height of the subtree. + /// + 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 abstract double MainMargin { get; } + public abstract double LateralMargin { get; } + + private List childs = new List(); + public List ChildEdges + { + get + { + return childs; + } + } + + public IEnumerable Childs + { + get + { + foreach (TreeEdge childEdge in ChildEdges) + yield return (TreeNode)childEdge.TargetNode; + } + } + + private List additionalNeighbors = new List(); + public List AdditionalNeighbors + { + get + { + return additionalNeighbors; + } + } + + public override IEnumerable Edges + { + get + { + foreach (TreeEdge childEdge in ChildEdges) + yield return childEdge; + foreach (TreeEdge neighborEdge in AdditionalNeighbors) + yield return neighborEdge; + } + } + } +} 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 new file mode 100644 index 0000000000..50efa075a1 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeLR.cs @@ -0,0 +1,30 @@ +// +// +// +// +// $Revision$ +// +using System; +using Debugger.AddIn.Visualizers.Graph.Drawing; + +namespace Debugger.AddIn.Visualizers.Graph.Layout +{ + /// + /// Description of TreeNodeLR. + /// + public class TreeNodeLR: TreeNode + { + public TreeNodeLR(NodeControl nodeControl) : base(nodeControl) + { + } + + 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; } } + } +} 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 new file mode 100644 index 0000000000..5c5a323ed3 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeTB.cs @@ -0,0 +1,30 @@ +// +// +// +// +// $Revision$ +// +using System; +using Debugger.AddIn.Visualizers.Graph.Drawing; + +namespace Debugger.AddIn.Visualizers.Graph.Layout +{ + /// + /// Description of TreeNodeTB. + /// + public class TreeNodeTB : TreeNode + { + public TreeNodeTB(NodeControl nodeControl) : base(nodeControl) + { + } + + 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; } } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/NamedEdge.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/NamedEdge.cs new file mode 100644 index 0000000000..8df02d517c --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/NamedEdge.cs @@ -0,0 +1,38 @@ +// +// +// +// +// $Revision$ +// +using System; + +namespace Debugger.AddIn.Visualizers.Graph +{ + /// + /// Named edge connecting 2 objects of same type. + /// + public class NamedEdge : NamedEdge + { + } + + /// + /// Named edge connecting 2 objects of arbitrary types. + /// + public class NamedEdge + { + /// + /// Name of the edge. + /// + public string Name { get; set; } + + /// + /// Target node of the edge. + /// + public TTarget TargetNode { get; set; } + + /// + /// Source node of the edge. + /// + public TSource SourceNode { get; set; } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectEdge.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectEdge.cs new file mode 100644 index 0000000000..2c23e7a38e --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectEdge.cs @@ -0,0 +1,19 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; +using System.Text; + +namespace Debugger.AddIn.Visualizers.Graph +{ + /// + /// Named edge in the . + /// + public class ObjectEdge : NamedEdge + { + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraph.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraph.cs new file mode 100644 index 0000000000..2a500f853d --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraph.cs @@ -0,0 +1,58 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; +using System.Text; + +namespace Debugger.AddIn.Visualizers.Graph +{ + /// + /// Object graph built by + /// + public class ObjectGraph + { + /// + /// Root of the graph. + /// + public ObjectNode Root { get; internal set; } + + /// + /// Adds node to the graph. + /// + /// node to be added + internal void AddNode(ObjectNode node) + { + _nodes.Add(node); + } + + private List _nodes = new List(); + /// + /// All nodes in the graph. + /// + public IEnumerable Nodes + { + get { return _nodes; } + } + + /// + /// All edges in the graph. + /// + public IEnumerable Edges + { + get + { + foreach (ObjectNode node in this.Nodes) + { + foreach (ObjectEdge edge in node.Edges) + { + yield return edge; + } + } + } + } + } +} 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 new file mode 100644 index 0000000000..28563667d6 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphBuilder.cs @@ -0,0 +1,259 @@ +// +// +// +// +// $Revision$ +// +using System; +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; + +namespace Debugger.AddIn.Visualizers.Graph +{ + // The object graph building starts with given expression and recursively + // explores all its members. + // + // Important part of the algorithm is finding if we already have a node + // for given value - to detect loops and shared references correctly. + // This is done using the following algorithm: + // + // getNodeForValue(value) + // get the hashCode for the value + // find if there is already a node with this hashCode (in O(1)) + // if not, we can be sure we have not seen this value yet + // if yes, it might be different object with the same hashCode -> compare addresses + // + // 'different object with the same hashCode' are possible - my question on stackoverflow: + // http://stackoverflow.com/questions/750947/-net-unique-object-identifier + // + // this way, the whole graph building is O(n) in the size of the resulting graph + // It tries to call as few Expression.Evaluate as possible, since Expression.Evaluate is expensive (5ms) - + // this is a prototype and the speed can be I believe greatly improved for future versions. + + /// + /// Builds for given string expression. + /// + public class ObjectGraphBuilder + { + /// + /// The underlying debugger service used for getting expression values. + /// + private WindowsDebugger _debuggerService; + /// + /// The resulting object graph. + /// + private ObjectGraph _resultGraph; + + /// + /// Given hash code, lookup already existing node(s) with this hash code. + /// + private Lookup objectNodesForHashCode = new Lookup(); + + /// + /// Binding flags for getting member expressions. + /// + private readonly Debugger.MetaData.BindingFlags _bindingFlags = + BindingFlags.Public | BindingFlags.Instance | BindingFlags.Field | BindingFlags.GetProperty; + + /// + /// Creates ObjectGraphBuilder. + /// + /// Debugger service. + public ObjectGraphBuilder(WindowsDebugger debuggerService) + { + _debuggerService = debuggerService; + } + + /// + /// Builds full object graph for given string expression. + /// + /// Expression valid in the program being debugged (eg. variable name) + /// Object graph + public ObjectGraph BuildGraphForExpression(string expression) + { + if (string.IsNullOrEmpty(expression)) + { + throw new DebuggerVisualizerException("Expression cannot be empty."); + } + + _resultGraph = new ObjectGraph(); + + // empty graph for null expression + if (!_debuggerService.GetValueFromName(expression).IsNull) + { + _resultGraph.Root = buildGraphRecursive(_debuggerService.GetValueFromName(expression).GetPermanentReference()); + } + + return _resultGraph; + } + + /// + /// Builds the subgraph representing given value. + /// + /// The Value for which the subgraph will be built. + /// ObjectNode representing the value + all recursive members. + private ObjectNode buildGraphRecursive(Value rootValue) + { + ObjectNode thisNode = createNewNode(rootValue); + + /*string[] memberValues = rootValue.GetMemberValuesAsString(_bindingFlags); + foreach (string memberValue in memberValues) + { + //Value memberValuePerm = memberValue.GetPermanentReference(); + + }*/ + + + foreach(Expression memberExpr in rootValue.Expression.AppendObjectMembers(rootValue.Type, _bindingFlags)) + { + checkIsOfSupportedType(memberExpr); + + string memberName = memberExpr.CodeTail; + if (isOfAtomicType(memberExpr)) + { + // atomic members are added to the list of node's "properties" + string memberValueAsString = memberExpr.Evaluate(_debuggerService.DebuggedProcess).AsString; + thisNode.AddProperty(memberName, memberValueAsString); + } + else + { + if (!isNull(memberExpr)) + { + // for object members, edges are added + + Value memberValue = getPermanentReference(memberExpr); + + // if node for memberValue already exists, only add edge to it (so loops etc. are solved correctly) + ObjectNode targetNode = getNodeForValue(memberValue); + if (targetNode == null) + { + // if no node for memberValue exists, build the subgraph for the value + targetNode = buildGraphRecursive(memberValue); + } + // add the edge + thisNode.AddNamedEdge(targetNode, memberName); + } + } + } + + return thisNode; + } + + /// + /// Creates new node for the value. + /// + /// Value, has to be valid. + /// New empty object node representing the value. + private ObjectNode createNewNode(Value permanentReference) + { + ObjectNode newNode = new ObjectNode(); + + _resultGraph.AddNode(newNode); + // remember this node's hashcode for quick lookup + objectNodesForHashCode.Add(invokeGetHashCode(permanentReference), newNode); + + // permanent reference to the object this node represents is useful for graph building, + // and matching nodes in animations + newNode.PermanentReference = permanentReference; + + return newNode; + } + + /// + /// Finds node that represents the same instance as given value. + /// + /// Valid value representing an instance. + /// + private ObjectNode getNodeForValue(Value value) + { + string objectHashCode = invokeGetHashCode(value); + // are there any nodes with the same hash code? + LookupValueCollection nodesWithSameHashCode = objectNodesForHashCode[objectHashCode]; + if (nodesWithSameHashCode == null) + { + return null; + } + else + { + // 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); + ObjectNode nodeWithSameAddress = nodesWithSameHashCode.Find( + node => { return objectAddress == getObjectValue(node.PermanentReference); } ); + return nodeWithSameAddress; + } + } + + /// + /// Invokes GetHashCode on given value. + /// + /// Valid value. + /// Hash code of the object in the debugee. + private string invokeGetHashCode(Value value) + { + MethodInfo method = value.Type.GetMember("GetHashCode", BindingFlags.Method | BindingFlags.IncludeSuperType) as MethodInfo; + string hashCode = value.InvokeMethod(method, null).AsString; + return hashCode; + } + + /// + /// Checks whether given expression's type is supported by the graph builder. + /// + /// Expression to be checked. + private void checkIsOfSupportedType(Expression expr) + { + DebugType typeOfValue = expr.Evaluate(_debuggerService.DebuggedProcess).Type; + if (typeOfValue.IsArray) + { + // arrays will be supported of course in the final version + throw new DebuggerVisualizerException("Arrays are not supported yet"); + } + } + + /// + /// Checks whether given expression's type is atomic - atomic values will be added to node's property list. + /// + /// Expression. + /// True if expression's type is atomic, False otherwise. + private bool isOfAtomicType(Expression expr) + { + DebugType typeOfValue = expr.Evaluate(_debuggerService.DebuggedProcess).Type; + return (!typeOfValue.IsClass) || typeOfValue.IsString; + } + + #region helpers + + private Value getPermanentReference(Expression expr) + { + return expr.Evaluate(_debuggerService.DebuggedProcess).GetPermanentReference(); + } + + private bool isNull(Expression expr) + { + return expr.Evaluate(_debuggerService.DebuggedProcess).IsNull; + } + + private DebugType getType(Expression expr) + { + return expr.Evaluate(_debuggerService.DebuggedProcess).Type; + } + + private ulong getObjectAddress(Expression expr) + { + return getObjectValue(expr.Evaluate(_debuggerService.DebuggedProcess)); + } + + private ulong getObjectValue(Value val) + { + ICorDebugReferenceValue refVal = val.CorValue.CastTo(); + return refVal.Value; + } + + #endregion + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectNode.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectNode.cs new file mode 100644 index 0000000000..886598322a --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectNode.cs @@ -0,0 +1,57 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; +using System.Text; + +namespace Debugger.AddIn.Visualizers.Graph +{ + /// + /// Node in the . + /// + public class ObjectNode + { + /// + /// Additional info useful for internal algorithms, not to be visible to the user. + /// + internal Debugger.Value PermanentReference { get; set; } + + private List _edges = new List(); + /// + /// Outgoing edges. + /// + public IEnumerable Edges + { + get { return _edges; } + } + + /// + /// Adds outgoing edge. + /// + internal void AddNamedEdge(ObjectNode targetNode, string edgeName) + { + _edges.Add(new ObjectEdge { Name = edgeName, SourceNode = this, TargetNode = targetNode }); + } + + private ObjectPropertyCollection _properties = new ObjectPropertyCollection(); + /// + /// Properties representing atomic string values as part of the node. + /// + public ObjectPropertyCollection Properties + { + get { return _properties; } + } + + /// + /// Adds string property. + /// + internal void AddProperty(string name, string value) + { + _properties.Add(new ObjectProperty { Name = name, Value = value }); + } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectProperty.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectProperty.cs new file mode 100644 index 0000000000..233bd263af --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectProperty.cs @@ -0,0 +1,27 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; +using System.Text; + +namespace Debugger.AddIn.Visualizers.Graph +{ + /// + /// Primitive property (int, string, etc.) of an object, in string form. + /// + public class ObjectProperty + { + /// + /// e.g. "Age" + /// + public string Name { get; set; } + /// + /// e.g. "19" + /// + public string Value { get; set; } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectPropertyCollection.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectPropertyCollection.cs new file mode 100644 index 0000000000..5113193ebf --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectPropertyCollection.cs @@ -0,0 +1,19 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; +using System.Text; + +namespace Debugger.AddIn.Visualizers.Graph +{ + /// + /// Collection of object primitive properties. + /// + public class ObjectPropertyCollection : List + { + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraphVisualizerViewContent.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraphVisualizerViewContent.cs new file mode 100644 index 0000000000..6a805f10e7 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraphVisualizerViewContent.cs @@ -0,0 +1,39 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Text; +using System.Windows.Forms; +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Gui; + +namespace Debugger.AddIn.Visualizers.Graph +{ + /// + /// ViewContent of the visualizer. + /// + public class ObjectGraphVisualizerViewContent : AbstractViewContent + { + VisualizerWinFormsControl control = new VisualizerWinFormsControl(); + + /// + /// The representing the view. + /// + public override object Control + { + get + { + return control; + } + } + + public ObjectGraphVisualizerViewContent() + { + this.TitleName = "Object graph visualizer"; + } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ShowObjectGraphVisualizerCommand.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ShowObjectGraphVisualizerCommand.cs new file mode 100644 index 0000000000..81394c3116 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ShowObjectGraphVisualizerCommand.cs @@ -0,0 +1,31 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Text; +using System.Windows.Forms; +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Gui; + +namespace Debugger.AddIn.Visualizers.Graph +{ + /// + /// Command in the tools menu for showing the object graph visualizer. + /// + public class ShowObjectGraphVisualizerCommand : AbstractMenuCommand + { + public override void Run() + { + VisualizerWPFWindow window = new VisualizerWPFWindow(); + window.Topmost = true; + // fix non-editable TextBox bug + //System.Windows.Forms.Integration.ElementHost.EnableModelessKeyboardInterop(window); + window.Show(); + //WorkbenchSingleton.Workbench.ShowView(new DebuggerVisualizerViewContent()); + } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Utils/Lookup.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Utils/Lookup.cs new file mode 100644 index 0000000000..eff669edc9 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Utils/Lookup.cs @@ -0,0 +1,48 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; + +namespace Debugger.AddIn.Visualizers.Graph.Utils +{ + /// + /// Same like Dictionary, but can store multiple values for one key. + /// + public class Lookup + { + private Dictionary> _dictionary; + + public Lookup() + { + _dictionary = new Dictionary>(); + } + + public LookupValueCollection this[TKey key] + { + get + { + LookupValueCollection values = null; + if (_dictionary.TryGetValue(key, out values)) + { + return values; + } + return null; + } + } + + public void Add(TKey key, TValue value) + { + LookupValueCollection values = null;; + if (!_dictionary.TryGetValue(key, out values)) + { + values = new LookupValueCollection(); + _dictionary.Add(key, values); + } + values.Add(value); + } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Utils/LookupValueCollection.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Utils/LookupValueCollection.cs new file mode 100644 index 0000000000..4f63814918 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Utils/LookupValueCollection.cs @@ -0,0 +1,18 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; + +namespace Debugger.AddIn.Visualizers.Graph.Utils +{ + /// + /// A collection of values for one key in the Lookup class. + /// + public class LookupValueCollection : List + { + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFControl.xaml b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFControl.xaml new file mode 100644 index 0000000000..8446aed80f --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFControl.xaml @@ -0,0 +1,12 @@ + + + + Expression: + + + + + + \ No newline at end of file diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFControl.xaml.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFControl.xaml.cs new file mode 100644 index 0000000000..f6b8aa7273 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFControl.xaml.cs @@ -0,0 +1,33 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + + +namespace Debugger.AddIn.Visualizers.Graph +{ + /// + /// Interaction logic for VisualizerWPFControl.xaml + /// + public partial class VisualizerWPFControl : UserControl + { + public VisualizerWPFControl() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml new file mode 100644 index 0000000000..def412b38d --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml @@ -0,0 +1,17 @@ + + + + + Expression: + + + + + + + + \ No newline at end of file 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 new file mode 100644 index 0000000000..182f4185f2 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml.cs @@ -0,0 +1,97 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + +using ICSharpCode.SharpDevelop.Debugging; +using ICSharpCode.SharpDevelop.Services; + +namespace Debugger.AddIn.Visualizers.Graph +{ + /// + /// Interaction logic for VisualizerWPFWindow.xaml + /// + public partial class VisualizerWPFWindow : Window + { + WindowsDebugger _debuggerService = null; + + public VisualizerWPFWindow() + { + InitializeComponent(); + + _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); + } + + public void debuggerService_IsProcessRunningChanged(object sender, EventArgs e) + { + // on step or breakpoint hit + if (!_debuggerService.IsProcessRunning) + { + refreshGraph(); + } + } + + public void _debuggerService_DebugStopped(object sender, EventArgs e) + { + _debuggerService.IsProcessRunningChanged -= new EventHandler(debuggerService_IsProcessRunningChanged); + this.Close(); + } + + private void Inspect_Button_Click(object sender, RoutedEventArgs e) + { + refreshGraph(); + } + + void refreshGraph() + { + ObjectGraphBuilder graphBuilder = new ObjectGraphBuilder(_debuggerService); + ObjectGraph graph = null; + + try + { + graph = graphBuilder.BuildGraphForExpression(txtExpression.Text); + } + catch(DebuggerVisualizerException ex) + { + guiHandleException(ex); + return; + } + catch(Debugger.GetValueException ex) + { + guiHandleException(ex); + return; + } + + Layout.TreeLayouter layouter = new Layout.TreeLayouter(); + Layout.PositionedGraph posGraph = layouter.CalculateLayout(graph, Layout.LayoutDirection.LeftRight); + + GraphDrawer.Draw(posGraph, canvas); + + //GraphDrawer drawer = new GraphDrawer(graph); + //drawer.Draw(canvas); + } + + void guiHandleException(System.Exception ex) + { + MessageBox.Show(ex.Message, "Exception", MessageBoxButton.OK, MessageBoxImage.Error); + } + } +} \ No newline at end of file diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWinFormsControl.Designer.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWinFormsControl.Designer.cs new file mode 100644 index 0000000000..929ca9b041 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWinFormsControl.Designer.cs @@ -0,0 +1,92 @@ +namespace Debugger.AddIn.Visualizers.Graph +{ + partial class VisualizerWinFormsControl + { + /// + /// Designer variable used to keep track of non-visual components. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Disposes resources used by the control. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing) { + if (components != null) { + components.Dispose(); + } + } + base.Dispose(disposing); + } + + /// + /// This method is required for Windows Forms designer support. + /// Do not change the method contents inside the source code editor. The Forms designer might + /// not be able to load this method if it was changed manually. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.txtExpression = new System.Windows.Forms.TextBox(); + this.btnInspect = new System.Windows.Forms.Button(); + this.lblInfo = new System.Windows.Forms.Label(); + this.lblExpression = new System.Windows.Forms.Label(); + this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); + this.SuspendLayout(); + // + // txtExpression + // + this.txtExpression.Location = new System.Drawing.Point(88, 13); + this.txtExpression.Name = "txtExpression"; + this.txtExpression.Size = new System.Drawing.Size(131, 20); + this.txtExpression.TabIndex = 0; + this.toolTip1.SetToolTip(this.txtExpression, "Expression (e.g. variable name) to be inspected"); + // + // btnInspect + // + this.btnInspect.Location = new System.Drawing.Point(225, 10); + this.btnInspect.Name = "btnInspect"; + this.btnInspect.Size = new System.Drawing.Size(75, 23); + this.btnInspect.TabIndex = 1; + this.btnInspect.Text = "Inspect"; + this.btnInspect.UseVisualStyleBackColor = true; + this.btnInspect.Click += new System.EventHandler(this.BtnInspectClick); + // + // lblInfo + // + this.lblInfo.Location = new System.Drawing.Point(11, 58); + this.lblInfo.Name = "lblInfo"; + this.lblInfo.Size = new System.Drawing.Size(349, 23); + this.lblInfo.TabIndex = 2; + this.lblInfo.Text = "< Result >"; + // + // lblExpression + // + this.lblExpression.Location = new System.Drawing.Point(11, 14); + this.lblExpression.Name = "lblExpression"; + this.lblExpression.Size = new System.Drawing.Size(71, 23); + this.lblExpression.TabIndex = 4; + this.lblExpression.Text = "Expression:"; + // + // VisualizerWinFormsControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.lblExpression); + this.Controls.Add(this.lblInfo); + this.Controls.Add(this.btnInspect); + this.Controls.Add(this.txtExpression); + this.Name = "VisualizerWinFormsControl"; + this.Size = new System.Drawing.Size(525, 426); + this.ResumeLayout(false); + this.PerformLayout(); + } + private System.Windows.Forms.Button btnInspect; + private System.Windows.Forms.ToolTip toolTip1; + private System.Windows.Forms.Label lblExpression; + private System.Windows.Forms.Label lblInfo; + private System.Windows.Forms.TextBox txtExpression; + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWinFormsControl.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWinFormsControl.cs new file mode 100644 index 0000000000..87537b95d3 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWinFormsControl.cs @@ -0,0 +1,84 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.ComponentModel; +using System.Drawing; +using System.Windows.Forms; +using System.Linq; + +using ICSharpCode.SharpDevelop.Debugging; +using ICSharpCode.SharpDevelop.Services; + +namespace Debugger.AddIn.Visualizers.Graph +{ + /// + /// Windows forms control making up the object graph visualizer user interface/ + /// + public partial class VisualizerWinFormsControl : UserControl + { + WindowsDebugger _debuggerService = null; + + public VisualizerWinFormsControl() + { + // + // The InitializeComponent() call is required for Windows Forms designer support. + // + InitializeComponent(); + + _debuggerService = DebuggerService.CurrentDebugger as WindowsDebugger; + if (_debuggerService == null) + throw new ApplicationException("Only windows debugger is currently supported"); + + _debuggerService.IsProcessRunningChanged += new EventHandler(debuggerService_IsProcessRunningChanged); + } + + public void debuggerService_IsProcessRunningChanged(object sender, EventArgs e) + { + // on step, breakpoint hit + if (!_debuggerService.IsProcessRunning) + { + refreshGraph(); + } + } + + void BtnInspectClick(object sender, EventArgs e) + { + refreshGraph(); + } + + void refreshGraph() + { + ObjectGraphBuilder graphBuilder = new ObjectGraphBuilder(_debuggerService); + ObjectGraph graph = null; + + try + { + graph = graphBuilder.BuildGraphForExpression(txtExpression.Text); + } + catch(DebuggerVisualizerException ex) + { + guiHandleException(ex); + return; + } + catch(Debugger.GetValueException ex) + { + guiHandleException(ex); + + return; + } + + // just a simple message for checking the graph is build ok, will be replaced by graph drawing of course + lblInfo.Text = string.Format("Done. Number of graph nodes: {0}, number of edges: {1}", graph.Nodes.Count(), graph.Edges.Count()); + } + + void guiHandleException(System.Exception ex) + { + lblInfo.Text = "< Result >"; + MessageBox.Show(ex.Message, "Exception", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/WinFormsControl.resx b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/WinFormsControl.resx new file mode 100644 index 0000000000..7080a7d118 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/WinFormsControl.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file