Browse Source

Initial import of object graph visualizer.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4049 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Martin Koníček 17 years ago
parent
commit
74f79c9732
  1. 9
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.addin
  2. 45
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj
  3. 31
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/DebuggerVisualizerException.cs
  4. 43
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/GraphDrawer.cs
  5. 25
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/NodeControl.xaml
  6. 78
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/NodeControl.xaml.cs
  7. 17
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedEdge.cs
  8. 44
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedGraph.cs
  9. 54
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedNode.cs
  10. 19
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/LayoutDirection.cs
  11. 17
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeEdge.cs
  12. 122
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeLayouter.cs
  13. 89
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNode.cs
  14. 30
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeLR.cs
  15. 30
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeTB.cs
  16. 38
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/NamedEdge.cs
  17. 19
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectEdge.cs
  18. 58
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraph.cs
  19. 259
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphBuilder.cs
  20. 57
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectNode.cs
  21. 27
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectProperty.cs
  22. 19
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectPropertyCollection.cs
  23. 39
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraphVisualizerViewContent.cs
  24. 31
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ShowObjectGraphVisualizerCommand.cs
  25. 48
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Utils/Lookup.cs
  26. 18
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Utils/LookupValueCollection.cs
  27. 12
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFControl.xaml
  28. 33
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFControl.xaml.cs
  29. 17
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml
  30. 97
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml.cs
  31. 92
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWinFormsControl.Designer.cs
  32. 84
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWinFormsControl.cs
  33. 120
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/WinFormsControl.resx

9
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.addin

@ -149,4 +149,13 @@
class = "ICSharpCode.SharpDevelop.Gui.OptionPanels.DebuggingSymbolsPanel"/> class = "ICSharpCode.SharpDevelop.Gui.OptionPanels.DebuggingSymbolsPanel"/>
</OptionPanel> </OptionPanel>
</Path> </Path>
<Path name = "/Workspace/Tools">
<Condition name="SolutionOpen" action="disable">
<MenuItem id = "DebuggerVisualizerCommandShow"
label = "Show object graph visualizer"
class = "Debugger.AddIn.Visualizers.Graph.ShowObjectGraphVisualizerCommand"/>
</Condition>
</Path>
</AddIn> </AddIn>

45
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj

