// 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.Specialized; using System.Linq; using System.Windows; using System.Windows.Data; using System.Windows.Input; using System.Windows.Threading; using System.Xml.Serialization; using Debugger; using Debugger.AddIn; using Debugger.AddIn.Pads.Controls; using Debugger.AddIn.TreeModel; using ICSharpCode.Core; using ICSharpCode.Core.Presentation; using ICSharpCode.NRefactory; using ICSharpCode.SharpDevelop.Debugging; using ICSharpCode.SharpDevelop.Project; using Exception = System.Exception; namespace ICSharpCode.SharpDevelop.Gui.Pads { public class WatchPad : DebuggerPad { WatchList watchList; Process debuggedProcess; static WatchPad instance; /// Always check if Instance is null, might be null if pad is not opened! public static WatchPad Instance { get { return instance; } } public WatchList WatchList { get { return watchList; } } public WatchPad() { instance = this; } public Process Process { get { return debuggedProcess; } } protected override void InitializeComponents() { watchList = new WatchList(WatchListType.Watch); watchList.ContextMenu = MenuService.CreateContextMenu(this, "/SharpDevelop/Pads/WatchPad/ContextMenu"); watchList.MouseDoubleClick += WatchListDoubleClick; watchList.WatchItems.CollectionChanged += OnWatchItemsCollectionChanged; panel.Children.Add(watchList); panel.KeyDown += PanelKeyDown; // wire events that influence the items LoadSavedNodes(); ProjectService.SolutionClosed += delegate { watchList.WatchItems.Clear(); }; ProjectService.ProjectAdded += delegate { LoadSavedNodes(); }; ProjectService.SolutionLoaded += delegate { LoadSavedNodes(); }; } #region Saved nodes void LoadSavedNodes() { var props = GetSavedVariablesProperties(); if (props == null) return; foreach (var element in props.Elements) { watchList.WatchItems.Add(new TextNode(null, element, (SupportedLanguage)Enum.Parse(typeof(SupportedLanguage), props[element])).ToSharpTreeNode()); } } void OnWatchItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems.Count > 0) { // add to saved data var data = e.NewItems.OfType().FirstOrDefault(); if (data != null) { var props = GetSavedVariablesProperties(); if (props == null) return; props.Set(data.FullName, data.Language.ToString()); } } if (e.Action == NotifyCollectionChangedAction.Remove) { // remove from saved data var data = e.OldItems.OfType().FirstOrDefault(); if (data != null) { var props = GetSavedVariablesProperties(); if (props == null) return; props.Remove(data.FullName); } } } Properties GetSavedVariablesProperties() { if (ProjectService.CurrentProject == null) return null; if (ProjectService.CurrentProject.ProjectSpecificProperties == null) return null; var props = ProjectService.CurrentProject.ProjectSpecificProperties.Get("watchVars") as Properties; if (props == null) { ProjectService.CurrentProject.ProjectSpecificProperties.Set("watchVars", new Properties()); } return ProjectService.CurrentProject.ProjectSpecificProperties.Get("watchVars") as Properties; } #endregion void PanelKeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Insert) { AddNewWatch(); e.Handled = true; } } void WatchListDoubleClick(object sender, MouseEventArgs e) { if (watchList.SelectedNode == null) { AddNewWatch(); } } void AddNewWatch() { AddWatchCommand command = new AddWatchCommand { Owner = this }; command.Run(); } void ResetPad(object sender, EventArgs e) { string language = "CSharp"; if (ProjectService.CurrentProject != null) language = ProjectService.CurrentProject.Language; // rebuild list var nodes = new List(); foreach (var nod in watchList.WatchItems.OfType()) nodes.Add(new TextNode(null, nod.Node.Name, language == "VB" || language == "VBNet" ? SupportedLanguage.VBNet : SupportedLanguage.CSharp) .ToSharpTreeNode()); watchList.WatchItems.Clear(); foreach (var nod in nodes) watchList.WatchItems.Add(nod); } protected override void SelectProcess(Process process) { if (debuggedProcess != null) { debuggedProcess.Paused -= debuggedProcess_Paused; debuggedProcess.Exited -= ResetPad; } debuggedProcess = process; if (debuggedProcess != null) { debuggedProcess.Paused += debuggedProcess_Paused; debuggedProcess.Exited += ResetPad; } InvalidatePad(); } void debuggedProcess_Paused(object sender, ProcessEventArgs e) { InvalidatePad(); } TreeNodeWrapper UpdateNode(TreeNodeWrapper node) { try { LoggingService.Info("Evaluating: " + (string.IsNullOrEmpty(node.Node.Name) ? "is null or empty!" : node.Node.Name)); var nodExpression = debugger.GetExpression(node.Node.Name); //Value val = ExpressionEvaluator.Evaluate(nod.Name, nod.Language, debuggedProcess.SelectedStackFrame); ExpressionNode valNode = new ExpressionNode(null, null, node.Node.Name, nodExpression); return valNode.ToSharpTreeNode(); } catch (GetValueException) { string error = String.Format(StringParser.Parse("${res:MainWindow.Windows.Debug.Watch.InvalidExpression}"), node.Node.Name); ErrorInfoNode infoNode = new ErrorInfoNode(node.Node.Name, error); return infoNode.ToSharpTreeNode(); } } protected override void RefreshPad() { if (debuggedProcess == null || debuggedProcess.IsRunning) return; using(new PrintTimes("Watch Pad refresh")) { var nodes = watchList.WatchItems.OfType().ToArray(); watchList.WatchItems.Clear(); debuggedProcess.EnqueueForEach( Dispatcher.CurrentDispatcher, nodes, n => watchList.WatchItems.Add(UpdateNode(n)) ); } } } }