From 53dbd13ee5bf15e4cd1f2ccebb4ae392becbed21 Mon Sep 17 00:00:00 2001 From: Eusebiu Marcu Date: Tue, 9 Aug 2011 01:36:55 +0200 Subject: [PATCH] Setup infrastructure to save project specific data. Save watch variables. Fix http://bugtracker.sharpdevelop.net/issue/ViewIssue.aspx?id=1509&PROJID=4 --- data/resources/StringResources.resx | 2 +- .../Debugger/Debugger.AddIn/Pads/WatchPad.cs | 92 ++++++++++++++++++- .../Debugger.AddIn/Pads/WatchPadModel.cs | 40 +++++++- .../Project/ICSharpCode.SharpDevelop.csproj | 4 + .../Project/Src/Project/AbstractProject.cs | 15 +++ .../SavedData/IProjectSavedData.cs | 60 ++++++++++++ .../SavedData/ProjectSavedDataConverter.cs | 54 +++++++++++ .../SavedData/SavedDataManager.cs | 81 ++++++++++++++++ 8 files changed, 343 insertions(+), 5 deletions(-) create mode 100644 src/Main/Base/Project/Src/Services/ProjectService/SavedData/IProjectSavedData.cs create mode 100644 src/Main/Base/Project/Src/Services/ProjectService/SavedData/ProjectSavedDataConverter.cs create mode 100644 src/Main/Base/Project/Src/Services/ProjectService/SavedData/SavedDataManager.cs diff --git a/data/resources/StringResources.resx b/data/resources/StringResources.resx index cf578a4f55..3dd6afa578 100644 --- a/data/resources/StringResources.resx +++ b/data/resources/StringResources.resx @@ -5156,7 +5156,7 @@ Goto 'Options->Visual Style' and change the current language ambience.ASP.NET/IIS (Express) worker process ({0}) was not found. - There's no Project Url specified or external program. Check the web server at Project Properties -> Debug tab. + There's no Project Url specified or external program. Check the web server at Project Properties -> Debug tab. Server port: diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPad.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPad.cs index 1fb2b894af..299fa04985 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPad.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPad.cs @@ -3,6 +3,8 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; using System.Windows; using System.Windows.Input; @@ -13,9 +15,10 @@ using Debugger.AddIn.TreeModel; using ICSharpCode.Core; using ICSharpCode.Core.Presentation; using ICSharpCode.NRefactory; +using ICSharpCode.SharpDevelop.Debugging; using ICSharpCode.SharpDevelop.Project; +using ICSharpCode.SharpDevelop.Project.SavedData; using Exception = System.Exception; -using ICSharpCode.SharpDevelop.Debugging; namespace ICSharpCode.SharpDevelop.Gui.Pads { @@ -58,11 +61,98 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads watchList.Drop += watchList_Drop; watchList.MouseDoubleClick += watchList_DoubleClick; watchList.KeyUp += watchList_KeyUp; + watchList.WatchItems.CollectionChanged += OnWatchItemsCollectionChanged; panel.Children.Add(watchList); panel.KeyUp += new KeyEventHandler(panel_KeyUp); + + // wire events that influence the items + LoadSavedNodes(); + ProjectService.SolutionClosed += delegate { watchList.WatchItems.Clear(); }; + ProjectService.SolutionPreferencesSaving += OnSolutionClosing; + ProjectService.ProjectRemoved += OnProjectRemoved; + ProjectService.ProjectAdded += OnProjectAdded; + ProjectService.SolutionLoaded += delegate { LoadSavedNodes(); }; + } + + #region Saved nodes + + void AddNodes(Func predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + // get nodes of current projects + List temp = new List(); + foreach (var data in SavedDataManager.GetSavedData().Where(predicate)) { + string[] v = data.SavedString.Split('|'); + + TextNode node = new TextNode(null, v[5], (SupportedLanguage)Enum.Parse(typeof(SupportedLanguage), v[7])) { ProjectName = data.ProjectName }; + temp.Add(node); + } + + // add them to watch list + temp.ForEach(d => { if (!watchList.WatchItems.Contains(d)) watchList.WatchItems.Add(d); } ); + } + + void LoadSavedNodes() + { + AddNodes(d => d.SavedDataType == ProjectSavedDataType.WatchVariables && + ProjectService.OpenSolution.Projects.Any(p => p.Name == d.ProjectName)); + + // remove them temporarilly - they will be saved on exit + SavedDataManager.RemoveAll(d => d.SavedDataType == ProjectSavedDataType.WatchVariables && + ProjectService.OpenSolution.Projects.Any(p => p.Name == d.ProjectName)); + } + + void OnSolutionClosing(object sender, SolutionEventArgs e) + { + foreach (var element in watchList.WatchItems) { + SavedDataManager.Add((IProjectSavedData)element); + } + } + + void OnProjectAdded(object sender, ProjectEventArgs e) + { + AddNodes(d => d.SavedDataType == ProjectSavedDataType.WatchVariables && e.Project.Name == d.ProjectName); + } + + void OnProjectRemoved(object sender, ProjectEventArgs e) + { + if (e.Project == null) + return; + + // get the specific nodes from the list + List nodes = new List(); + foreach (var element in watchList.WatchItems + .OfType() + .Where(tn => tn.ProjectName == e.Project.Name)) { + nodes.Add(element); + } + + // remove nodes from the list + nodes.ForEach(n => watchList.WatchItems.Remove(n)); } + void OnWatchItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (e.Action == NotifyCollectionChangedAction.Add) { + // add to saved data + var data = e.NewItems[0] as IProjectSavedData; + if (data != null) + SavedDataManager.Add(data); + } + + if (e.Action == NotifyCollectionChangedAction.Remove) { + // remove from saved data + var data = e.OldItems[0] as IProjectSavedData; + if (data != null) + SavedDataManager.Remove(data); + } + } + + #endregion + void panel_KeyUp(object sender, KeyEventArgs e) { if (e.Key == Key.Insert) { diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPadModel.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPadModel.cs index 14365919db..f599aa0e12 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPadModel.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPadModel.cs @@ -2,20 +2,24 @@ // 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.Text; using Debugger.AddIn.TreeModel; using ICSharpCode.NRefactory; -using ICSharpCode.SharpDevelop.Debugging; +using ICSharpCode.SharpDevelop.Project; +using ICSharpCode.SharpDevelop.Project.SavedData; namespace ICSharpCode.SharpDevelop.Gui.Pads { - public class TextNode : TreeNode, ISetText + public class TextNode : TreeNode, ISetText, IProjectSavedData { public TextNode(TreeNode parent, string text, SupportedLanguage language) : base(parent) { this.Name = text; this.Language = language; + + if (ProjectService.CurrentProject != null) + ProjectName = ProjectService.CurrentProject.Name; } public override bool CanSetText { @@ -37,6 +41,36 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads } public SupportedLanguage Language { get; set; } + + #region IProjectSavedData implementation + + string savedString; + + public string SavedString { + get { + StringBuilder sb = new StringBuilder(); + sb.Append("ProjectName"); sb.Append('|'); sb.Append(ProjectName); sb.Append('|'); + sb.Append("ProjectSavedDataType"); sb.Append('|'); sb.Append(SavedDataType); sb.Append('|'); + sb.Append("FullName"); sb.Append('|'); sb.Append(FullName); sb.Append('|'); + sb.Append("Language"); sb.Append('|'); sb.Append(Language.ToString()); + + savedString = sb.ToString(); + return savedString; + } + set { savedString = value; } + } + + public ProjectSavedDataType SavedDataType { + get { + return ProjectSavedDataType.WatchVariables; + } + } + + public string ProjectName { + get; internal set; + } + + #endregion } public class ErrorInfoNode : ICorDebug.InfoNode diff --git a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj index 9518e0dcff..a294f06954 100644 --- a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj +++ b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj @@ -243,6 +243,9 @@ SelectCulturePanel.xaml Code + + + WebProjectOptionsPanel.xaml @@ -848,6 +851,7 @@ + diff --git a/src/Main/Base/Project/Src/Project/AbstractProject.cs b/src/Main/Base/Project/Src/Project/AbstractProject.cs index 4655f7dce5..9da00fb957 100644 --- a/src/Main/Base/Project/Src/Project/AbstractProject.cs +++ b/src/Main/Base/Project/Src/Project/AbstractProject.cs @@ -7,12 +7,14 @@ using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.IO; +using System.Linq; using System.Xml.Linq; using ICSharpCode.Core; using ICSharpCode.SharpDevelop.Debugging; using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Gui.OptionPanels; +using ICSharpCode.SharpDevelop.Project.SavedData; namespace ICSharpCode.SharpDevelop.Project { @@ -71,6 +73,7 @@ namespace ICSharpCode.SharpDevelop.Project { WorkbenchSingleton.AssertMainThread(); + // breakpoints and files Properties properties = new Properties(); properties.Set("bookmarks", ICSharpCode.SharpDevelop.Bookmarks.BookmarkManager.GetProjectBookmarks(this).ToArray()); List files = new List(); @@ -81,10 +84,15 @@ namespace ICSharpCode.SharpDevelop.Project } properties.Set("files", files.ToArray()); + // web project properties var webOptions = WebProjectsOptions.Instance.GetWebProjectOptions(Name); if (webOptions != null) properties.Set("WebProjectOptions", webOptions); + // other project data - logic in ProjectSavedDataConverter + properties.Set("projectSavedData_" + Name, SavedDataManager.GetSavedData() + .Where(d => d.ProjectName == Name).ToArray()); + return properties; } @@ -99,7 +107,14 @@ namespace ICSharpCode.SharpDevelop.Project filesToOpenAfterSolutionLoad.Add(fileName); } + // web project properties WebProjectsOptions.Instance.SetWebProjectOptions(Name, memento.Get("WebProjectOptions", new WebProjectOptions()) as WebProjectOptions); + + // other project data - logic in ProjectSavedDataConverter + foreach(var data in memento.Get("projectSavedData_" + Name, new IProjectSavedData[0]) + .Where(d => d.ProjectName == Name)) { + SavedDataManager.Add(data); + } } #endregion diff --git a/src/Main/Base/Project/Src/Services/ProjectService/SavedData/IProjectSavedData.cs b/src/Main/Base/Project/Src/Services/ProjectService/SavedData/IProjectSavedData.cs new file mode 100644 index 0000000000..f595ed5f8a --- /dev/null +++ b/src/Main/Base/Project/Src/Services/ProjectService/SavedData/IProjectSavedData.cs @@ -0,0 +1,60 @@ +// 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.ComponentModel; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Project.SavedData +{ + public enum ProjectSavedDataType + { + WatchVariables + } + + /// + /// Interface for storing project specific data. + /// When implementing this, one should be carefull when and how + /// the SavedDataManager is used in order to not alter the other data. + /// + [TypeConverter(typeof(ProjectSavedDataConverter))] + public interface IProjectSavedData + { + /// + /// Saved data type. + /// + ProjectSavedDataType SavedDataType { get; } + + /// + /// Saved data. + /// The format is: "ProjectName"|{0}|"ProjectSavedDataType"|{1}|(specific data splited by '|'). + /// + string SavedString { get; set; } + + /// + /// Gets the project name. + /// + string ProjectName { get; } + } + + /// + /// Dummy data. Used to map the saved data and exposed to addins where project specific data can exist. + /// + public sealed class DummyProjectSavedData : IProjectSavedData + { + public ProjectSavedDataType SavedDataType { get; set; } + + public string SavedString { get; set; } + + public string ProjectName { + get { + if (string.IsNullOrEmpty(SavedString)) + return string.Empty; + + string[] v = SavedString.Split('|'); + + return v[1]; + } + } + } +} diff --git a/src/Main/Base/Project/Src/Services/ProjectService/SavedData/ProjectSavedDataConverter.cs b/src/Main/Base/Project/Src/Services/ProjectService/SavedData/ProjectSavedDataConverter.cs new file mode 100644 index 0000000000..8256ea7b43 --- /dev/null +++ b/src/Main/Base/Project/Src/Services/ProjectService/SavedData/ProjectSavedDataConverter.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.ComponentModel; +using System.Globalization; +using System.Text; + +namespace ICSharpCode.SharpDevelop.Project.SavedData +{ + public sealed class ProjectSavedDataConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof(string)) { + return true; + } else { + return base.CanConvertFrom(context, sourceType); + } + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + // convert from saved data(string) to objects + if (value is string) { + string[] v = ((string)value).Split('|'); + ProjectSavedDataType type = (ProjectSavedDataType)Enum.Parse(typeof(ProjectSavedDataType), v[3]); + var data = new DummyProjectSavedData { + SavedString = (string)value, + SavedDataType = type + }; + return data; + } else { + return base.ConvertFrom(context, culture, value); + } + } + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + { + // convert objects to saved data(string) + var data = value as IProjectSavedData; + if (destinationType == typeof(string) && data != null) { + switch (data.SavedDataType) { + case ProjectSavedDataType.WatchVariables: + return data.SavedString; + default: + throw new Exception("Invalid value for ProjectSavedDataType"); + } + } else { + return base.ConvertTo(context, culture, value, destinationType); + } + } + } +} diff --git a/src/Main/Base/Project/Src/Services/ProjectService/SavedData/SavedDataManager.cs b/src/Main/Base/Project/Src/Services/ProjectService/SavedData/SavedDataManager.cs new file mode 100644 index 0000000000..2d7c6e735b --- /dev/null +++ b/src/Main/Base/Project/Src/Services/ProjectService/SavedData/SavedDataManager.cs @@ -0,0 +1,81 @@ +// 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.Concurrent; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Project.SavedData +{ + /// + /// Stores the project specific data. + /// + public static class SavedDataManager + { + static readonly List list; + static object _syncObject = new object(); + + static SavedDataManager() + { + list = new List(); + ProjectService.SolutionClosed += delegate { list.Clear(); }; + } + + /// + /// Gets all saved data. + /// + /// + public static List GetSavedData() + { + return list; + } + + /// + /// Adds a new data. + /// + /// + public static void Add(IProjectSavedData data) + { + if (data == null) + throw new ArgumentNullException("data"); + + if (!list.Contains(data)) { + lock (_syncObject) { + if (!list.Contains(data)) + list.Add(data); + } + } + } + + /// + /// Removes data. + /// + /// + public static void Remove(IProjectSavedData data) + { + if (data == null) + throw new ArgumentNullException("data"); + + if (list.Contains(data)) { + lock (_syncObject) { + if (list.Contains(data)) + list.Remove(data); + } + } + } + + /// + /// Removes all data that satisfies a predicate. + /// + /// + public static void RemoveAll(Predicate match) + { + if (match == null) + throw new ArgumentNullException("match"); + + lock (_syncObject) { + list.RemoveAll(match); + } + } + } +}