@ -19,7 +19,7 @@
<FileAlignment>4096</FileAlignment> <FileAlignment>4096</FileAlignment>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors> <TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<RunPostBuildEvent>Always</RunPostBuildEvent> <RunPostBuildEvent>Always</RunPostBuildEvent>
<TargetFrameworkVersion>v3.0</TargetFrameworkVersion> <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<SourceAnalysisOverrideSettingsFile>"C:\Program Files\SharpDevelop\3.0\bin\..\AddIns\AddIns\Misc\SourceAnalysis\Settings.SourceAnalysis"</SourceAnalysisOverrideSettingsFile> <SourceAnalysisOverrideSettingsFile>"C:\Program Files\SharpDevelop\3.0\bin\..\AddIns\AddIns\Misc\SourceAnalysis\Settings.SourceAnalysis"</SourceAnalysisOverrideSettingsFile>
<SourceAnalysisOverrideSettingsFile>C:\Dokumente und Einstellungen\HP\Anwendungsdaten\ICSharpCode/SharpDevelop3.0\Settings.SourceAnalysis</SourceAnalysisOverrideSettingsFile> <SourceAnalysisOverrideSettingsFile>C:\Dokumente und Einstellungen\HP\Anwendungsdaten\ICSharpCode/SharpDevelop3.0\Settings.SourceAnalysis</SourceAnalysisOverrideSettingsFile>
<PublishUrl>http://localhost/Debugger.AddIn/</PublishUrl> <PublishUrl>http://localhost/Debugger.AddIn/</PublishUrl>
@ -62,7 +62,13 @@
<Reference Include="PresentationFramework"> <Reference Include="PresentationFramework">
<RequiredTargetFramework>3.0</RequiredTargetFramework> <RequiredTargetFramework>3.0</RequiredTargetFramework>
</Reference> </Reference>
<Reference Include="PresentationFramework.Aero">
<RequiredTargetFramework>3.0</RequiredTargetFramework>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Drawing" /> <Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" /> <Reference Include="System.Windows.Forms" />
@ -140,6 +146,7 @@
<EmbeddedResource Include="Src\Service\EditBreakpointScriptForm.resx"> <EmbeddedResource Include="Src\Service\EditBreakpointScriptForm.resx">
<DependentUpon>EditBreakpointScriptForm.cs</DependentUpon> <DependentUpon>EditBreakpointScriptForm.cs</DependentUpon>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Src\Visualizers\Graph\WinFormsControl.resx" />
<Compile Include="Src\Service\SetCurrentStatementCommand.cs" /> <Compile Include="Src\Service\SetCurrentStatementCommand.cs" />
<Compile Include="..\..\..\..\..\Main\GlobalAssemblyInfo.cs"> <Compile Include="..\..\..\..\..\Main\GlobalAssemblyInfo.cs">
<Link>Configuration\GlobalAssemblyInfo.cs</Link> <Link>Configuration\GlobalAssemblyInfo.cs</Link>
@ -160,6 +167,33 @@
<Compile Include="Src\TreeModel\ChildNodesOfObject.cs" /> <Compile Include="Src\TreeModel\ChildNodesOfObject.cs" />
<Compile Include="Src\TreeModel\StackFrameNode.cs" /> <Compile Include="Src\TreeModel\StackFrameNode.cs" />
<Compile Include="Src\TreeModel\Utils.cs" /> <Compile Include="Src\TreeModel\Utils.cs" />
<Compile Include="Src\Visualizers\Graph\DebuggerVisualizerException.cs" />
<Compile Include="Src\Visualizers\Graph\Drawing\GraphDrawer.cs" />
<Compile Include="Src\Visualizers\Graph\Drawing\NodeControl.xaml.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\PositionedEdge.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\PositionedGraph.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\PositionedNode.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\LayoutDirection.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\TreeEdge.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\TreeLayouter.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\TreeNode.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\TreeNodeLR.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\TreeNodeTB.cs" />
<Compile Include="Src\Visualizers\Graph\ObjectGraphVisualizerViewContent.cs" />
<Compile Include="Src\Visualizers\Graph\ObjectGraph\NamedEdge.cs" />
<Compile Include="Src\Visualizers\Graph\ObjectGraph\ObjectEdge.cs" />
<Compile Include="Src\Visualizers\Graph\ObjectGraph\ObjectGraph.cs" />
<Compile Include="Src\Visualizers\Graph\ObjectGraph\ObjectGraphBuilder.cs" />
<Compile Include="Src\Visualizers\Graph\ObjectGraph\ObjectNode.cs" />
<Compile Include="Src\Visualizers\Graph\ObjectGraph\ObjectProperty.cs" />
<Compile Include="Src\Visualizers\Graph\ObjectGraph\ObjectPropertyCollection.cs" />
<Compile Include="Src\Visualizers\Graph\ShowObjectGraphVisualizerCommand.cs" />
<Compile Include="Src\Visualizers\Graph\Utils\Lookup.cs" />
<Compile Include="Src\Visualizers\Graph\Utils\LookupValueCollection.cs" />
<Compile Include="Src\Visualizers\Graph\VisualizerWinFormsControl.cs" />
<Compile Include="Src\Visualizers\Graph\VisualizerWinFormsControl.Designer.cs" />
<Compile Include="Src\Visualizers\Graph\VisualizerWPFControl.xaml.cs" />
<Compile Include="Src\Visualizers\Graph\VisualizerWPFWindow.xaml.cs" />
<Compile Include="Src\Visualizers\ICorDebug.cs" /> <Compile Include="Src\Visualizers\ICorDebug.cs" />
<Compile Include="Src\Visualizers\IList.cs" /> <Compile Include="Src\Visualizers\IList.cs" />
<None Include="COPYING" /> <None Include="COPYING" />
@ -223,6 +257,15 @@
<Name>ICSharpCode.Core.WinForms</Name> <Name>ICSharpCode.Core.WinForms</Name>
<Private>False</Private> <Private>False</Private>
</ProjectReference> </ProjectReference>
<Folder Include="Src\Visualizers\Graph" />
<Folder Include="Src\Visualizers\Graph\Drawing" />
<Folder Include="Src\Visualizers\Graph\Layout" />
<Folder Include="Src\Visualizers\Graph\Layout\Tree" />
<Folder Include="Src\Visualizers\Graph\ObjectGraph" />
<Folder Include="Src\Visualizers\Graph\Utils" />
<Page Include="Src\Visualizers\Graph\Drawing\NodeControl.xaml" />
<Page Include="Src\Visualizers\Graph\VisualizerWPFControl.xaml" />
<Page Include="Src\Visualizers\Graph\VisualizerWPFWindow.xaml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5"> <BootstrapperPackage Include="Microsoft.Net.Client.3.5">

