diff --git a/data/resources/StringResources.resx b/data/resources/StringResources.resx
index c8145d0c77..408fa69e4a 100644
--- a/data/resources/StringResources.resx
+++ b/data/resources/StringResources.resx
@@ -5950,7 +5950,13 @@ Shows the full callstack of the error.
Object Graph
- Parallel Stack
+ Parallel Stacks
+
+
+ Toggle Method View
+
+
+ Show/Hide Zoom Control
Run to cursor
diff --git a/data/resources/image/BitmapResources/BitmapResources-data/Icons.48x48.CurrentFrame.png b/data/resources/image/BitmapResources/BitmapResources-data/Icons.48x48.CurrentFrame.png
new file mode 100644
index 0000000000..db6c9772b9
Binary files /dev/null and b/data/resources/image/BitmapResources/BitmapResources-data/Icons.48x48.CurrentFrame.png differ
diff --git a/data/resources/image/BitmapResources/BitmapResources.res b/data/resources/image/BitmapResources/BitmapResources.res
index 6b1df909cd..1cb487ede6 100644
--- a/data/resources/image/BitmapResources/BitmapResources.res
+++ b/data/resources/image/BitmapResources/BitmapResources.res
@@ -37,6 +37,10 @@ ProjectBrowser.WebReferenceFolder.Closed = ProjectBrowserIcons\WebReferenceFolde
ProjectBrowser.WebReferenceFolder.Open = ProjectBrowserIcons\WebReferenceFolder.Open.png
ProjectBrowser.WebReference = ProjectBrowserIcons\WebReference.png
+#Parallel Stacks pad
+ParallelStacks.MethodView = PadIcons\MethodView.png
+ParallelStacks.ZoomControl = PadIcons\ZoomControl.png
+
#Output pad
OutputPad.Toolbar.ClearOutputWindow = OutputPadIcons\ClearOutputWindow.png
@@ -226,6 +230,7 @@ Icons.16x16.OpenFileIcon = BitmapResources-data\Icons.1
Icons.16x16.HtmlElements.FieldSetElement = BitmapResources-data\Icons.16x16.HtmlElements.FieldSetElement.png
Icons.16x16.SplitWindow = BitmapResources-data\Icons.16x16.SplitWindow.png
Icons.16x16.DeleteHistory = BitmapResources-data\Icons.16x16.DeleteHistory.png
+Icons.48x48.CurrentFrame = BitmapResources-data\Icons.48x48.CurrentFrame.png
Icons.16x16.Refresh = BitmapResources-data\Icons.16x16.Refresh.png
Icons.Magnifier = BitmapResources-data\magnifier.png
@@ -249,6 +254,7 @@ PadIcons.LocalVariables = PadIcons\LocalVariables.png
PadIcons.Threads = PadIcons\Threads.png
PadIcons.Exceptions = PadIcons\Exceptions.png
PadIcons.XPathQuery = PadIcons\XPathQuery.png
+PadIcons.Parallel = PadIcons\Parallel.png
#SharpQuery icons
Icons.16x16.SharpQuery.DataBaseRoot = SharpQueryIcons\Icons.16x16.SharpQuery.Database.png
diff --git a/data/resources/image/BitmapResources/PadIcons/MethodView.png b/data/resources/image/BitmapResources/PadIcons/MethodView.png
new file mode 100644
index 0000000000..a830304afa
Binary files /dev/null and b/data/resources/image/BitmapResources/PadIcons/MethodView.png differ
diff --git a/data/resources/image/BitmapResources/PadIcons/Parallel.png b/data/resources/image/BitmapResources/PadIcons/Parallel.png
new file mode 100644
index 0000000000..eeac4da65a
Binary files /dev/null and b/data/resources/image/BitmapResources/PadIcons/Parallel.png differ
diff --git a/data/resources/image/BitmapResources/PadIcons/ZoomControl.png b/data/resources/image/BitmapResources/PadIcons/ZoomControl.png
new file mode 100644
index 0000000000..5d73c69855
Binary files /dev/null and b/data/resources/image/BitmapResources/PadIcons/ZoomControl.png differ
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.addin b/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.addin
index 89209b2151..210f63b594 100644
--- a/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.addin
+++ b/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.addin
@@ -130,6 +130,13 @@
icon = "PadIcons.LocalVariables"
class = "ICSharpCode.SharpDevelop.Gui.Pads.ObjectGraphPad"
defaultPosition = "Bottom, Hidden" />
+
+
@@ -166,6 +173,24 @@
type="ComboBox" id="SelectLanguageCommand" />
+
+
+
+
+
+ />
+
+
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj b/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj
index a84df33228..67d344518c 100644
--- a/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj
+++ b/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj
@@ -58,6 +58,15 @@
..\..\..\..\bin\SharpDevelop.exe
+
+ ..\..\..\Libraries\GraphSharp\GraphSharp.dll
+
+
+ ..\..\..\Libraries\GraphSharp\GraphSharp.Controls.dll
+
+
+ ..\..\..\Libraries\GraphSharp\Microsoft.Contracts.dll
+
3.0
@@ -68,6 +77,9 @@
3.0
+
+ ..\..\..\Libraries\GraphSharp\QuickGraph.dll
+
3.5
@@ -89,6 +101,9 @@
CallStackPad.xaml
Code
+
+
+
ConditionCell.xaml
@@ -101,6 +116,17 @@
WatchList.xaml
+
+ DrawSurface.xaml
+ Code
+
+
+
+
+
+ ThreadStack.xaml
+ Code
+
WatchInputBox.xaml
Code
@@ -125,8 +151,6 @@
DebuggingSymbolsPanel.cs
-
-
UserControl
@@ -367,6 +391,8 @@
+
+
@@ -394,7 +420,9 @@
ICSharpCode.Core.WinForms
False
+
+
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/CallStackPad.xaml.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/CallStackPad.xaml.cs
index 6da43185b1..39e0e41589 100644
--- a/src/AddIns/Debugger/Debugger.AddIn/Pads/CallStackPad.xaml.cs
+++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/CallStackPad.xaml.cs
@@ -216,7 +216,6 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
bool showModuleNames = DebuggingOptions.Instance.ShowModuleNames;
StringBuilder name = new StringBuilder();
-
name.Append(frame.MethodInfo.DeclaringType.FullName);
name.Append('.');
name.Append(frame.MethodInfo.Name);
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/ParallelStacksViewCommands.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/ParallelStacksViewCommands.cs
new file mode 100644
index 0000000000..84192784bf
--- /dev/null
+++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/ParallelStacksViewCommands.cs
@@ -0,0 +1,92 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows.Controls;
+using ICSharpCode.Core;
+
+namespace ICSharpCode.SharpDevelop.Gui.Pads
+{
+ public sealed class ShowZoomControlCommand : AbstractCheckableMenuCommand
+ {
+ ParallelStackPad pad;
+
+ public override object Owner {
+ get { return base.Owner; }
+ set {
+ if (!(value is ParallelStackPad))
+ throw new Exception("Owner has to be a ParallelStackPad");
+ pad = value as ParallelStackPad;
+ base.Owner = value;
+ }
+ }
+
+ public override bool IsChecked {
+ get { return pad.IsZoomControlVisible; }
+ set { pad.IsZoomControlVisible = value; }
+ }
+
+ public override void Run()
+ {
+ IsChecked = !IsChecked;
+ }
+ }
+
+ public sealed class ToggleMethodViewCommand : AbstractCheckableMenuCommand
+ {
+ ParallelStackPad pad;
+
+ public override object Owner {
+ get { return base.Owner; }
+ set {
+ if (!(value is ParallelStackPad))
+ throw new Exception("Owner has to be a AbstractConsolePad");
+ pad = value as ParallelStackPad;
+ base.Owner = value;
+ }
+ }
+
+ public override bool IsChecked {
+ get { return pad.IsMethodView; }
+ set { pad.IsMethodView = value; }
+ }
+
+ public override void Run()
+ {
+ IsChecked = !IsChecked;
+ }
+ }
+
+ public sealed class ParallelStacksViewCommand : AbstractComboBoxCommand
+ {
+ ParallelStackPad pad;
+ ComboBox box;
+
+ protected override void OnOwnerChanged(EventArgs e)
+ {
+ this.pad = this.Owner as ParallelStackPad;
+ if (this.pad == null)
+ return;
+
+ box = this.ComboBox as ComboBox;
+
+ if (this.box == null)
+ return;
+
+ foreach (var name in Enum.GetNames(typeof(ParallelStacksView)))
+ box.Items.Add(name);
+
+ box.SelectedIndex = 0;
+
+ base.OnOwnerChanged(e);
+ }
+
+ public override void Run()
+ {
+ if (this.pad != null && this.box != null) {
+ pad.ParallelStacksView = (ParallelStacksView)Enum.Parse(typeof(ParallelStacksView), box.SelectedValue.ToString());
+ }
+ base.Run();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/SelectLanguageCommand.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/SelectLanguageCommand.cs
similarity index 96%
rename from src/AddIns/Debugger/Debugger.AddIn/Pads/SelectLanguageCommand.cs
rename to src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/SelectLanguageCommand.cs
index 8cddba8ae9..b117571b31 100644
--- a/src/AddIns/Debugger/Debugger.AddIn/Pads/SelectLanguageCommand.cs
+++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/SelectLanguageCommand.cs
@@ -1,47 +1,47 @@
-// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
-// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
-
-using System;
-using Debugger;
-using Debugger.AddIn;
-using ICSharpCode.Core;
-using ICSharpCode.NRefactory;
-using ICSharpCode.SharpDevelop.Debugging;
-using ICSharpCode.SharpDevelop.Services;
-using System.Windows.Controls;
-
-namespace ICSharpCode.SharpDevelop.Gui.Pads
-{
- class SelectLanguageCommand : AbstractComboBoxCommand
- {
- ConsolePad pad;
- ComboBox box;
-
- protected override void OnOwnerChanged(EventArgs e)
- {
- this.pad = this.Owner as ConsolePad;
- if (this.pad == null)
- return;
-
- box = this.ComboBox as ComboBox;
-
- if (this.box == null)
- return;
-
- foreach (var name in Enum.GetNames(typeof(SupportedLanguage)))
- box.Items.Add(name);
-
- box.SelectedIndex = 0;
-
- base.OnOwnerChanged(e);
- }
-
- public override void Run()
- {
- if (this.pad != null && this.box != null) {
- pad.SelectedLanguage = (SupportedLanguage)Enum.Parse(typeof(SupportedLanguage), box.SelectedValue.ToString());
- }
- base.Run();
- }
- }
-}
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
+
+using System;
+using Debugger;
+using Debugger.AddIn;
+using ICSharpCode.Core;
+using ICSharpCode.NRefactory;
+using ICSharpCode.SharpDevelop.Debugging;
+using ICSharpCode.SharpDevelop.Services;
+using System.Windows.Controls;
+
+namespace ICSharpCode.SharpDevelop.Gui.Pads
+{
+ class SelectLanguageCommand : AbstractComboBoxCommand
+ {
+ ConsolePad pad;
+ ComboBox box;
+
+ protected override void OnOwnerChanged(EventArgs e)
+ {
+ this.pad = this.Owner as ConsolePad;
+ if (this.pad == null)
+ return;
+
+ box = this.ComboBox as ComboBox;
+
+ if (this.box == null)
+ return;
+
+ foreach (var name in Enum.GetNames(typeof(SupportedLanguage)))
+ box.Items.Add(name);
+
+ box.SelectedIndex = 0;
+
+ base.OnOwnerChanged(e);
+ }
+
+ public override void Run()
+ {
+ if (this.pad != null && this.box != null) {
+ pad.SelectedLanguage = (SupportedLanguage)Enum.Parse(typeof(SupportedLanguage), box.SelectedValue.ToString());
+ }
+ base.Run();
+ }
+ }
+}
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPadCommands.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/WatchPadCommands.cs
similarity index 100%
rename from src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPadCommands.cs
rename to src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/WatchPadCommands.cs
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/DebuggerPad.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/DebuggerPad.cs
index 54afe42de7..1076c8e141 100644
--- a/src/AddIns/Debugger/Debugger.AddIn/Pads/DebuggerPad.cs
+++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/DebuggerPad.cs
@@ -1,41 +1,66 @@
-// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
-// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
-
-using Debugger;
-using ICSharpCode.SharpDevelop.Debugging;
-using ICSharpCode.SharpDevelop.Services;
-
-namespace ICSharpCode.SharpDevelop.Gui.Pads
-{
- public abstract class DebuggerPad: AbstractPadContent
- {
- protected WindowsDebugger debugger;
-
- public DebuggerPad()
- {
- debugger = (WindowsDebugger)DebuggerService.CurrentDebugger;
-
- InitializeComponents();
-
- debugger.ProcessSelected += delegate(object sender, ProcessEventArgs e) {
- SelectProcess(e.Process);
- };
- SelectProcess(debugger.DebuggedProcess);
- }
-
- protected virtual void InitializeComponents()
- {
-
- }
-
- protected virtual void SelectProcess(Process process)
- {
-
- }
-
- public virtual void RefreshPad()
- {
-
- }
- }
-}
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
+
+using System.Windows.Controls;
+using Debugger;
+using ICSharpCode.SharpDevelop.Debugging;
+using ICSharpCode.SharpDevelop.Services;
+
+namespace ICSharpCode.SharpDevelop.Gui.Pads
+{
+ public abstract class DebuggerPad : AbstractPadContent
+ {
+ protected DockPanel panel;
+ ToolBar toolbar;
+ protected WindowsDebugger debugger;
+
+ public override object Control {
+ get {
+ return panel;
+ }
+ }
+
+ public DebuggerPad()
+ {
+ // UI
+ this.panel = new DockPanel();
+ this.toolbar = BuildToolBar();
+
+ if (this.toolbar != null) {
+ this.toolbar.SetValue(DockPanel.DockProperty, Dock.Top);
+
+ this.panel.Children.Add(toolbar);
+ }
+
+ // logic
+ debugger = (WindowsDebugger)DebuggerService.CurrentDebugger;
+
+ InitializeComponents();
+
+ debugger.ProcessSelected += delegate(object sender, ProcessEventArgs e) {
+ SelectProcess(e.Process);
+ };
+ SelectProcess(debugger.DebuggedProcess);
+ }
+
+ protected virtual void InitializeComponents()
+ {
+
+ }
+
+ protected virtual void SelectProcess(Process process)
+ {
+
+ }
+
+ public virtual void RefreshPad()
+ {
+
+ }
+
+ protected virtual ToolBar BuildToolBar()
+ {
+ return null;
+ }
+ }
+}
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/LoadedModulesPad.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/LoadedModulesPad.cs
index 0ccfc5c182..b93cf95ed0 100644
--- a/src/AddIns/Debugger/Debugger.AddIn/Pads/LoadedModulesPad.cs
+++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/LoadedModulesPad.cs
@@ -17,15 +17,10 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
SimpleListViewControl loadedModulesList;
Process debuggedProcess;
- public override object Control {
- get {
- return loadedModulesList;
- }
- }
-
protected override void InitializeComponents()
{
loadedModulesList = new SimpleListViewControl();
+ panel.Children.Add(loadedModulesList);
RedrawContent();
ResourceService.LanguageChanged += delegate { RedrawContent(); };
}
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/LocalVarPad.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/LocalVarPad.cs
index c2669f3ce7..61a6c0b550 100644
--- a/src/AddIns/Debugger/Debugger.AddIn/Pads/LocalVarPad.cs
+++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/LocalVarPad.cs
@@ -27,15 +27,6 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
get { return instance; }
}
- ///
- /// This is not used anywhere, but it is neccessary to be overridden in children of AbstractPadContent.
- ///
- public override object Control {
- get {
- return localVarList;
- }
- }
-
public Process Process {
get { return debuggedProcess; }
}
@@ -43,6 +34,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
protected override void InitializeComponents()
{
localVarList = new WatchList();
+ panel.Children.Add(localVarList);
}
protected override void SelectProcess(Process process)
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/ObjectGraphPad.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/ObjectGraphPad.cs
index 5a11ca25e7..06384cdebb 100644
--- a/src/AddIns/Debugger/Debugger.AddIn/Pads/ObjectGraphPad.cs
+++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/ObjectGraphPad.cs
@@ -1,76 +1,72 @@
-// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
-// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
-
-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 when pad becomes visible -> VisibleChanged event?
- if (!objectGraphControl.IsVisible)
- {
- 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();
- }
- }
-}
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
+
+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();
+ panel.Children.Add(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 when pad becomes visible -> VisibleChanged event?
+ if (!objectGraphControl.IsVisible)
+ {
+ 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/Debugger/Debugger.AddIn/Pads/ParallelPad/DrawSurface.xaml b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/DrawSurface.xaml
new file mode 100644
index 0000000000..9d574e64d0
--- /dev/null
+++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/DrawSurface.xaml
@@ -0,0 +1,147 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/DrawSurface.xaml.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/DrawSurface.xaml.cs
new file mode 100644
index 0000000000..22b9aa63fa
--- /dev/null
+++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/DrawSurface.xaml.cs
@@ -0,0 +1,71 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Shapes;
+
+using Microsoft.Windows.Themes;
+
+namespace Debugger.AddIn.Pads.ParallelPad
+{
+ public partial class DrawSurface : UserControl
+ {
+ private ScaleTransform zoom = new ScaleTransform();
+
+ public DrawSurface()
+ {
+ InitializeComponent();
+
+ ContentControl.LayoutTransform = zoom;
+ }
+
+ public void SetGraph(ParallelStacksGraph graph)
+ {
+ this.ParallelStacksLayout.Graph = graph;
+
+ if (graph == null)
+ this.ParallelStacksLayout.CancelLayout();
+ else
+ this.ParallelStacksLayout.Relayout();
+ }
+
+ public bool IsZoomControlVisible {
+ get { return ZoomControl.Visibility == Visibility.Visible; }
+ set {
+ if (value)
+ ZoomControl.Visibility = Visibility.Visible;
+ else
+ ZoomControl.Visibility = Visibility.Hidden;
+ }
+ }
+
+ #region Zoom
+
+ void SliderControl_ValueChanged(object sender, RoutedPropertyChangedEventArgs e)
+ {
+ if (e.OldValue == 0)
+ return;
+
+ double value = (e.NewValue / 5d) * 100;
+
+ this.PercentText.Text = string.Format("{0}%", value);
+
+ // zoom canvas
+ zoom.ScaleX = e.NewValue / 5d;
+ zoom.ScaleY = e.NewValue / 5d;
+ zoom.CenterX = drawingSurface.ActualWidth / 2d;
+ zoom.CenterY = drawingSurface.ActualHeight / 2d;
+ }
+
+ void Reset_Click(object sender, RoutedEventArgs e)
+ {
+ this.SliderControl.Value = 5;
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ParallelStackPad.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ParallelStackPad.cs
new file mode 100644
index 0000000000..d267d81bdb
--- /dev/null
+++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ParallelStackPad.cs
@@ -0,0 +1,679 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Dynamic;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+
+using Debugger;
+using Debugger.AddIn.Pads.ParallelPad;
+using Debugger.AddIn.TreeModel;
+using ICSharpCode.AvalonEdit.Rendering;
+using ICSharpCode.Core;
+using ICSharpCode.Core.Presentation;
+using ICSharpCode.NRefactory;
+using ICSharpCode.SharpDevelop.Bookmarks;
+using ICSharpCode.SharpDevelop.Debugging;
+using ICSharpCode.SharpDevelop.Editor;
+using ICSharpCode.SharpDevelop.Gui.Pads;
+
+namespace ICSharpCode.SharpDevelop.Gui.Pads
+{
+ public enum ParallelStacksView
+ {
+ Threads,
+ Tasks
+ }
+
+ public class ParallelStackPad : DebuggerPad
+ {
+ private DrawSurface surface;
+ private Process debuggedProcess;
+ private ParallelStacksGraph graph;
+ private List currentThreadStacks = new List();
+ private ParallelStacksView parallelStacksView;
+ private StackFrame selectedFrame;
+ private bool isMethodView;
+
+ #region Overrides
+
+ protected override void InitializeComponents()
+ {
+ surface = new DrawSurface();
+
+ panel.Children.Add(surface);
+ }
+
+ protected override void SelectProcess(Process process)
+ {
+ if (debuggedProcess != null) {
+ debuggedProcess.Paused -= OnProcessPaused;
+ }
+ debuggedProcess = process;
+ if (debuggedProcess != null) {
+ debuggedProcess.Paused += OnProcessPaused;
+ }
+
+ DebuggerService.DebugStarted += OnReset;
+ DebuggerService.DebugStopped += OnReset;
+
+ RefreshPad();
+ }
+
+ public override void RefreshPad()
+ {
+ if (debuggedProcess == null || debuggedProcess.IsRunning) {
+ return;
+ }
+
+ LoggingService.InfoFormatted("Start refresh: {0}" + Environment.NewLine, parallelStacksView);
+
+ currentThreadStacks.Clear();
+
+ using(new PrintTimes("Create stacks")) {
+ try {
+ // create all simple ThreadStacks
+ foreach (Thread thread in debuggedProcess.Threads) {
+ if (debuggedProcess.IsPaused) {
+ Utils.DoEvents(debuggedProcess);
+ }
+ CreateThreadStack(thread);
+ }
+ }
+ catch(AbortedBecauseDebuggeeResumedException) { }
+ catch(System.Exception) {
+ if (debuggedProcess == null || debuggedProcess.HasExited) {
+ // Process unexpectedly exited
+ } else {
+ throw;
+ }
+ }
+ }
+ using(new PrintTimes("Run algorithm")) {
+ if (isMethodView)
+ {
+ // build method view for threads
+ CreateMethodViewStacks();
+ }
+ else
+ {
+ // normal view
+ CreateCommonStacks();
+ }
+ }
+
+ using(new PrintTimes("Graph refresh")) {
+ // paint the ThreadStaks
+ graph = new ParallelStacksGraph();
+ foreach (var stack in this.currentThreadStacks.FindAll(ts => ts.ThreadStackParents == null ))
+ {
+ graph.AddVertex(stack);
+
+ // add the children
+ AddChildren(stack);
+ }
+
+ surface.SetGraph(graph);
+ }
+ }
+
+ protected override ToolBar BuildToolBar()
+ {
+ return ToolBarService.CreateToolBar(this, "/SharpDevelop/Pads/ParallelStacksPad/ToolBar");
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ public ParallelStacksView ParallelStacksView {
+ get { return parallelStacksView; }
+ set {
+ parallelStacksView = value;
+ RefreshPad();
+ }
+ }
+
+ public bool IsMethodView {
+ get { return isMethodView; }
+ set {
+ isMethodView = value;
+ RefreshPad();
+ }
+ }
+
+ public bool IsZoomControlVisible {
+ get { return surface.IsZoomControlVisible; }
+ set { surface.IsZoomControlVisible = value; }
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ private void OnReset(object sender, EventArgs e)
+ {
+ currentThreadStacks.Clear();
+ selectedFrame = null;
+
+ // remove all
+ BookmarkManager.RemoveAll(b => b is SelectedFrameBookmark);
+ }
+
+ private void OnProcessPaused(object sender, ProcessEventArgs e)
+ {
+ RefreshPad();
+ }
+
+ private void AddChildren(ThreadStack parent)
+ {
+ if(parent.ThreadStackChildren == null || parent.ThreadStackChildren.Count == 0)
+ return;
+
+ foreach (var ts in parent.ThreadStackChildren)
+ {
+ if (ts == null) continue;
+
+ graph.AddVertex(ts);
+ graph.AddEdge(new ParallelStacksEdge(parent, ts));
+
+ if (ts.ThreadStackChildren == null || ts.ThreadStackChildren.Count == 0)
+ continue;
+
+ AddChildren(ts);
+ }
+ }
+
+ private void CreateCommonStacks()
+ {
+ // stack.ItemCollection order
+ // 0 -> top of stack = S.C
+ // 1 -> ............ = S.B
+ // .......................
+ // n -> bottom of stack = [External Methods]
+
+ // ThreadStacks with common frame
+ var commonFrameThreads = new Dictionary>();
+
+ bool isOver = false;
+ while (true) {
+
+ for (int i = currentThreadStacks.Count - 1; i >=0; --i) {
+ var stack = currentThreadStacks[i];
+ if (stack.ItemCollection.Count == 0) {
+ currentThreadStacks.Remove(stack);
+ continue;
+ }
+ }
+ //get all thread stacks with common start frame
+ foreach (var stack in currentThreadStacks) {
+ int count = stack.ItemCollection.Count;
+ dynamic frame = stack.ItemCollection[count - 1];
+ string fullname = frame.MethodName + stack.Level.ToString();
+
+ if (!commonFrameThreads.ContainsKey(fullname))
+ commonFrameThreads.Add(fullname, new List());
+
+ if (!commonFrameThreads[fullname].Contains(stack))
+ commonFrameThreads[fullname].Add(stack);
+ }
+
+ // for every list of common stacks, find split place and add them to currentThreadStacks
+ foreach (var frameName in commonFrameThreads.Keys) {
+ var listOfCurrentStacks = commonFrameThreads[frameName];
+
+ if (listOfCurrentStacks.Count == 1) // just skip the parents
+ {
+ isOver = true; // we finish when all are pseodo-parents: no more spliting
+ continue;
+ }
+
+ isOver = false;
+
+ // find the frameIndex where we can split
+ int frameIndex = 0;
+ string fn = string.Empty;
+ bool canContinue = true;
+
+ while(canContinue) {
+ for (int i = 0; i < listOfCurrentStacks.Count; ++i) {
+ var stack = listOfCurrentStacks[i];
+ if (stack.ItemCollection.Count == frameIndex)
+ {
+ canContinue = false;
+ break;
+ }
+ dynamic item = stack.ItemCollection[stack.ItemCollection.Count - frameIndex - 1];
+
+ string currentName = item.MethodName;
+
+ if (i == 0) {
+ fn = currentName;
+ continue;
+ }
+
+ if (fn == currentName)
+ continue;
+ else {
+ canContinue = false;
+ break;
+ }
+ }
+ if (canContinue)
+ frameIndex++;
+ }
+
+ // remove last [frameIndex] and create a new ThreadStack as the parent of what remained in the children
+ var threadIds = new List();
+ var parentItems = new Stack();
+
+ while (frameIndex > 0) {
+ for (int i = 0 ; i < listOfCurrentStacks.Count; ++i) {
+ var stack = listOfCurrentStacks[i];
+ int indexToRemove = stack.ItemCollection.Count - 1;
+
+ #if DEBUG
+ dynamic d_item = stack.ItemCollection[indexToRemove];
+ string name = d_item.MethodName;
+ #endif
+ if (i == 0)
+ parentItems.Push(stack.ItemCollection[indexToRemove]);
+
+ stack.ItemCollection.RemoveAt(indexToRemove);
+ }
+
+ frameIndex--;
+ }
+
+ // update thread ids
+ for (int i = 0 ; i < listOfCurrentStacks.Count; ++i)
+ threadIds.AddRange(listOfCurrentStacks[i].ThreadIds);
+
+ // remove stacks with no items
+ for (int i = listOfCurrentStacks.Count - 1; i >= 0; --i) {
+ var stack = listOfCurrentStacks[i];
+ if (stack.ItemCollection.Count == 0)
+ listOfCurrentStacks.Remove(stack);
+ }
+
+ // increase the Level
+ for (int i = 0 ; i < listOfCurrentStacks.Count; ++i)
+ listOfCurrentStacks[i].Level++;
+
+ // create new parent stack
+ ThreadStack commonParent = new ThreadStack();
+ commonParent.UpdateThreadIds(parallelStacksView == ParallelStacksView.Tasks, threadIds.ToArray());
+ commonParent.ItemCollection = parentItems.ToObservable();
+ commonParent.Process = debuggedProcess;
+ commonParent.StackSelected += OnThreadStackSelected;
+ commonParent.FrameSelected += OnFrameSelected;
+ commonParent.IsSelected = commonParent.ThreadIds.Contains(debuggedProcess.SelectedThread.ID);
+ // add new children
+ foreach (var stack in listOfCurrentStacks) {
+ if (stack.ItemCollection.Count == 0)
+ {
+ currentThreadStacks.Remove(stack);
+ continue;
+ }
+ dynamic item = stack.ItemCollection[stack.ItemCollection.Count - 1];
+ // add the parent to the parent
+ if (stack.ThreadStackParents != null) {
+ // remove stack from it's parent because it will have the commonParent as parent
+ stack.ThreadStackParents[0].ThreadStackChildren.Remove(stack);
+ commonParent.ThreadStackParents = stack.ThreadStackParents.Clone();
+ commonParent.ThreadStackParents[0].ThreadStackChildren.Add(commonParent);
+ // set level
+ commonParent.Level = stack.Level - 1;
+ }
+ else
+ stack.ThreadStackParents = new List();
+
+ // update thread ids
+ if (commonParent.ThreadStackParents != null) {
+ commonParent.ThreadStackParents[0].UpdateThreadIds(
+ parallelStacksView == ParallelStacksView.Tasks,
+ commonParent.ThreadIds.ToArray());
+ }
+
+ stack.ThreadStackParents.Clear();
+ stack.ThreadStackParents.Add(commonParent);
+ string currentName = item.MethodName + stack.Level.ToString();;
+
+ // add name or add to list
+ if (!commonFrameThreads.ContainsKey(currentName)) {
+ var newList = new List();
+ newList.Add(stack);
+ commonFrameThreads.Add(currentName, newList);
+ }
+ else {
+ var list = commonFrameThreads[currentName];
+ list.Add(stack);
+ }
+ }
+
+ commonParent.ThreadStackChildren = listOfCurrentStacks.Clone();
+ commonFrameThreads[frameName].Clear();
+ commonFrameThreads[frameName].Add(commonParent);
+ currentThreadStacks.Add(commonParent);
+
+ // exit and retry
+ break;
+ }
+
+ if (isOver || currentThreadStacks.Count == 0)
+ break;
+ }
+ }
+
+ private void CreateMethodViewStacks()
+ {
+ var list =
+ new List, ObservableCollection, List>>();
+
+ // find all threadstacks that contains the selected frame
+ for (int i = currentThreadStacks.Count - 1; i >= 0; --i) {
+ var tuple = currentThreadStacks[i].ItemCollection.SplitStack(selectedFrame, currentThreadStacks[i].ThreadIds);
+ if (tuple != null)
+ list.Add(tuple);
+ }
+
+ currentThreadStacks.Clear();
+
+ // common
+ ThreadStack common = new ThreadStack();
+ var observ = new ObservableCollection();
+ bool dummy = false;
+ dynamic obj = CreateItemForFrame(selectedFrame, ref dummy);
+ obj.Image = PresentationResourceService.GetImage("Icons.48x48.CurrentFrame").Source;
+ observ.Add(obj);
+ common.ItemCollection = observ;
+ common.StackSelected += OnThreadStackSelected;
+ common.FrameSelected += OnFrameSelected;
+ common.UpdateThreadIds(parallelStacksView == ParallelStacksView.Tasks, selectedFrame.Thread.ID);
+ common.Process = debuggedProcess;
+ common.ThreadStackChildren = new List();
+ common.ThreadStackParents = new List();
+
+ // for all thread stacks, split them in 2 :
+ // one that invokes the method frame, second that get's invoked by current method frame
+ foreach (var tuple in list) {
+ // add top
+ if (tuple.Item1.Count > 0)
+ {
+ ThreadStack topStack = new ThreadStack();
+ topStack.ItemCollection = tuple.Item1;
+ topStack.StackSelected += OnThreadStackSelected;
+ topStack.FrameSelected += OnFrameSelected;
+ topStack.UpdateThreadIds(parallelStacksView == ParallelStacksView.Tasks, tuple.Item3.ToArray());
+ topStack.Process = debuggedProcess;
+ topStack.ThreadStackParents = new List();
+ topStack.ThreadStackParents.Add(common);
+
+ currentThreadStacks.Add(topStack);
+ common.ThreadStackChildren.Add(topStack);
+ }
+
+ // add bottom
+ if(tuple.Item2.Count > 0)
+ {
+ ThreadStack bottomStack = new ThreadStack();
+ bottomStack.ItemCollection = tuple.Item2;
+ bottomStack.StackSelected += OnThreadStackSelected;
+ bottomStack.FrameSelected += OnFrameSelected;
+ bottomStack.UpdateThreadIds(parallelStacksView == ParallelStacksView.Tasks, tuple.Item3.ToArray());
+ bottomStack.Process = debuggedProcess;
+ bottomStack.ThreadStackChildren = new List();
+ bottomStack.ThreadStackChildren.Add(common);
+ common.UpdateThreadIds(parallelStacksView == ParallelStacksView.Tasks, tuple.Item3.ToArray());
+ common.ThreadStackParents.Add(bottomStack);
+ currentThreadStacks.Add(bottomStack);
+ }
+ }
+
+ currentThreadStacks.Add(common);
+ common.IsSelected = true;
+ }
+
+ private void CreateThreadStack(Thread thread)
+ {
+ var items = CreateItems(thread);
+ if (items == null || items.Count == 0)
+ return;
+
+ ThreadStack threadStack = new ThreadStack();
+ threadStack.StackSelected += OnThreadStackSelected;
+ threadStack.FrameSelected += OnFrameSelected;
+ threadStack.Process = debuggedProcess;
+ threadStack.ItemCollection = items;
+ threadStack.UpdateThreadIds(parallelStacksView == ParallelStacksView.Tasks, thread.ID);
+
+ if (debuggedProcess.SelectedThread != null) {
+ threadStack.IsSelected = threadStack.ThreadIds.Contains(debuggedProcess.SelectedThread.ID);
+ if (selectedFrame == null)
+ selectedFrame = debuggedProcess.SelectedStackFrame;
+ }
+
+ currentThreadStacks.Add(threadStack);
+ }
+
+ private ObservableCollection CreateItems(Thread thread)
+ {
+ bool lastItemIsExternalMethod = false;
+ int noTasks = 0;
+ var result = new ObservableCollection();
+ var callstack = thread.GetCallstack(100);
+
+ if (parallelStacksView == ParallelStacksView.Threads) {
+ foreach (StackFrame frame in callstack) {
+ dynamic obj = CreateItemForFrame(frame, ref lastItemIsExternalMethod);
+
+ if (obj != null)
+ result.Add(obj);
+ }
+ } else {
+ for (int i = 0 ; i < callstack.Length; ++i) {
+ StackFrame frame = callstack[i];
+ dynamic obj = CreateItemForFrame(frame, ref lastItemIsExternalMethod);
+
+ if (frame.MethodInfo.FullName.IndexOf("System.Threading.Tasks.Task.ExecuteEntry") != -1) {
+ noTasks++;
+ }
+
+ if (noTasks == 1) {
+ if (frame.HasSymbols) {
+ // create thread stack for the items collected until now
+ ThreadStack threadStack = new ThreadStack();
+ threadStack.StackSelected += OnThreadStackSelected;
+ threadStack.FrameSelected += OnFrameSelected;
+ threadStack.Process = debuggedProcess;
+ threadStack.ItemCollection = result.Clone();
+ threadStack.UpdateThreadIds(true, frame.Thread.ID);
+
+ if (debuggedProcess.SelectedThread != null) {
+ threadStack.IsSelected = threadStack.ThreadIds.Contains(debuggedProcess.SelectedThread.ID);
+ if (selectedFrame == null)
+ selectedFrame = debuggedProcess.SelectedStackFrame;
+ }
+
+ currentThreadStacks.Add(threadStack);
+ // reset
+ result.Clear();
+ noTasks = 0;
+ }
+ }
+
+ if (obj != null)
+ result.Add(obj);
+ }
+
+ Utils.DoEvents(debuggedProcess);
+ // return null if we are dealing with a simple thread
+ return noTasks == 0 ? null : result;
+ }
+
+ Utils.DoEvents(debuggedProcess);
+ return result;
+ }
+
+ private ExpandoObject CreateItemForFrame(StackFrame frame, ref bool lastItemIsExternalMethod)
+ {
+ dynamic obj = new ExpandoObject();
+ string fullName;
+ if (frame.HasSymbols) {
+ // Show the method in the list
+ fullName = frame.GetMethodName();
+ lastItemIsExternalMethod = false;
+ obj.FontWeight = FontWeights.Normal;
+ obj.Foreground = Brushes.Black;
+ } else {
+ // Show [External methods] in the list
+ if (lastItemIsExternalMethod) return null;
+ fullName = ResourceService.GetString("MainWindow.Windows.Debug.CallStack.ExternalMethods").Trim();
+ obj.FontWeight = FontWeights.Normal;
+ obj.Foreground = Brushes.Gray;
+ lastItemIsExternalMethod = true;
+ }
+
+ if (frame.Thread.SelectedStackFrame != null &&
+ frame.Thread.ID == debuggedProcess.SelectedThread.ID &&
+ frame.Thread.SelectedStackFrame.IP == frame.IP &&
+ frame.Thread.SelectedStackFrame.GetMethodName() == frame.GetMethodName()) {
+ obj.Image = PresentationResourceService.GetImage("Bookmarks.CurrentLine").Source;
+ obj.IsRunningStackFrame = true;
+ } else {
+ if (selectedFrame != null && frame.Thread.ID == selectedFrame.Thread.ID &&
+ frame.GetMethodName() == selectedFrame.GetMethodName())
+ obj.Image = PresentationResourceService.GetImage("Icons.48x48.CurrentFrame").Source;
+ else
+ obj.Image = null;
+ obj.IsRunningStackFrame = false;
+ }
+
+ obj.MethodName = fullName;
+
+ return obj;
+ }
+
+ private void ToggleSelectedFrameBookmark(Location location)
+ {
+ // remove all
+ BookmarkManager.RemoveAll(b => b is SelectedFrameBookmark);
+
+ ITextEditorProvider provider = WorkbenchSingleton.Workbench.ActiveContent as ITextEditorProvider;
+ if (provider != null) {
+ ITextEditor editor = provider.TextEditor;
+ BookmarkManager.AddMark(new SelectedFrameBookmark(editor.FileName, location));
+ }
+ }
+
+ private void OnThreadStackSelected(object sender, EventArgs e)
+ {
+ foreach (var ts in this.currentThreadStacks) {
+ if (ts.IsSelected)
+ ts.IsSelected = false;
+ ts.ClearImages();
+ }
+ }
+
+ private void OnFrameSelected(object sender, FrameSelectedEventArgs e)
+ {
+ selectedFrame = e.Item;
+
+ ToggleSelectedFrameBookmark(e.Location);
+
+ if (isMethodView)
+ RefreshPad();
+ }
+
+ #endregion
+ }
+
+ internal static class StackFrameExtensions
+ {
+ internal static string GetMethodName(this StackFrame frame)
+ {
+ if (frame == null)
+ return null;
+
+ StringBuilder name = new StringBuilder();
+ name.Append(frame.MethodInfo.DeclaringType.FullName);
+ name.Append('.');
+ name.Append(frame.MethodInfo.Name);
+
+ return name.ToString();
+ }
+ }
+
+ internal static class ParallelStackExtensions
+ {
+ internal static List Clone(this List listToClone)
+ {
+ if (listToClone == null)
+ return null;
+
+ var result = new List();
+ foreach (var item in listToClone)
+ result.Add(item);
+
+ return result;
+ }
+
+ internal static ObservableCollection Clone(this ObservableCollection collectionToClone)
+ {
+ if (collectionToClone == null)
+ return null;
+
+ var result = new ObservableCollection();
+ foreach (var item in collectionToClone)
+ result.Add(item);
+
+ return result;
+ }
+
+ internal static ObservableCollection ToObservable(this Stack stack)
+ {
+ if (stack == null)
+ throw new NullReferenceException("Stack is null!");
+
+ var result = new ObservableCollection();
+ while (stack.Count > 0)
+ result.Add(stack.Pop());
+
+ return result;
+ }
+
+ internal static Tuple, ObservableCollection, List>
+ SplitStack(this ObservableCollection source, StackFrame frame, List threadIds)
+ {
+ var bottom = new ObservableCollection();
+ var top = new ObservableCollection();
+
+ int found = 0;
+
+ foreach (dynamic item in source)
+ {
+ if (item.MethodName == frame.GetMethodName())
+ found = 1;
+
+ if (found >= 1) {
+ if(found > 1)
+ bottom.Add(item);
+
+ found++;
+ }
+ else
+ top.Add(item);
+ }
+
+ var result =
+ new Tuple, ObservableCollection, List>(top, bottom, threadIds);
+
+ return found > 1 ? result : null;
+ }
+ }
+}
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ParallelStacksGraph.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ParallelStacksGraph.cs
new file mode 100644
index 0000000000..888ae9ed07
--- /dev/null
+++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ParallelStacksGraph.cs
@@ -0,0 +1,38 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+using System;
+using QuickGraph;
+using GraphSharp.Algorithms.Layout;
+using GraphSharp.Algorithms.Layout.Contextual;
+using GraphSharp.Algorithms.Layout.Simple.Tree;
+using GraphSharp.Controls;
+
+namespace Debugger.AddIn.Pads.ParallelPad
+{
+ public class ParallelStacksEdge : QuickGraph.Edge
+ {
+ public ParallelStacksEdge(ThreadStack source, ThreadStack target) : base(source, target)
+ { }
+ }
+
+ public class ParallelStacksGraph : BidirectionalGraph
+ {
+ public ParallelStacksGraph()
+ { }
+ }
+
+ public class ParallelStacksGraphLayout : GraphLayout
+ {
+ public ParallelStacksGraphLayout()
+ {
+ // TODO : Replace the current tree layout with EfficientSugiyama layout when Direction is available for this type of layout
+ var par = new SimpleTreeLayoutParameters();
+ par.LayerGap = 30;
+ par.VertexGap = 50;
+ par.Direction = LayoutDirection.BottomToTop;
+ par.SpanningTreeGeneration = SpanningTreeGeneration.DFS;
+
+ this.LayoutParameters = par;
+ }
+ }
+}
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/SelectedFrameBookmark.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/SelectedFrameBookmark.cs
new file mode 100644
index 0000000000..523458ea1e
--- /dev/null
+++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/SelectedFrameBookmark.cs
@@ -0,0 +1,54 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+using System;
+using System.Windows.Media;
+using ICSharpCode.Core;
+using ICSharpCode.NRefactory;
+using ICSharpCode.SharpDevelop;
+using ICSharpCode.SharpDevelop.Bookmarks;
+using ICSharpCode.SharpDevelop.Editor;
+
+namespace Debugger.AddIn.Pads.ParallelPad
+{
+ public class SelectedFrameBookmark : SDMarkerBookmark
+ {
+ public static readonly IImage SelectedFrameImage = new ResourceServiceImage("Icons.48x48.CurrentFrame");
+
+ public SelectedFrameBookmark(FileName fileName, Location location) : base(fileName, location)
+ {
+ this.IsVisibleInBookmarkPad = false;
+ }
+
+ ///
+ ///
+ ///
+ public override bool CanToggle {
+ get {
+ return false;
+ }
+ }
+
+ ///
+ ///
+ ///
+ public override IImage Image {
+ get {
+ return SelectedFrameImage;
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ protected override ITextMarker CreateMarker(ITextMarkerService markerService)
+ {
+ IDocumentLine line = this.Document.GetLine(this.LineNumber);
+ ITextMarker marker = markerService.Create(line.Offset, line.Length);
+ marker.BackgroundColor = Color.FromRgb(162, 208, 80);
+ marker.ForegroundColor = Colors.White;
+ return marker;
+ }
+ }
+}
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ThreadStack.xaml b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ThreadStack.xaml
new file mode 100644
index 0000000000..c95bcac522
--- /dev/null
+++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ThreadStack.xaml
@@ -0,0 +1,132 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ThreadStack.xaml.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ThreadStack.xaml.cs
new file mode 100644
index 0000000000..1d8c012d5c
--- /dev/null
+++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ThreadStack.xaml.cs
@@ -0,0 +1,340 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Dynamic;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+
+using ICSharpCode.Core.Presentation;
+using ICSharpCode.NRefactory;
+using ICSharpCode.SharpDevelop;
+using ICSharpCode.SharpDevelop.Gui.Pads;
+
+namespace Debugger.AddIn.Pads.ParallelPad
+{
+ public class FrameSelectedEventArgs : EventArgs
+ {
+ public StackFrame Item {
+ get;
+ private set;
+ }
+
+ public Location Location {
+ get;
+ private set;
+ }
+
+ public FrameSelectedEventArgs(StackFrame item, Location location)
+ {
+ Item = item;
+ Location = location;
+ }
+ }
+
+ public partial class ThreadStack : UserControl
+ {
+ public static SolidColorBrush SelectedBrush = new SolidColorBrush(Color.FromRgb(84, 169, 255));
+
+ public static readonly DependencyProperty IsSelectedProperty =
+ DependencyProperty.Register("IsSelected", typeof(bool), typeof(ThreadStack),
+ new FrameworkPropertyMetadata());
+
+ public event EventHandler StackSelected;
+
+ public event EventHandler FrameSelected;
+
+ private ObservableCollection itemCollection = new ObservableCollection();
+
+ private ToolTip toolTip = new ToolTip();
+ private List threadIds = new List();
+
+ public ThreadStack()
+ {
+ InitializeComponent();
+ datagrid.ToolTip = toolTip;
+ datagrid.ToolTipOpening += OnToolTipOpening;
+ datagrid.PreviewMouseMove += new MouseEventHandler(datagrid_PreviewMouseMove);
+ datagrid.MouseLeave += delegate { toolTip.IsOpen = false; };
+ }
+
+ #region Public Properties
+
+ public Process Process { get; set; }
+
+ public int Level { get; set; }
+
+ public bool IsSelected {
+ get { return (bool)GetValue(IsSelectedProperty); }
+ set {
+ if (value) {
+ BorderParent.BorderBrush = SelectedBrush;
+ BorderParent.BorderThickness = new Thickness(5);
+ }
+ else {
+ BorderParent.BorderBrush = Brushes.Black;
+ BorderParent.BorderThickness = new Thickness(3);
+ }
+
+ SetValue(IsSelectedProperty, value);
+
+ SelectParent(value);
+ }
+ }
+
+ public List ThreadStackParents { get; set; }
+
+ public List ThreadStackChildren { get; set; }
+
+ public List ThreadIds {
+ get {
+ return threadIds;
+ }
+ }
+
+ public ObservableCollection ItemCollection {
+ get {
+ return itemCollection;
+ }
+
+ set {
+ itemCollection = value;
+ this.datagrid.ItemsSource = itemCollection;
+ }
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ public void UpdateThreadIds(bool isTask, params uint[] threadIds)
+ {
+ var list = new List();
+ foreach (uint id in threadIds) {
+ if (!this.threadIds.Contains(id)) {
+ list.Add(id);
+ }
+ }
+ this.threadIds.AddRange(list);
+
+ if (this.threadIds.Count > 1) {
+ string suffix = isTask ? " Tasks" : " Threads";
+ this.HeaderText.Text = this.threadIds.Count.ToString() + suffix;
+ }
+ else {
+ this.HeaderText.Text = isTask ? "1 Task" : "1 Thread";
+ }
+ }
+
+ public void ClearImages()
+ {
+ foreach(dynamic item in itemCollection) {
+ if (!item.IsRunningStackFrame)
+ item.Image = null;
+ }
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ private void SelectParent(bool isSelected)
+ {
+ if (this.ThreadStackParents == null || this.ThreadStackParents.Count == 0)
+ return;
+
+ foreach (var ts in this.ThreadStackParents)
+ if (ts != null)
+ ts.IsSelected = isSelected;
+ }
+
+ void datagrid_PreviewMouseMove(object sender, MouseEventArgs e)
+ {
+ var result = VisualTreeHelper.HitTest(this, e.GetPosition(this));
+ if (result != null)
+ {
+ var row = TryFindParent(result.VisualHit);
+ if (row != null)
+ {
+ datagrid.SelectedItem = row.DataContext;
+ e.Handled = true;
+ }
+ }
+ }
+
+ private void Datagrid_MouseDoubleClick(object sender, MouseButtonEventArgs e)
+ {
+ if (Process.IsRunning) return;
+
+ dynamic selectedItem = datagrid.SelectedItem;
+ if (selectedItem != null) {
+ if (ThreadIds.Count > 1) {
+ datagrid.ContextMenu = CreateContextMenu(selectedItem);
+ datagrid.ContextMenu.IsOpen = true;
+ }
+ else
+ {
+ SelectFrame(ThreadIds[0], selectedItem);
+ }
+ }
+ }
+
+ private void SelectFrame(uint threadId, ExpandoObject selectedItem)
+ {
+ if (selectedItem == null)
+ return;
+
+ var thread = Process.Threads.Find(t => t.ID == threadId);
+ if (thread == null)
+ return;
+
+ if (StackSelected != null)
+ StackSelected(this, EventArgs.Empty);
+
+ this.IsSelected = true;
+
+ dynamic obj = selectedItem;
+
+ foreach(var frame in thread.Callstack)
+ {
+ if (frame.GetMethodName() == obj.MethodName)
+ {
+ if (!obj.IsRunningStackFrame)
+ obj.Image = PresentationResourceService.GetImage("Icons.48x48.CurrentFrame").Source;
+
+ SourcecodeSegment nextStatement = frame.NextStatement;
+ if (nextStatement != null) {
+ var location = new Location(nextStatement.StartColumn, nextStatement.StartLine);
+ FileService.JumpToFilePosition(
+ nextStatement.Filename, location.Line, location.Column);
+
+ if (FrameSelected != null)
+ FrameSelected(this, new FrameSelectedEventArgs(frame, location));
+ }
+
+ break;
+ }
+ }
+ }
+
+ private void Datagrid_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
+ {
+ if (Process.IsRunning) return;
+
+ dynamic selectedItem = datagrid.SelectedItem;
+ if (selectedItem == null)
+ return;
+
+ datagrid.ContextMenu = CreateContextMenu(selectedItem);
+ datagrid.ContextMenu.IsOpen = true;
+ }
+
+ private ContextMenu CreateContextMenu(ExpandoObject item)
+ {
+ dynamic obj = item;
+
+ var menu = new ContextMenu();
+ foreach (var id in ThreadIds)
+ {
+ MenuItem m = new MenuItem();
+ m.IsCheckable = true;
+ m.IsChecked = id == Process.SelectedThread.ID;
+ m.Click += delegate(object sender, RoutedEventArgs e) {
+ var menuItem = e.OriginalSource as MenuItem;
+ SelectFrame((uint)menuItem.Tag, item);
+ };
+ m.Tag = id;
+ m.Header = id.ToString() + ":" + obj.MethodName;
+
+ menu.Items.Add(m);
+ }
+
+ return menu;
+ }
+
+ private void OnToolTipOpening(object sender, ToolTipEventArgs e)
+ {
+ if (Process.IsRunning)
+ return;
+
+ StackPanel panel = new StackPanel();
+
+ dynamic selectedItem = datagrid.SelectedItem;
+ if (selectedItem == null) {
+ panel.Children.Add(new TextBlock { Text = "No item selected" });
+ this.toolTip.Content = panel;
+ return;
+ }
+
+ foreach(var thread in Process.Threads)
+ {
+ if (ThreadIds.Contains(thread.ID))
+ {
+ foreach (var frame in thread.Callstack)
+ {
+ if (selectedItem.MethodName == frame.GetMethodName())
+ {
+ TextBlock tb = new TextBlock();
+ tb.Text = thread.ID + ": " + CallStackPadContent.GetFullName(frame);
+ panel.Children.Add(tb);
+ }
+ }
+ }
+ }
+
+ this.toolTip.Content = panel;
+ }
+
+ #endregion
+
+ #region Static Methods
+
+ private static T TryFindParent(DependencyObject child) where T : DependencyObject
+ {
+ if (child is T) return child as T;
+
+ DependencyObject parentObject = GetParentObject(child);
+ if (parentObject == null) return null;
+
+ var parent = parentObject as T;
+ if (parent != null && parent is T)
+ {
+ return parent;
+ }
+ else
+ {
+ return TryFindParent(parentObject);
+ }
+ }
+
+ private static DependencyObject GetParentObject(DependencyObject child)
+ {
+ if (child == null) return null;
+
+ ContentElement contentElement = child as ContentElement;
+ if (contentElement != null)
+ {
+ DependencyObject parent = ContentOperations.GetParent(contentElement);
+ if (parent != null) return parent;
+
+ FrameworkContentElement fce = contentElement as FrameworkContentElement;
+ return fce != null ? fce.Parent : null;
+ }
+
+ FrameworkElement frameworkElement = child as FrameworkElement;
+ if (frameworkElement != null)
+ {
+ DependencyObject parent = frameworkElement.Parent;
+ if (parent != null) return parent;
+ }
+
+ return VisualTreeHelper.GetParent(child);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/RunningThreadsPad.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/RunningThreadsPad.cs
index 672583380b..699993def5 100644
--- a/src/AddIns/Debugger/Debugger.AddIn/Pads/RunningThreadsPad.cs
+++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/RunningThreadsPad.cs
@@ -22,17 +22,12 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
SimpleListViewControl runningThreadsList;
Process debuggedProcess;
- public override object Control {
- get {
- return runningThreadsList;
- }
- }
-
protected override void InitializeComponents()
{
runningThreadsList = new SimpleListViewControl();
runningThreadsList.ContextMenu = CreateContextMenuStrip();
runningThreadsList.ItemActivated += RunningThreadsListItemActivate;
+ panel.Children.Add(runningThreadsList);
RedrawContent();
ResourceService.LanguageChanged += delegate { RedrawContent(); };
@@ -147,7 +142,9 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
if (item == null) return;
var thread = item.Tag as Thread;
- if (thread == null) return;
+ if (thread == null)
+ return;
+
item.ID = thread.ID;
item.Tag = thread;
StackFrame location = null;
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPad.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPad.cs
index 5ff26e1fbc..aa4cce417f 100644
--- a/src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPad.cs
+++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPad.cs
@@ -36,15 +36,6 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
instance = this;
}
- ///
- /// This is not used anywhere, but it is neccessary to be overridden in children of AbstractPadContent.
- ///
- public override object Control {
- get {
- return watchList;
- }
- }
-
public Process Process {
get { return debuggedProcess; }
}
@@ -59,6 +50,8 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
watchList.DragEnter += watchList_DragOver;
watchList.Drop += watchList_Drop;
watchList.KeyUp += watchList_KeyUp;
+
+ panel.Children.Add(watchList);
}
void watchList_KeyUp(object sender, KeyEventArgs e)
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Visualizers/Controls/DragScrollViewer.cs b/src/AddIns/Debugger/Debugger.AddIn/Visualizers/Controls/DragScrollViewer.cs
index cf88805625..465e5fd4ac 100644
--- a/src/AddIns/Debugger/Debugger.AddIn/Visualizers/Controls/DragScrollViewer.cs
+++ b/src/AddIns/Debugger/Debugger.AddIn/Visualizers/Controls/DragScrollViewer.cs
@@ -19,6 +19,9 @@ namespace Debugger.AddIn.Visualizers.Controls
public DragScrollViewer() : base()
{
+ this.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
+ this.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
+
this.PreviewMouseDown += new System.Windows.Input.MouseButtonEventHandler(DragScrollViewer_PreviewMouseDown);
this.PreviewMouseMove += new System.Windows.Input.MouseEventHandler(DragScrollViewer_PreviewMouseMove);
this.PreviewMouseUp += new System.Windows.Input.MouseButtonEventHandler(DragScrollViewer_PreviewMouseUp);
diff --git a/src/AddIns/Debugger/Debugger.Core/ThreadCollection.cs b/src/AddIns/Debugger/Debugger.Core/ThreadCollection.cs
index 9437a92a9f..500b52aa46 100644
--- a/src/AddIns/Debugger/Debugger.Core/ThreadCollection.cs
+++ b/src/AddIns/Debugger/Debugger.Core/ThreadCollection.cs
@@ -17,6 +17,20 @@ namespace Debugger
set { selected = value; }
}
+ public Thread Find(Predicate predicate)
+ {
+ if (predicate == null)
+ return null;
+
+ foreach (var thread in this)
+ {
+ if (predicate(thread))
+ return thread;
+ }
+
+ return null;
+ }
+
internal bool Contains(ICorDebugThread corThread)
{
foreach(Thread thread in this) {
diff --git a/src/Main/StartUp/Project/Resources/BitmapResources.resources b/src/Main/StartUp/Project/Resources/BitmapResources.resources
index 18662dc382..31f13c839a 100644
Binary files a/src/Main/StartUp/Project/Resources/BitmapResources.resources and b/src/Main/StartUp/Project/Resources/BitmapResources.resources differ