// 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 System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Debugger;
using Debugger.AddIn.TreeModel;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Gui.Pads;
using ICSharpCode.SharpDevelop.Services;
namespace ICSharpCode.SharpDevelop.Gui.Pads
{
///
/// Interaction logic for CallStackPadContent.xaml
///
public partial class CallStackPadContent : UserControl
{
CallStackPad callStackPad;
Process debuggedProcess;
public CallStackPadContent(CallStackPad pad)
{
this.callStackPad = pad;
InitializeComponent();
view.ContextMenu = CreateMenu();
((GridView)view.View).Columns[0].Width = DebuggingOptions.Instance.ShowModuleNames ? 100d : 0d;
((GridView)view.View).Columns[2].Width = DebuggingOptions.Instance.ShowLineNumbers ? 50d : 0d;
}
ContextMenu CreateMenu()
{
MenuItem extMethodsItem = new MenuItem();
extMethodsItem.Header = ResourceService.GetString("MainWindow.Windows.Debug.CallStack.ShowExternalMethods");
extMethodsItem.IsChecked = DebuggingOptions.Instance.ShowExternalMethods;
extMethodsItem.Click += delegate {
extMethodsItem.IsChecked = DebuggingOptions.Instance.ShowExternalMethods = !DebuggingOptions.Instance.ShowExternalMethods;
callStackPad.InvalidatePad();
};
MenuItem moduleItem = new MenuItem();
moduleItem.Header = ResourceService.GetString("MainWindow.Windows.Debug.CallStack.ShowModuleNames");
moduleItem.IsChecked = DebuggingOptions.Instance.ShowModuleNames;
moduleItem.Click += delegate {
moduleItem.IsChecked = DebuggingOptions.Instance.ShowModuleNames = !DebuggingOptions.Instance.ShowModuleNames;
((GridView)view.View).Columns[0].Width = DebuggingOptions.Instance.ShowModuleNames ? 100d : 0d;
callStackPad.InvalidatePad();
};
MenuItem argNamesItem = new MenuItem();
argNamesItem.Header = ResourceService.GetString("MainWindow.Windows.Debug.CallStack.ShowArgumentNames");
argNamesItem.IsChecked = DebuggingOptions.Instance.ShowArgumentNames;
argNamesItem.Click += delegate {
argNamesItem.IsChecked = DebuggingOptions.Instance.ShowArgumentNames = !DebuggingOptions.Instance.ShowArgumentNames;
callStackPad.InvalidatePad();
};
MenuItem argValuesItem = new MenuItem();
argValuesItem.Header = ResourceService.GetString("MainWindow.Windows.Debug.CallStack.ShowArgumentValues");
argValuesItem.IsChecked = DebuggingOptions.Instance.ShowArgumentValues;
argValuesItem.Click += delegate {
argValuesItem.IsChecked = DebuggingOptions.Instance.ShowArgumentValues = !DebuggingOptions.Instance.ShowArgumentValues;
callStackPad.InvalidatePad();
};
MenuItem lineItem = new MenuItem();
lineItem.Header = ResourceService.GetString("MainWindow.Windows.Debug.CallStack.ShowLineNumber");
lineItem.IsChecked = DebuggingOptions.Instance.ShowLineNumbers;
lineItem.Click += delegate {
lineItem.IsChecked = DebuggingOptions.Instance.ShowLineNumbers = !DebuggingOptions.Instance.ShowLineNumbers;
((GridView)view.View).Columns[2].Width = DebuggingOptions.Instance.ShowLineNumbers ? 50d : 0d;
callStackPad.InvalidatePad();
};
return new ContextMenu() {
Items = {
extMethodsItem,
new Separator(),
moduleItem,
argNamesItem,
argValuesItem,
lineItem
}
};
}
public void SelectProcess(Process process)
{
if (debuggedProcess != null) {
debuggedProcess.Paused -= debuggedProcess_Paused;
}
debuggedProcess = process;
if (debuggedProcess != null) {
debuggedProcess.Paused += debuggedProcess_Paused;
}
callStackPad.InvalidatePad();
}
void debuggedProcess_Paused(object sender, ProcessEventArgs e)
{
callStackPad.InvalidatePad();
}
void View_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (debuggedProcess == null)
return;
if (debuggedProcess.IsPaused) {
CallStackItem item = view.SelectedItem as CallStackItem;
if (item == null)
return;
if (item.Frame != null && debuggedProcess.SelectedThread != null) {
// check for options - if these options are enabled, selecting the frame should not continue
if (!item.Frame.HasSymbols && !debuggedProcess.Options.DecompileCodeWithoutSymbols) {
MessageService.ShowMessage("${res:MainWindow.Windows.Debug.CallStack.CannotSwitchWithoutSymbolsOrDecompiledCodeOptions}",
"${res:MainWindow.Windows.Debug.CallStack.FunctionSwitch}");
return;
}
debuggedProcess.SelectedThread.SelectedStackFrame = item.Frame;
debuggedProcess.PauseSession.PausedReason = PausedReason.CurrentFunctionChanged;
debuggedProcess.OnPaused(); // Force refresh of pads - artificial pause
}
} else {
MessageService.ShowMessage("${res:MainWindow.Windows.Debug.CallStack.CannotSwitchWhileRunning}", "${res:MainWindow.Windows.Debug.CallStack.FunctionSwitch}");
}
}
void View_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter) {
View_MouseLeftButtonUp(sender, null);
e.Handled = true;
}
}
internal void RefreshPad()
{
if (debuggedProcess == null || debuggedProcess.IsRunning || debuggedProcess.SelectedThread == null) {
view.ItemsSource = null;
return;
}
var items = new ObservableCollection();
using(new PrintTimes("Callstack refresh")) {
bool showExternalMethods = DebuggingOptions.Instance.ShowExternalMethods;
bool previousItemIsExternalMethod = false;
debuggedProcess.EnqueueForEach(
Dispatcher,
debuggedProcess.SelectedThread.GetCallstack(100),
f => items.AddIfNotNull(CreateItem(f, showExternalMethods, ref previousItemIsExternalMethod))
);
}
view.ItemsSource = items;
}
CallStackItem CreateItem(StackFrame frame, bool showExternalMethods, ref bool previousItemIsExternalMethod)
{
CallStackItem item;
// line number
string lineNumber = string.Empty;
if (DebuggingOptions.Instance.ShowLineNumbers) {
if (frame.NextStatement != null)
lineNumber = frame.NextStatement.StartLine.ToString();
}
// show modules names
string moduleName = string.Empty;
if (DebuggingOptions.Instance.ShowModuleNames) {
moduleName = frame.MethodInfo.DebugModule.ToString();
}
if (frame.HasSymbols || showExternalMethods) {
// Show the method in the list
item = new CallStackItem() {
Name = GetFullName(frame), Language = "", Line = lineNumber, ModuleName = moduleName
};
previousItemIsExternalMethod = false;
item.Frame = frame;
} else {
// Show [External methods] in the list
if (previousItemIsExternalMethod) return null;
item = new CallStackItem() {
Name = ResourceService.GetString("MainWindow.Windows.Debug.CallStack.ExternalMethods"),
Language = ""
};
previousItemIsExternalMethod = true;
}
return item;
}
internal static string GetFullName(StackFrame frame)
{
bool showArgumentNames = DebuggingOptions.Instance.ShowArgumentNames;
bool showArgumentValues = DebuggingOptions.Instance.ShowArgumentValues;
bool showLineNumber = DebuggingOptions.Instance.ShowLineNumbers;
bool showModuleNames = DebuggingOptions.Instance.ShowModuleNames;
StringBuilder name = new StringBuilder();
name.Append(frame.MethodInfo.DeclaringType.FullName);
name.Append('.');
name.Append(frame.MethodInfo.Name);
if (showArgumentNames || showArgumentValues) {
name.Append("(");
for (int i = 0; i < frame.ArgumentCount; i++) {
string parameterName = null;
string argValue = null;
if (showArgumentNames) {
try {
parameterName = frame.MethodInfo.GetParameters()[i].Name;
} catch { }
if (parameterName == "") parameterName = null;
}
if (showArgumentValues) {
try {
argValue = frame.GetArgumentValue(i).AsString(100);
} catch { }
}
if (parameterName != null && argValue != null) {
name.Append(parameterName);
name.Append("=");
name.Append(argValue);
}
if (parameterName != null && argValue == null) {
name.Append(parameterName);
}
if (parameterName == null && argValue != null) {
name.Append(argValue);
}
if (parameterName == null && argValue == null) {
name.Append(ResourceService.GetString("Global.NA"));
}
if (i < frame.ArgumentCount - 1) {
name.Append(", ");
}
}
name.Append(")");
}
return name.ToString();
}
}
public class CallStackItem
{
public string Name { get; set; }
public string Language { get; set; }
public StackFrame Frame { get; set; }
public string Line { get; set; }
public string ModuleName { get; set; }
public Brush FontColor {
get { return Frame == null || Frame.HasSymbols ? Brushes.Black : Brushes.Gray; }
}
}
public class CallStackPad : DebuggerPad
{
CallStackPadContent callStackList;
static CallStackPad instance;
public static CallStackPad Instance {
get { return instance; }
}
public CallStackPad()
{
instance = this;
}
public override object Control {
get {
return callStackList;
}
}
protected override void InitializeComponents()
{
callStackList = new CallStackPadContent(this);
}
protected override void SelectProcess(Process process)
{
callStackList.SelectProcess(process);
}
protected override void RefreshPad()
{
callStackList.RefreshPad();
}
}
}