31
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/DebuggerVisualizerException.cs

@ -0,0 +1,31 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníèek" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
namespace Debugger.AddIn.Visualizers.Graph
{
/// <summary>
/// Description of DebuggerVisualizerException.
/// </summary>
public class DebuggerVisualizerException : ApplicationException
{
public DebuggerVisualizerException()
: base()
{
}
public DebuggerVisualizerException(string message)
: base(message)
{
}
public DebuggerVisualizerException(string message, System.Exception innerException)
: base(message, innerException)
{
}
}
}

43
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/GraphDrawer.cs

@ -0,0 +1,43 @@
using System;
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
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
{
/// <summary>
/// Draws ObjectGraph on a Canvas.
/// </summary>
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);
}
}
}
}

25
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/NodeControl.xaml

@ -0,0 +1,25 @@
<UserControl x:Class="Debugger.AddIn.Visualizers.Graph.Drawing.NodeControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dropShadow="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero">
<dropShadow:SystemDropShadowChrome>
<Border Name="border1" BorderBrush="Black" BorderThickness="1" Padding="04,04,04,04"
Canvas.Left="20" Canvas.Top="20">
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#ddeeff" Offset="0.0" />
<GradientStop Color="White" Offset="0.4" />
<GradientStop Color="White" Offset="0.8" />
</LinearGradientBrush>
</Border.Background>
<Grid Name="propertyGrid">
<Grid.RowDefinitions>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
</Grid>
</Border>
</dropShadow:SystemDropShadowChrome>
</UserControl>

78
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/NodeControl.xaml.cs

@ -0,0 +1,78 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
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
{
/// <summary>
/// UserControl used to display ObjectNode.
/// </summary>
public partial class NodeControl : UserControl
{
public NodeControl()
{
InitializeComponent();
}
private ObjectNode node;
/// <summary>
/// ObjectNode that this control displays.
/// </summary>
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++;
}
}
}
/// <summary>
/// Creates TextBlock with given text.
/// </summary>
private TextBlock createTextBlock(string text)
{
TextBlock newTextblock = new TextBlock();
newTextblock.Text = text;
newTextblock.Padding = new Thickness(4);
return newTextblock;
}
}
}

17
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedEdge.cs

@ -0,0 +1,17 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// Edge with position information.
/// </summary>
public class PositionedEdge : NamedEdge<PositionedNode>
{
}
}

44
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedGraph.cs

