From ae9496cac211ac1477d42cd0f6ee93ba6dd1524a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kon=C3=AD=C4=8Dek?= Date: Mon, 17 Aug 2009 15:42:45 +0000 Subject: [PATCH] Object graph visualizer can be opened from debugger tooltips. Attemp to move Object graph visualizer to Pad - will stay in separate topmost Window until AbstractPadContent.IsVisible works properly. git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4708 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../Project/Debugger.AddIn.addin | 9 + .../Project/Debugger.AddIn.csproj | 6 + .../Project/Src/Pads/ObjectGraphPad.cs | 80 ++++++ .../Commands/ObjectGraphVisualizerCommand.cs | 11 +- .../ObjectGraphVisualizerMenuCommand.cs | 5 +- .../Visualizers/Graph/Drawing/GraphDrawer.cs | 50 +++- .../PositionedGraphNodeControl.xaml.cs | 2 +- .../Src/Visualizers/Graph/NodeControlCache.cs | 9 +- .../Graph/ObjectGraph/ObjectGraph.cs | 6 +- .../Graph/ObjectGraph/ObjectGraphBuilder.cs | 35 +-- .../Visualizers/Graph/ObjectGraphControl.xaml | 51 ++++ .../Graph/ObjectGraphControl.xaml.cs | 228 ++++++++++++++++++ .../Graph/VisualizerWPFWindow.xaml | 48 +--- .../Graph/VisualizerWPFWindow.xaml.cs | 191 +++------------ .../Tooltips/DebuggerTooltipControl.xaml.cs | 6 +- 15 files changed, 494 insertions(+), 243 deletions(-) create mode 100644 src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Pads/ObjectGraphPad.cs create mode 100644 src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraphControl.xaml create mode 100644 src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraphControl.xaml.cs diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.addin b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.addin index 2a6bfc4d30..1b50b15422 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.addin +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.addin @@ -123,6 +123,15 @@ shortcut = "Control|Alt|W" class = "ICSharpCode.SharpDevelop.Gui.Pads.WatchPad" defaultPosition = "Bottom, Hidden" /> + diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj index 37d3d27996..35055fca9e 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj @@ -93,6 +93,7 @@ DebuggingSymbolsPanel.cs + @@ -144,6 +145,10 @@ + + ObjectGraphControl.xaml + Code + @@ -323,6 +328,7 @@ + {3A9AE6AA-BC07-4A2F-972C-581E3AE2F195} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Pads/ObjectGraphPad.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Pads/ObjectGraphPad.cs new file mode 100644 index 0000000000..a14fad2a2c --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Pads/ObjectGraphPad.cs @@ -0,0 +1,80 @@ +// +// +// +// +// $Revision$ +// +using ICSharpCode.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using Debugger; +using Debugger.AddIn.Visualizers.Graph; + +namespace ICSharpCode.SharpDevelop.Gui.Pads +{ + /// + /// Description of ObjectGraphPad. + /// + public class ObjectGraphPad : DebuggerPad + { + Process debuggedProcess; + ObjectGraphControl objectGraphControl; + static ObjectGraphPad instance; + + public ObjectGraphPad() + { + instance = this; + } + + /// Always check if Instance is null, might be null if pad is not opened! + public static ObjectGraphPad Instance { + get { return instance; } + } + + protected override void InitializeComponents() + { + objectGraphControl = new ObjectGraphControl(); + } + + public override object Control { + get { + return objectGraphControl; + } + } + + public override void RefreshPad() + { + // BUG: if pad window is undocked and floats standalone, IsVisible == false (so pad won't refresh) + // REQUEST: need to refresh pad + if (!this.IsVisible) + { + LoggingService.Info("skipped refresh"); + return; + } + if (debuggedProcess == null || debuggedProcess.IsRunning || debuggedProcess.SelectedStackFrame == null) { + this.objectGraphControl.Clear(); + return; + } + this.objectGraphControl.Refresh(); + } + + protected override void SelectProcess(Process process) + { + if (debuggedProcess != null) { + debuggedProcess.Paused -= debuggedProcess_Paused; + } + debuggedProcess = process; + if (debuggedProcess != null) { + debuggedProcess.Paused += debuggedProcess_Paused; + } + RefreshPad(); + } + + void debuggedProcess_Paused(object sender, ProcessEventArgs e) + { + RefreshPad(); + } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Commands/ObjectGraphVisualizerCommand.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Commands/ObjectGraphVisualizerCommand.cs index 2c42a6b964..576846d8c3 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Commands/ObjectGraphVisualizerCommand.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Commands/ObjectGraphVisualizerCommand.cs @@ -4,10 +4,11 @@ // // $Revision$ // -using Debugger.AddIn.TreeModel; +using Debugger.AddIn.Visualizers.Graph; using System; using System.Collections.Generic; using System.Linq; +using Debugger.AddIn.TreeModel; namespace Debugger.AddIn.Visualizers { @@ -32,7 +33,13 @@ namespace Debugger.AddIn.Visualizers public override void Execute() { - + if (this.Node != null && this.Node.Expression != null) + { + var objectGraphWindow = VisualizerWPFWindow.EnsureShown(); + // is this ok with different languages than C#? + // - Prettyprint an expression and then call WindowsDebugger.GetValueFromName, which is C# only + objectGraphWindow.ShownExpression = this.Node.Expression.PrettyPrint(); + } } } } diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Commands/ObjectGraphVisualizerMenuCommand.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Commands/ObjectGraphVisualizerMenuCommand.cs index 3ce433a9a7..e0f2d5accd 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Commands/ObjectGraphVisualizerMenuCommand.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Commands/ObjectGraphVisualizerMenuCommand.cs @@ -20,10 +20,7 @@ namespace Debugger.AddIn.Visualizers { public override void Run() { - VisualizerWPFWindow window = new VisualizerWPFWindow(); - window.Topmost = true; - window.Show(); - //WorkbenchSingleton.Workbench.ShowView(new DebuggerVisualizerViewContent()); + VisualizerWPFWindow.EnsureShown(); } } } diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/GraphDrawer.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/GraphDrawer.cs index 698c5d271c..ac2bf26ad2 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/GraphDrawer.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/GraphDrawer.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; @@ -42,6 +43,20 @@ namespace Debugger.AddIn.Visualizers.Graph /// public void StartAnimation(PositionedGraph oldGraph, PositionedGraph newGraph, GraphDiff diff) { + if (oldGraph != null) + { + foreach (var oldNode in oldGraph.Nodes) + { + foreach (var newNode in newGraph.Nodes) + { + if (oldNode.NodeVisualControl == newNode.NodeVisualControl) + { + ClearCanvas(); + } + } + } + } + this.canvas.Width = newGraph.BoundingRect.Width; this.canvas.Height = newGraph.BoundingRect.Height; @@ -112,6 +127,19 @@ namespace Debugger.AddIn.Visualizers.Graph { canvas.Children.Clear(); + /*try + { + // why do the controls disappear? + var n1 = posGraph.Nodes.First().NodeVisualControl; + var n2 = posGraph.Nodes.Skip(1).First().NodeVisualControl; + var n3 = posGraph.Nodes.Skip(2).First().NodeVisualControl; + if (n1 == n2 || n1 == n3 || n2 == n3) + { + ClearCanvas(); + } + } + catch{}*/ + // draw nodes foreach (PositionedGraphNode node in posGraph.Nodes) { @@ -129,6 +157,14 @@ namespace Debugger.AddIn.Visualizers.Graph canvas.Children.Add(edgeTooltip); } + /// + /// Clears the drawing Canvas. + /// + public void ClearCanvas() + { + canvas.Children.Clear(); + } + private PositionedGraphNodeControl addNodeToCanvas(PositionedGraphNode node) { canvas.Children.Add(node.NodeVisualControl); @@ -160,21 +196,21 @@ namespace Debugger.AddIn.Visualizers.Graph pathInVisible.StrokeThickness = 16; pathInVisible.Data = geometryInVisible; - pathInVisible.MouseEnter += delegate(object sender, MouseEventArgs e) - { - pathVisible.StrokeThickness = 2; + pathInVisible.MouseEnter += delegate(object sender, MouseEventArgs e) + { + pathVisible.StrokeThickness = 2; this.edgeTooltip.Text = ((PositionedEdge)pathVisible.Tag).Name; Point mousePos = e.GetPosition(this.canvas); Canvas.SetLeft(this.edgeTooltip, mousePos.X - 5); Canvas.SetTop(this.edgeTooltip, mousePos.Y - 20); this.edgeTooltip.Visibility = Visibility.Visible; }; - pathInVisible.MouseLeave += delegate(object sender, MouseEventArgs e) - { - pathVisible.StrokeThickness = 1; + pathInVisible.MouseLeave += delegate(object sender, MouseEventArgs e) + { + pathVisible.StrokeThickness = 1; this.edgeTooltip.Visibility = Visibility.Hidden; }; - pathInVisible.MouseMove += delegate(object sender, MouseEventArgs e) + pathInVisible.MouseMove += delegate(object sender, MouseEventArgs e) { Point mousePos = e.GetPosition(this.canvas); Canvas.SetLeft(this.edgeTooltip, mousePos.X - 5); diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/PositionedGraphNodeControl.xaml.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/PositionedGraphNodeControl.xaml.cs index 14914aaa9e..70d0426bd9 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/PositionedGraphNodeControl.xaml.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/PositionedGraphNodeControl.xaml.cs @@ -124,7 +124,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Drawing PropertyCollapsed = null; ContentNodeExpanded = null; ContentNodeCollapsed = null; - //this.listView.ItemsSource = null; + this.listView.ItemsSource = null; } void TextBlock_MouseDown(object sender, MouseButtonEventArgs e) diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/NodeControlCache.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/NodeControlCache.cs index 311e500c1a..583d7feef5 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/NodeControlCache.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/NodeControlCache.cs @@ -33,12 +33,13 @@ namespace Debugger.AddIn.Visualizers.Graph public PositionedGraphNodeControl GetNodeControl() { + controlsReturned++; return new PositionedGraphNodeControl(); - var control = controls.Count == 0 ? new PositionedGraphNodeControl() : controls.Pop(); + // bugs in drawing + /*var control = controls.Count == 0 ? new PositionedGraphNodeControl() : controls.Pop(); control.Init(); - //control.InvalidateVisual(); - controlsReturned++; - return control; + control.InvalidateVisual(); + return control;*/ } public void Clear() diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraph.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraph.cs index cc90ae330e..afe2f18391 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraph.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraph.cs @@ -11,12 +11,12 @@ using System.Text; namespace Debugger.AddIn.Visualizers.Graph { /// - /// Object graph built by + /// Object graph built by . The graph is never empty. /// public class ObjectGraph { /// - /// Root of the graph. + /// Root of the graph. Should never be null. /// public ObjectGraphNode Root { get; internal set; } @@ -31,7 +31,7 @@ namespace Debugger.AddIn.Visualizers.Graph private List _nodes = new List(); /// - /// All nodes in the graph. + /// All nodes in the graph. Should always contain at least one node. /// public IEnumerable Nodes { diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphBuilder.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphBuilder.cs index 9f82ed69ac..e398376240 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphBuilder.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphBuilder.cs @@ -80,27 +80,30 @@ namespace Debugger.AddIn.Visualizers.Graph /// Object graph public ObjectGraph BuildGraphForExpression(string expression, ExpandedExpressions expandedNodes) { - if (string.IsNullOrEmpty(expression)) - { - throw new DebuggerVisualizerException("Expression is empty."); + if (string.IsNullOrEmpty(expression)) { + throw new DebuggerVisualizerException("Please specify an expression."); } - - resultGraph = new ObjectGraph(); - Value rootValue = debuggerService.GetValueFromName(expression); - if (rootValue == null) - { + + var debuggedProcess = this.debuggerService.DebuggedProcess; + if (debuggedProcess == null || debuggedProcess.IsRunning || debuggedProcess.SelectedStackFrame == null) { throw new DebuggerVisualizerException("Please use the visualizer when debugging."); } - // empty graph for null expression - if (!rootValue.IsNull) - { - //resultGraph.Root = buildGraphRecursive(debuggerService.GetValueFromName(expression).GetPermanentReference(), expandedNodes); - resultGraph.Root = createNewNode(debuggerService.GetValueFromName(expression).GetPermanentReference()); - loadContent(resultGraph.Root); - loadNeighborsRecursive(resultGraph.Root, expandedNodes); + // TODO GetValueFromName calls evaluator with SupportedLanguage.CSharp, is that ok? + Value rootValue = debuggerService.GetValueFromName(expression); + if (rootValue.IsNull) { + throw new DebuggerVisualizerException(expression + " is null."); } - + return buildGraphForValue(rootValue, expandedNodes); + } + + private ObjectGraph buildGraphForValue(Value rootValue, ExpandedExpressions expandedNodes) + { + resultGraph = new ObjectGraph(); + //resultGraph.Root = buildGraphRecursive(debuggerService.GetValueFromName(expression).GetPermanentReference(), expandedNodes); + resultGraph.Root = createNewNode(rootValue.GetPermanentReference()); + loadContent(resultGraph.Root); + loadNeighborsRecursive(resultGraph.Root, expandedNodes); return resultGraph; } diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraphControl.xaml b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraphControl.xaml new file mode 100644 index 0000000000..c1f0ab9326 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraphControl.xaml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + Expression: + + + Layout: + + + + Error: + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraphControl.xaml.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraphControl.xaml.cs new file mode 100644 index 0000000000..c28b308f18 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraphControl.xaml.cs @@ -0,0 +1,228 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +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 Debugger.AddIn.Visualizers.Graph.Layout; +using ICSharpCode.SharpDevelop.Debugging; +using ICSharpCode.SharpDevelop.Services; + +namespace Debugger.AddIn.Visualizers.Graph +{ + /// + /// Interaction logic for ObjectGraphControl.xaml + /// + public partial class ObjectGraphControl : UserControl + { + private WindowsDebugger debuggerService; + private EnumViewModel layoutViewModel; + private ObjectGraph objectGraph; + private ObjectGraphBuilder objectGraphBuilder; + + private PositionedGraph oldPosGraph; + private PositionedGraph currentPosGraph; + private GraphDrawer graphDrawer; + private Layout.TreeLayouter layouter; + + /// Long-lived map telling which graph nodes and content nodes the user expanded. + private Expanded expanded = new Expanded(); + + public ObjectGraphControl() + { + InitializeComponent(); + + debuggerService = DebuggerService.CurrentDebugger as WindowsDebugger; + if (debuggerService == null) + throw new ApplicationException("Only windows debugger is currently supported"); + + this.layoutViewModel = new EnumViewModel(); + this.layoutViewModel.PropertyChanged += new PropertyChangedEventHandler(layoutViewModel_PropertyChanged); + this.cmbLayoutDirection.DataContext = this.layoutViewModel; + + this.layouter = new TreeLayouter(); + this.graphDrawer = new GraphDrawer(this.canvas); + } + + public void Clear() + { + txtExpression.Text = string.Empty; + } + + public void Refresh() + { + refreshGraph(); + } + + public string ShownExpression + { + get { + return this.txtExpression.Text; + } + set { + if (value != this.txtExpression.Text) { + this.txtExpression.Text = value; + } + } + } + + /// + /// ObjectGraph visualizer caches UI controls, this clears the cache. + /// + public void ClearUIControlCache() + { + NodeControlCache.Instance.Clear(); + } + + private void Inspect_Button_Click(object sender, RoutedEventArgs e) + { + this.Refresh(); + } + + void refreshGraph() + { + clearErrorMessage(); + if (string.IsNullOrEmpty(txtExpression.Text)) + { + this.graphDrawer.ClearCanvas(); + return; + } + if (debuggerService.IsProcessRunning) // TODO "Process not paused" exception still occurs + { + showErrorMessage("Cannot inspect when the process is running."); + return; + } + bool graphBuiltOk = true; + try + { + this.objectGraph = rebuildGraph(txtExpression.Text); + } + catch(DebuggerVisualizerException ex) + { + graphBuiltOk = false; + showErrorMessage(ex.Message); + } + catch(Debugger.GetValueException ex) + { + graphBuiltOk = false; + showErrorMessage("Cannot evaluate: " + ex.Message); + } + if (graphBuiltOk) + { + layoutGraph(this.objectGraph); + } + else + { + this.graphDrawer.ClearCanvas(); + } + } + + ObjectGraph rebuildGraph(string expression) + { + this.objectGraphBuilder = new ObjectGraphBuilder(debuggerService); + ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Building graph for expression: " + txtExpression.Text); + return this.objectGraphBuilder.BuildGraphForExpression(expression, this.expanded.Expressions); + } + + void layoutGraph(ObjectGraph graph) + { + if (this.oldPosGraph != null) + { + foreach (var oldNode in this.oldPosGraph.Nodes) + { + // controls from old graph would be garbage collected, reuse them + NodeControlCache.Instance.ReturnForReuse(oldNode.NodeVisualControl); + } + } + this.oldPosGraph = this.currentPosGraph; + ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Calculating graph layout"); + this.currentPosGraph = this.layouter.CalculateLayout(graph, layoutViewModel.SelectedEnumValue, this.expanded); + ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Graph layout done"); + registerExpandCollapseEvents(this.currentPosGraph); + + var graphDiff = new GraphMatcher().MatchGraphs(oldPosGraph, currentPosGraph); + ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: starting graph animation"); + this.graphDrawer.StartAnimation(oldPosGraph, currentPosGraph, graphDiff); + //this.graphDrawer.Draw(this.currentPosGraph); // buggy layout with NodeControlCache + } + + void layoutViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == "SelectedEnumValue") // TODO special event for enum value change + { + if (this.objectGraph != null) + { + layoutGraph(this.objectGraph); + } + } + } + + void clearErrorMessage() + { + this.pnlError.Visibility = Visibility.Collapsed; + } + + void showErrorMessage(string message) + { + this.txtError.Text = message; + this.pnlError.Visibility = Visibility.Visible; + //MessageBox.Show(ex.Message, "Exception", MessageBoxButton.OK, MessageBoxImage.Error); + } + + void registerExpandCollapseEvents(PositionedGraph posGraph) + { + foreach (var node in posGraph.Nodes) + { + node.PropertyExpanded += new EventHandler(node_PropertyExpanded); + node.PropertyCollapsed += new EventHandler(node_PropertyCollapsed); + node.ContentNodeExpanded += new EventHandler(node_ContentNodeExpanded); + node.ContentNodeCollapsed += new EventHandler(node_ContentNodeCollapsed); + } + } + + void node_ContentNodeExpanded(object sender, ContentNodeEventArgs e) + { + expanded.ContentNodes.SetExpanded(e.Node); + layoutGraph(this.objectGraph); + } + + void node_ContentNodeCollapsed(object sender, ContentNodeEventArgs e) + { + expanded.ContentNodes.SetCollapsed(e.Node); + layoutGraph(this.objectGraph); + } + + void node_PropertyExpanded(object sender, PositionedPropertyEventArgs e) + { + // remember this property is expanded (for later graph rebuilds) + expanded.Expressions.SetExpanded(e.Property.Expression); + + // add edge (+ possibly nodes) to underlying object graph (no need to fully rebuild) + // TODO can add more nodes if they are expanded - now this adds always one node + e.Property.ObjectGraphProperty.TargetNode = this.objectGraphBuilder.ObtainNodeForExpression(e.Property.Expression); + layoutGraph(this.objectGraph); + } + + void node_PropertyCollapsed(object sender, PositionedPropertyEventArgs e) + { + // remember this property is collapsed (for later graph rebuilds) + expanded.Expressions.SetCollapsed(e.Property.Expression); + + // just remove edge from underlying object graph (no need to fully rebuild) + e.Property.ObjectGraphProperty.TargetNode = null; + layoutGraph(this.objectGraph); + } + } +} \ No newline at end of file diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml index 8e6b67745b..bfd3dc947a 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml @@ -4,50 +4,6 @@ xmlns:dv="clr-namespace:Debugger.AddIn.Visualizers.Graph" xmlns:controls="clr-namespace:Debugger.AddIn.Visualizers.Controls" Title="Object graph visualizer" Height="400" Width="600"> - - - - - - - - - - - - - - - - Expression: - - - Layout: - - - - Error: - - - - - - - - - - - - - - - - - - + + \ No newline at end of file diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml.cs index ce7517fdd9..55b7aeeb6c 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml.cs @@ -29,20 +29,7 @@ namespace Debugger.AddIn.Visualizers.Graph public partial class VisualizerWPFWindow : Window { private WindowsDebugger debuggerService; - private EnumViewModel layoutViewModel; - private ObjectGraph objectGraph; - private ObjectGraphBuilder objectGraphBuilder; - private PositionedGraph oldPosGraph; - private PositionedGraph currentPosGraph; - private GraphDrawer graphDrawer; - private Layout.TreeLayouter layouter; - - /// - /// Long-lived map telling which graph nodes and content nodes the user expanded. - /// - private Expanded expanded = new Expanded(); - public VisualizerWPFWindow() { InitializeComponent(); @@ -51,181 +38,69 @@ namespace Debugger.AddIn.Visualizers.Graph if (debuggerService == null) throw new ApplicationException("Only windows debugger is currently supported"); - this.Closed += new EventHandler(VisualizerWPFWindow_Closed); - registerEvents(); - - this.layoutViewModel = new EnumViewModel(); - this.layoutViewModel.PropertyChanged += new PropertyChangedEventHandler(layoutViewModel_PropertyChanged); - this.DataContext = this.layoutViewModel; - - this.layouter = new TreeLayouter(); - this.graphDrawer = new GraphDrawer(this.canvas); - } - - void VisualizerWPFWindow_Closed(object sender, EventArgs e) - { - unregisterEvents(); - NodeControlCache.Instance.Clear(); + instance = this; } - private void registerEvents() + protected override void OnClosed(EventArgs e) { - debuggerService.IsProcessRunningChanged += new EventHandler(debuggerService_IsProcessRunningChanged); - debuggerService.DebugStopped += new EventHandler(_debuggerService_DebugStopped); - } - - private void unregisterEvents() - { - 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(); - } + base.OnClosed(e); + unregisterEvents(); + this.objectGraphControl.ClearUIControlCache(); + instance = null; } - public void _debuggerService_DebugStopped(object sender, EventArgs e) + static VisualizerWPFWindow instance; + public static VisualizerWPFWindow Instance { - this.Close(); + get { return instance; } } - private void Inspect_Button_Click(object sender, RoutedEventArgs e) + public string ShownExpression { - clearErrorMessage(); - if (debuggerService.IsProcessRunning) // TODO "Process not paused" exception still occurs - { - showErrorMessage("Cannot inspect when the process is running."); + get { + return this.objectGraphControl.ShownExpression; } - else - { - refreshGraph(); - } - } - - void layoutViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == "SelectedEnumValue") // TODO special event for enum value change - { - if (this.objectGraph != null) - { - layoutGraph(this.objectGraph); + set { + if (value != this.objectGraphControl.ShownExpression) { + this.objectGraphControl.ShownExpression = value; + this.objectGraphControl.Refresh(); } } } - void refreshGraph() - { - bool graphBuiltOk = true; - clearErrorMessage(); - try - { - this.objectGraph = rebuildGraph(); - } - catch(DebuggerVisualizerException ex) - { - graphBuiltOk = false; - showErrorMessage(ex.Message); - } - catch(Debugger.GetValueException ex) - { - graphBuiltOk = false; - showErrorMessage(ex.Message); - } - if (graphBuiltOk) - { - layoutGraph(this.objectGraph); - } - } - - ObjectGraph rebuildGraph() + public static VisualizerWPFWindow EnsureShown() { - this.objectGraphBuilder = new ObjectGraphBuilder(debuggerService); - ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Building graph for expression: " + txtExpression.Text); - return this.objectGraphBuilder.BuildGraphForExpression(txtExpression.Text, this.expanded.Expressions); + var window = VisualizerWPFWindow.Instance ?? new VisualizerWPFWindow(); + window.Topmost = true; + window.Show(); + return window; } - void layoutGraph(ObjectGraph graph) - { - if (this.oldPosGraph != null) - { - foreach (var oldNode in this.oldPosGraph.Nodes) - { - // controls from old graph would be garbage collected, reuse them - NodeControlCache.Instance.ReturnForReuse(oldNode.NodeVisualControl); - } - } - this.oldPosGraph = this.currentPosGraph; - ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Calculating graph layout"); - this.currentPosGraph = this.layouter.CalculateLayout(graph, layoutViewModel.SelectedEnumValue, this.expanded); - ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Graph layout done"); - registerExpandCollapseEvents(this.currentPosGraph); - - var graphDiff = new GraphMatcher().MatchGraphs(oldPosGraph, currentPosGraph); - ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: starting graph animation"); - this.graphDrawer.StartAnimation(oldPosGraph, currentPosGraph, graphDiff); - //this.graphDrawer.Draw(this.currentPosGraph); - } - - void clearErrorMessage() + private void registerEvents() { - this.pnlError.Visibility = Visibility.Collapsed; + debuggerService.IsProcessRunningChanged += new EventHandler(debuggerService_IsProcessRunningChanged); + debuggerService.DebugStopped += new EventHandler(debuggerService_DebugStopped); } - void showErrorMessage(string message) + private void unregisterEvents() { - this.txtError.Text = message; - this.pnlError.Visibility = Visibility.Visible; - //MessageBox.Show(ex.Message, "Exception", MessageBoxButton.OK, MessageBoxImage.Error); + debuggerService.IsProcessRunningChanged -= new EventHandler(debuggerService_IsProcessRunningChanged); + debuggerService.DebugStopped -= new EventHandler(debuggerService_DebugStopped); } - void registerExpandCollapseEvents(PositionedGraph posGraph) + public void debuggerService_IsProcessRunningChanged(object sender, EventArgs e) { - foreach (var node in posGraph.Nodes) + // on step or breakpoint hit + if (!debuggerService.IsProcessRunning) { - node.PropertyExpanded += new EventHandler(node_PropertyExpanded); - node.PropertyCollapsed += new EventHandler(node_PropertyCollapsed); - node.ContentNodeExpanded += new EventHandler(node_ContentNodeExpanded); - node.ContentNodeCollapsed += new EventHandler(node_ContentNodeCollapsed); + this.objectGraphControl.Refresh(); } } - void node_ContentNodeExpanded(object sender, ContentNodeEventArgs e) + public void debuggerService_DebugStopped(object sender, EventArgs e) { - expanded.ContentNodes.SetExpanded(e.Node); - layoutGraph(this.objectGraph); - } - - void node_ContentNodeCollapsed(object sender, ContentNodeEventArgs e) - { - expanded.ContentNodes.SetCollapsed(e.Node); - layoutGraph(this.objectGraph); - } - - void node_PropertyExpanded(object sender, PositionedPropertyEventArgs e) - { - // remember this property is expanded (for later graph rebuilds) - expanded.Expressions.SetExpanded(e.Property.Expression); - - // add edge (+ possibly nodes) to underlying object graph (no need to fully rebuild) - // TODO can add more nodes if they are expanded - now this adds always one node - e.Property.ObjectGraphProperty.TargetNode = this.objectGraphBuilder.ObtainNodeForExpression(e.Property.Expression); - layoutGraph(this.objectGraph); - } - - void node_PropertyCollapsed(object sender, PositionedPropertyEventArgs e) - { - // remember this property is collapsed (for later graph rebuilds) - expanded.Expressions.SetCollapsed(e.Property.Expression); - - // just remove edge from underlying object graph (no need to fully rebuild) - e.Property.ObjectGraphProperty.TargetNode = null; - layoutGraph(this.objectGraph); + this.Close(); } } } \ No newline at end of file diff --git a/src/Main/Base/Project/Src/Services/Debugger/Tooltips/DebuggerTooltipControl.xaml.cs b/src/Main/Base/Project/Src/Services/Debugger/Tooltips/DebuggerTooltipControl.xaml.cs index b8854ebdf5..74e0d9f3cd 100644 --- a/src/Main/Base/Project/Src/Services/Debugger/Tooltips/DebuggerTooltipControl.xaml.cs +++ b/src/Main/Base/Project/Src/Services/Debugger/Tooltips/DebuggerTooltipControl.xaml.cs @@ -25,6 +25,8 @@ namespace ICSharpCode.SharpDevelop.Debugging { private readonly double ChildPopupOpenXOffet = 16; private readonly double ChildPopupOpenYOffet = 15; + private readonly int InitialItemsCount = 12; + private readonly int VisibleItemsCount = 11; public DebuggerTooltipControl() { @@ -65,7 +67,7 @@ namespace ICSharpCode.SharpDevelop.Debugging set { this.itemsSource = value; - this.lazyGrid = new LazyItemsControl(this.dataGrid, 12); + this.lazyGrid = new LazyItemsControl(this.dataGrid, InitialItemsCount); lazyGrid.ItemsSource = new VirtualizingIEnumerable(value); this.dataGrid.AddHandler(ScrollViewer.ScrollChangedEvent, new ScrollChangedEventHandler(handleScroll)); @@ -73,7 +75,7 @@ namespace ICSharpCode.SharpDevelop.Debugging { // hide up/down buttons if too few items btnUp.Visibility = btnDown.Visibility = - this.lazyGrid.ItemsSourceTotalCount.Value <= 10 ? Visibility.Collapsed : Visibility.Visible; + this.lazyGrid.ItemsSourceTotalCount.Value <= VisibleItemsCount ? Visibility.Collapsed : Visibility.Visible; } } get