//
//
//
// $Revision$
//
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Debugger.Expressions;
using Debugger.MetaData;
using ICSharpCode.Core;
using ICSharpCode.Core.WinForms;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Gui.Pads;
using ICSharpCode.SharpDevelop.Services;
namespace Debugger.AddIn.TreeModel
{
///
/// Represents the data in a row in a TreeViewNode.
///
public class ValueNode: AbstractNode, ISetText, IContextMenu
{
Expression expression;
bool canSetText;
public Expression Expression {
get { return expression; }
}
/// HACK for WatchPad
public void SetName(string name)
{
this.Name = name;
}
///
/// Factory method to create an instance.
///
/// The expression containing the value you wish to display.
///
/// Returns a ValueNode if it can successfully evaluate expression or
/// ErrorNode if it fails to do so.
///
///
public static AbstractNode Create(Expression expression)
{
try {
Value val = expression.Evaluate(WindowsDebugger.DebuggedProcess);
return new ValueNode(val);
} catch (GetValueException e) {
return new ErrorNode(expression, e);
}
}
///
/// Constructor used by the factory method Create()
///
///
///
/// Can be thrown by InvokeToString()
///
public ValueNode(Value val)
{
this.expression = val.Expression;
canSetText = false;
if (val.Type.IsInteger) {
canSetText =
(val.Expression is LocalVariableIdentifierExpression) ||
(val.Expression is ParameterIdentifierExpression) ||
(val.Expression is ArrayIndexerExpression) ||
(val.Expression is MemberReferenceExpression && ((MemberReferenceExpression)val.Expression).MemberInfo is FieldInfo);
}
this.Image = IconService.GetBitmap("Icons.16x16." + GetImageName(val));
this.Name = val.Expression.CodeTail;
if (DebuggingOptions.Instance.ShowValuesInHexadecimal && val.Type.IsInteger) {
this.Text = String.Format("0x{0:X}", val.PrimitiveValue);
} else if (val.Type.IsPointer) {
this.Text = String.Format("0x{0:X}", val.PointerAddress);
} else {
this.Text = val.AsString;
}
this.Text = (this.Text.Length > 256) ? this.Text.Substring(0, 256) + "..." : this.Text;
if (val.Type != null) {
this.Type = val.Type.Name;
} else {
this.Type = String.Empty;
}
// Note that these return enumerators so they are lazy-evaluated
this.ChildNodes = null;
if (val.IsNull) {
} else if (val.Type.IsClass || val.Type.IsValueType) {
this.ChildNodes = Utils.GetChildNodesOfObject(this.Expression, val.Type);
} else if (val.Type.IsArray) {
this.ChildNodes = Utils.GetChildNodesOfArray(this.Expression, val.ArrayDimensions);
} else if (val.Type.IsPointer) {
Value deRef = val.Dereference();
if (deRef != null) {
this.ChildNodes = new AbstractNode [] { new ValueNode(deRef) };
}
}
if (DebuggingOptions.Instance.ICorDebugVisualizerEnabled) {
AbstractNode info = ICorDebug.GetDebugInfoRoot(val.Process, val.CorValue);
this.ChildNodes = PrependNode(info, this.ChildNodes);
}
// Do last since it may expire the object
if ((val.Type.IsClass || val.Type.IsValueType) && !val.IsNull) {
this.Text = val.InvokeToString();
}
}
IEnumerable PrependNode(AbstractNode node, IEnumerable rest)
{
yield return node;
if (rest != null) {
foreach(AbstractNode absNode in rest) {
yield return absNode;
}
}
}
public bool CanSetText {
get {
return canSetText;
}
}
public bool SetText(string newText)
{
Value val = null;
try {
val = this.Expression.Evaluate(WindowsDebugger.DebuggedProcess.SelectedStackFrame);
if (val.Type.IsInteger && newText.StartsWith("0x")) {
try {
val.PrimitiveValue = long.Parse(newText.Substring(2), NumberStyles.HexNumber);
} catch (FormatException) {
throw new NotSupportedException();
} catch (OverflowException) {
throw new NotSupportedException();
}
} else {
val.PrimitiveValue = newText;
}
this.Text = newText;
return true;
} catch (NotSupportedException) {
string format = ResourceService.GetString("MainWindow.Windows.Debug.LocalVariables.CannotSetValue.BadFormat");
string msg = String.Format(format, newText, val.Type.PrimitiveType.ToString());
MessageService.ShowMessage(msg ,"${res:MainWindow.Windows.Debug.LocalVariables.CannotSetValue.Title}");
} catch (COMException) {
// COMException (0x80131330): Cannot perfrom SetValue on non-leaf frames.
// Happens if trying to set value after exception is breaked
MessageService.ShowMessage("${res:MainWindow.Windows.Debug.LocalVariables.CannotSetValue.UnknownError}",
"${res:MainWindow.Windows.Debug.LocalVariables.CannotSetValue.Title}");
}
return false;
}
string GetImageName(Value val)
{
Expression expr = val.Expression;
if (expr is ThisReferenceExpression) {
if (val.Type.IsClass) {
return "Class";
}
if (val.Type.IsValueType) {
return "Struct";
}
}
if (expr is ParameterIdentifierExpression) {
return "Parameter";
}
if (expr is MemberReferenceExpression) {
MemberInfo memberInfo = ((MemberReferenceExpression)expr).MemberInfo;
string prefix;
if (memberInfo.IsPublic) {
prefix = "";
} else if (memberInfo.IsInternal) {
prefix = "Internal";
} else if (memberInfo.IsProtected) {
prefix = "Protected";
} else if (memberInfo.IsPrivate) {
prefix = "Private";
} else {
prefix = "";
}
if (memberInfo is FieldInfo) {
return prefix + "Field";
}
if (memberInfo is PropertyInfo) {
return prefix + "Property";
}
if (memberInfo is MethodInfo) {
return prefix + "Method";
}
}
if (expr is LocalVariableIdentifierExpression) {
return "Local";
}
if (expr is ArrayIndexerExpression) {
return "Field";
}
return "Field";
}
public ContextMenuStrip GetContextMenu()
{
ContextMenuStrip menu = new ContextMenuStrip();
ToolStripMenuItem copyItem;
copyItem = new ToolStripMenuItem();
copyItem.Text = ResourceService.GetString("MainWindow.Windows.Debug.LocalVariables.CopyToClipboard");
copyItem.Checked = false;
copyItem.Click += delegate {
ClipboardWrapper.SetText(this.Text);
};
ToolStripMenuItem hexView;
hexView = new ToolStripMenuItem();
hexView.Text = ResourceService.GetString("MainWindow.Windows.Debug.LocalVariables.ShowInHexadecimal");
hexView.Checked = DebuggingOptions.Instance.ShowValuesInHexadecimal;
hexView.Click += delegate {
// refresh all pads that use ValueNode for display
DebuggingOptions.Instance.ShowValuesInHexadecimal = !DebuggingOptions.Instance.ShowValuesInHexadecimal;
// always check if instance is null, might be null if pad is not opened
if (LocalVarPad.Instance != null)
LocalVarPad.Instance.RefreshPad();
if (WatchPad.Instance != null)
WatchPad.Instance.RefreshPad();
};
menu.Items.AddRange(new ToolStripItem[] {
copyItem,
hexView
});
return menu;
}
public static WindowsDebugger WindowsDebugger {
get {
return (WindowsDebugger)DebuggerService.CurrentDebugger;
}
}
}
}