@ -0,0 +1,44 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// Graph with positioned nodes and edges.
/// </summary>
public class PositionedGraph
{
internal List<PositionedNode> nodes = new List<PositionedNode>();
/// <summary>
/// All nodes in the graph.
/// </summary>
public IEnumerable<PositionedNode> Nodes
{
get { return nodes; }
}
/// <summary>
/// All edges in the graph.
/// </summary>
public IEnumerable<PositionedEdge> Edges
{
get
{
foreach (PositionedNode node in this.Nodes)
{
foreach (PositionedEdge edge in node.Edges)
{
yield return edge;
}
}
}
}
}
}

54
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedNode.cs

@ -0,0 +1,54 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using Debugger.AddIn.Visualizers.Graph.Drawing;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// Node with position information.
/// </summary>
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;
/// <summary>
/// Visual control to be shown for this node.
/// </summary>
public NodeControl NodeVisualControl
{
get
{
return this.nodeVisualControl;
}
}
public virtual IEnumerable<PositionedEdge> Edges
{
get
{
return new PositionedEdge[]{};
}
}
}
}

19
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/LayoutDirection.cs

@ -0,0 +1,19 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// Direction of tree layout.
/// </summary>
public enum LayoutDirection
{
TopBottom,
LeftRight
}
}

17
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeEdge.cs

@ -0,0 +1,17 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// Description of TreeEdge.
/// </summary>
public class TreeEdge : PositionedEdge
{
}
}

122
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeLayouter.cs

@ -0,0 +1,122 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníèek" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
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 = 20;
private static readonly double vertNodeMargin = 20;
private LayoutDirection layoutDirection = LayoutDirection.TopBottom;
PositionedGraph resultGraph = null;
Dictionary<ObjectNode, TreeNode> treeNodeFor = new Dictionary<ObjectNode, TreeNode>();
Dictionary<ObjectNode, object> seenNodes = new Dictionary<ObjectNode, object>();
public TreeLayouter()
{
}
private TreeNode createTreeNode(NodeControl nodeVisualControl)
{
return TreeNode.Create(this.layoutDirection, nodeVisualControl);
}
/// <summary>
/// Calculates layout for given <see cref="ObjectGraph" />.
/// </summary>
/// <param name="objectGraph"></param>
/// <returns></returns>
public PositionedGraph CalculateLayout(ObjectGraph objectGraph, LayoutDirection direction)
{
resultGraph = new PositionedGraph();
layoutDirection = direction;
var seenNodes = new Dictionary<ObjectNode, object>();
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;
}
/// <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(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;
}
}
}
}

89
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNode.cs

@ -0,0 +1,89 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using Debugger.AddIn.Visualizers.Graph.Drawing;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// 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 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)
{
}
/// <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 abstract double MainMargin { get; }
public abstract double LateralMargin { get; }
private List<TreeEdge> childs = new List<TreeEdge>();
public List<TreeEdge> ChildEdges
{
get
{
return childs;
}
}
public IEnumerable<TreeNode> Childs
{
get
{
foreach (TreeEdge childEdge in ChildEdges)
yield return (TreeNode)childEdge.TargetNode;
}
}
private List<TreeEdge> additionalNeighbors = new List<TreeEdge>();
public List<TreeEdge> AdditionalNeighbors
{
get
{
return additionalNeighbors;
}
}
public override IEnumerable<PositionedEdge> Edges
{
get
{
foreach (TreeEdge childEdge in ChildEdges)
yield return childEdge;
foreach (TreeEdge neighborEdge in AdditionalNeighbors)
yield return neighborEdge;
}
}
}
}

30
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeLR.cs

@ -0,0 +1,30 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using Debugger.AddIn.Visualizers.Graph.Drawing;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// Description of TreeNodeLR.
/// </summary>
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; } }
}
}

30
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeTB.cs

@ -0,0 +1,30 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using Debugger.AddIn.Visualizers.Graph.Drawing;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// Description of TreeNodeTB.
/// </summary>
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; } }
}
}

38
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/NamedEdge.cs

