Browse Source
git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4049 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61shortcuts
33 changed files with 1720 additions and 1 deletions
@ -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) |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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> |
||||||
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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> |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
||||||
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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[]{}; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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 |
||||||
|
} |
||||||
|
} |
||||||
@ -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 |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
||||||
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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; } } |
||||||
|
} |
||||||
|
} |
||||||
@ -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; } } |
||||||
|
} |
||||||
|
} |
||||||
@ -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; } |
||||||
|
} |
||||||
|
} |
||||||
@ -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> |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
||||||
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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
|
||||||
|
} |
||||||
|
} |
||||||
@ -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 }); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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; } |
||||||
|
} |
||||||
|
} |
||||||
@ -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> |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
||||||
@ -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"; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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());
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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> |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
||||||
@ -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> |
||||||
@ -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(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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> |
||||||
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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…
Reference in new issue