@ -0,0 +1,38 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
namespace Debugger.AddIn.Visualizers.Graph
{
/// <summary>
/// Named edge connecting 2 objects of same type.
/// </summary>
public class NamedEdge<TSourceTarget> : NamedEdge<TSourceTarget, TSourceTarget>
{
}
/// <summary>
/// Named edge connecting 2 objects of arbitrary types.
/// </summary>
public class NamedEdge<TSource, TTarget>
{
/// <summary>
/// Name of the edge.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Target node of the edge.
/// </summary>
public TTarget TargetNode { get; set; }
/// <summary>
/// Source node of the edge.
/// </summary>
public TSource SourceNode { get; set; }
}
}

19
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectEdge.cs

@ -0,0 +1,19 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Text;
namespace Debugger.AddIn.Visualizers.Graph
{
/// <summary>
/// Named edge in the <see cref="ObjectGraph" />.
/// </summary>
public class ObjectEdge : NamedEdge<ObjectNode>
{
}
}

58
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraph.cs

@ -0,0 +1,58 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Text;
namespace Debugger.AddIn.Visualizers.Graph
{
/// <summary>
/// Object graph built by <see cref="ObjectGraphBuilder"/>
/// </summary>
public class ObjectGraph
{
/// <summary>
/// Root of the graph.
/// </summary>
public ObjectNode Root { get; internal set; }
/// <summary>
/// Adds node to the graph.
/// </summary>
/// <param name="node">node to be added</param>
internal void AddNode(ObjectNode node)
{
_nodes.Add(node);
}
private List<ObjectNode> _nodes = new List<ObjectNode>();
/// <summary>
/// All nodes in the graph.
/// </summary>
public IEnumerable<ObjectNode> Nodes
{
get { return _nodes; }
}
/// <summary>
/// All edges in the graph.
/// </summary>
public IEnumerable<ObjectEdge> Edges
{
get
{
foreach (ObjectNode node in this.Nodes)
{
foreach (ObjectEdge edge in node.Edges)
{
yield return edge;
}
}
}
}
}
}

259
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphBuilder.cs

@ -0,0 +1,259 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníèek" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
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.
/// <summary>
/// Builds <see cref="ObjectGraph" /> for given string expression.
/// </summary>
public class ObjectGraphBuilder
{
/// <summary>
/// The underlying debugger service used for getting expression values.
/// </summary>
private WindowsDebugger _debuggerService;
/// <summary>
/// The resulting object graph.
/// </summary>
private ObjectGraph _resultGraph;
/// <summary>
/// Given hash code, lookup already existing node(s) with this hash code.
/// </summary>
private Lookup<string, ObjectNode> objectNodesForHashCode = new Lookup<string, ObjectNode>();
/// <summary>
/// Binding flags for getting member expressions.
/// </summary>
private readonly Debugger.MetaData.BindingFlags _bindingFlags =
BindingFlags.Public | BindingFlags.Instance | BindingFlags.Field | BindingFlags.GetProperty;
/// <summary>
/// Creates ObjectGraphBuilder.
/// </summary>
/// <param name="debuggerService">Debugger service.</param>
public ObjectGraphBuilder(WindowsDebugger debuggerService)
{
_debuggerService = debuggerService;
}
/// <summary>
/// Builds full object graph for given string expression.
/// </summary>
/// <param name="expression">Expression valid in the program being debugged (eg. variable name)</param>
/// <returns>Object graph</returns>
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;
}
/// <summary>
/// Builds the subgraph representing given value.
/// </summary>
/// <param name="rootValue">The Value for which the subgraph will be built.</param>
/// <returns>ObjectNode representing the value + all recursive members.</returns>
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;
}
/// <summary>
/// Creates new node for the value.
/// </summary>
/// <param name="permanentReference">Value, has to be valid.</param>
/// <returns>New empty object node representing the value.</returns>
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;
}
/// <summary>
/// Finds node that represents the same instance as given value.
/// </summary>
/// <param name="value">Valid value representing an instance.</param>
/// <returns></returns>
private ObjectNode getNodeForValue(Value value)
{
string objectHashCode = invokeGetHashCode(value);
// are there any nodes with the same hash code?
LookupValueCollection<ObjectNode> 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;
}
}
/// <summary>
/// Invokes GetHashCode on given value.
/// </summary>
/// <param name="value">Valid value.</param>
/// <returns>Hash code of the object in the debugee.</returns>
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;
}
/// <summary>
/// Checks whether given expression's type is supported by the graph builder.
/// </summary>
/// <param name="expr">Expression to be checked.</param>
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");
}
}
/// <summary>
/// Checks whether given expression's type is atomic - atomic values will be added to node's property list.
/// </summary>
/// <param name="expr">Expression.</param>
/// <returns>True if expression's type is atomic, False otherwise.</returns>
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<ICorDebugReferenceValue>();
return refVal.Value;
}
#endregion
}
}

57
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectNode.cs

@ -0,0 +1,57 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Text;
namespace Debugger.AddIn.Visualizers.Graph
{
/// <summary>
/// Node in the <see cref="ObjectGraph" />.
/// </summary>
public class ObjectNode
{
/// <summary>
/// Additional info useful for internal algorithms, not to be visible to the user.
/// </summary>
internal Debugger.Value PermanentReference { get; set; }
private List<ObjectEdge> _edges = new List<ObjectEdge>();
/// <summary>
/// Outgoing edges.
/// </summary>
public IEnumerable<ObjectEdge> Edges
{
get { return _edges; }
}
/// <summary>
/// Adds outgoing edge.
/// </summary>
internal void AddNamedEdge(ObjectNode targetNode, string edgeName)
{
_edges.Add(new ObjectEdge { Name = edgeName, SourceNode = this, TargetNode = targetNode });
}
private ObjectPropertyCollection _properties = new ObjectPropertyCollection();
/// <summary>
/// Properties representing atomic string values as part of the node.
/// </summary>
public ObjectPropertyCollection Properties
{
get { return _properties; }
}
/// <summary>
/// Adds string property.
/// </summary>
internal void AddProperty(string name, string value)
{
_properties.Add(new ObjectProperty { Name = name, Value = value });
}
}
}

27
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectProperty.cs

@ -0,0 +1,27 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Text;
namespace Debugger.AddIn.Visualizers.Graph
{
/// <summary>
/// Primitive property (int, string, etc.) of an object, in string form.
/// </summary>
public class ObjectProperty
{
/// <summary>
/// e.g. "Age"
/// </summary>
public string Name { get; set; }
/// <summary>
/// e.g. "19"
/// </summary>
public string Value { get; set; }
}
}

19
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectPropertyCollection.cs

@ -0,0 +1,19 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Text;
namespace Debugger.AddIn.Visualizers.Graph
{
/// <summary>
/// Collection of object primitive properties.
/// </summary>
public class ObjectPropertyCollection : List<ObjectProperty>
{
}
}

39
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraphVisualizerViewContent.cs

@ -0,0 +1,39 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníèek" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Text;
using System.Windows.Forms;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Gui;
namespace Debugger.AddIn.Visualizers.Graph
{
/// <summary>
/// ViewContent of the visualizer.
/// </summary>
public class ObjectGraphVisualizerViewContent : AbstractViewContent
{
VisualizerWinFormsControl control = new VisualizerWinFormsControl();
/// <summary>
/// The <see cref="System.Windows.Forms.Control"/> representing the view.
/// </summary>
public override object Control
{
get
{
return control;
}
}
public ObjectGraphVisualizerViewContent()
{
this.TitleName = "Object graph visualizer";
}
}
}

31
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ShowObjectGraphVisualizerCommand.cs

@ -0,0 +1,31 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníèek" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Text;
using System.Windows.Forms;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Gui;
namespace Debugger.AddIn.Visualizers.Graph
{
/// <summary>
/// Command in the tools menu for showing the object graph visualizer.
/// </summary>
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());
}
}
}

48
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Utils/Lookup.cs

@ -0,0 +1,48 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníèek" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
namespace Debugger.AddIn.Visualizers.Graph.Utils
{
/// <summary>
/// Same like Dictionary, but can store multiple values for one key.
/// </summary>
public class Lookup<TKey, TValue>
{
private Dictionary<TKey, LookupValueCollection<TValue>> _dictionary;
public Lookup()
{
_dictionary = new Dictionary<TKey, LookupValueCollection<TValue>>();
}
public LookupValueCollection<TValue> this[TKey key]
{
get
{
LookupValueCollection<TValue> values = null;
if (_dictionary.TryGetValue(key, out values))
{
return values;
}
return null;
}
}
public void Add(TKey key, TValue value)
{
LookupValueCollection<TValue> values = null;;
if (!_dictionary.TryGetValue(key, out values))
{
values = new LookupValueCollection<TValue>();
_dictionary.Add(key, values);
}
values.Add(value);
}
}
}

18
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Utils/LookupValueCollection.cs

@ -0,0 +1,18 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníèek" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
namespace Debugger.AddIn.Visualizers.Graph.Utils
{
/// <summary>
/// A collection of values for one key in the Lookup class.
/// </summary>
public class LookupValueCollection<TValue> : List<TValue>
{
}
}

12
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFControl.xaml

@ -0,0 +1,12 @@
<UserControl x:Class="Debugger.AddIn.Visualizers.Graph.VisualizerWPFControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Height="300" Width="300">
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Padding="4">Expression:</TextBlock>
<TextBox VerticalAlignment="Center" Margin="4" Width="100"></TextBox>
<Button Padding="4" Margin="4">Inspect</Button>
</StackPanel>
<Canvas></Canvas>
</StackPanel>
</UserControl>

33
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFControl.xaml.cs

@ -0,0 +1,33 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
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
{
/// <summary>
/// Interaction logic for VisualizerWPFControl.xaml
/// </summary>
public partial class VisualizerWPFControl : UserControl
{
public VisualizerWPFControl()
{
InitializeComponent();
}
}
}

17
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml

@ -0,0 +1,17 @@
<Window x:Class="Debugger.AddIn.Visualizers.Graph.VisualizerWPFWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dv="clr-namespace:Debugger.AddIn.Visualizers.Graph"
Title="Object graph visualizer" Height="400" Width="600">
<StackPanel>
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Padding="4">Expression:</TextBlock>
<TextBox Name="txtExpression" VerticalAlignment="Center" Margin="4" Width="100"></TextBox>
<Button Padding="4" Margin="4" Click="Inspect_Button_Click">Inspect</Button>
</StackPanel>
<Canvas Name="canvas">
</Canvas>
</StackPanel>
</StackPanel>
</Window>

97
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml.cs

@ -0,0 +1,97 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
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
{
/// <summary>
/// Interaction logic for VisualizerWPFWindow.xaml
/// </summary>
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);
}
}
}

92
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWinFormsControl.Designer.cs generated

@ -0,0 +1,92 @@
namespace Debugger.AddIn.Visualizers.Graph
{
partial class VisualizerWinFormsControl
{
/// <summary>
/// Designer variable used to keep track of non-visual components.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Disposes resources used by the control.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing) {
if (components != null) {
components.Dispose();
}
}
base.Dispose(disposing);
}
/// <summary>
/// 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.
/// </summary>
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;
}
}

84
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWinFormsControl.cs

@ -0,0 +1,84 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníèek" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
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
{
/// <summary>
/// Windows forms control making up the object graph visualizer user interface/
/// </summary>
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);
}
}
}

120
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/WinFormsControl.resx

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
Loading…
Cancel
Save