From ae5b6342f16e05dd8ecfa6628dff43ab7d5d2811 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 10 Aug 2011 17:49:42 +0200 Subject: [PATCH] add ProjectChangeWatcher for Solutions --- .../Project/ICSharpCode.SharpDevelop.csproj | 1 + .../Templates/Project/ProjectTemplate.cs | 5 +- .../Templates/Project/SolutionDescriptor.cs | 21 +-- .../Src/Project/ProjectChangeWatcher.cs | 133 ++++++++++++++++++ .../Project/Src/Project/Solution/Solution.cs | 15 +- .../Services/ProjectService/ProjectService.cs | 2 +- .../WebReferences/WebReferenceTestHelper.cs | 2 +- 7 files changed, 162 insertions(+), 17 deletions(-) create mode 100644 src/Main/Base/Project/Src/Project/ProjectChangeWatcher.cs diff --git a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj index 9518e0dcff..ba84eabbcd 100644 --- a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj +++ b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj @@ -307,6 +307,7 @@ + diff --git a/src/Main/Base/Project/Src/Internal/Templates/Project/ProjectTemplate.cs b/src/Main/Base/Project/Src/Internal/Templates/Project/ProjectTemplate.cs index f16e12e80f..2e8d75c0e7 100644 --- a/src/Main/Base/Project/Src/Internal/Templates/Project/ProjectTemplate.cs +++ b/src/Main/Base/Project/Src/Internal/Templates/Project/ProjectTemplate.cs @@ -302,9 +302,10 @@ namespace ICSharpCode.SharpDevelop.Internal.Templates } else if (projectDescriptor != null) { bool createNewSolution = projectCreateInformation.Solution == null; if (createNewSolution) { - projectCreateInformation.Solution = new Solution(); + string fileName = Path.Combine(projectCreateInformation.SolutionPath, projectCreateInformation.SolutionName + ".sln"); + projectCreateInformation.Solution = new Solution(new ProjectChangeWatcher(fileName)); projectCreateInformation.Solution.Name = projectCreateInformation.SolutionName; - projectCreateInformation.Solution.FileName = Path.Combine(projectCreateInformation.SolutionPath, projectCreateInformation.SolutionName + ".sln"); + projectCreateInformation.Solution.FileName = fileName; } IProject project = projectDescriptor.CreateProject(projectCreateInformation, this.languagename); if (project != null) { diff --git a/src/Main/Base/Project/Src/Internal/Templates/Project/SolutionDescriptor.cs b/src/Main/Base/Project/Src/Internal/Templates/Project/SolutionDescriptor.cs index 64f9691b01..f72d24ec49 100644 --- a/src/Main/Base/Project/Src/Internal/Templates/Project/SolutionDescriptor.cs +++ b/src/Main/Base/Project/Src/Internal/Templates/Project/SolutionDescriptor.cs @@ -90,15 +90,6 @@ namespace ICSharpCode.SharpDevelop.Internal.Templates public string CreateSolution(ProjectCreateInformation projectCreateInformation, string defaultLanguage) { - Solution newSolution = new Solution(); - projectCreateInformation.Solution = newSolution; - - string newSolutionName = StringParser.Parse(name, new string[,] { - {"ProjectName", projectCreateInformation.SolutionName} - }); - - newSolution.Name = newSolutionName; - string oldSolutionPath = projectCreateInformation.SolutionPath; string oldProjectPath = projectCreateInformation.ProjectBasePath; if (relativeDirectory != null && relativeDirectory.Length > 0 && relativeDirectory != ".") { @@ -115,12 +106,22 @@ namespace ICSharpCode.SharpDevelop.Internal.Templates projectCreateInformation.SolutionPath = oldSolutionPath; projectCreateInformation.ProjectBasePath = oldProjectPath; + string newSolutionName = StringParser.Parse(name, new string[,] { + {"ProjectName", projectCreateInformation.SolutionName} + }); + + string solutionLocation = Path.Combine(projectCreateInformation.SolutionPath, newSolutionName + ".sln"); + + Solution newSolution = new Solution(new ProjectChangeWatcher(solutionLocation)); + projectCreateInformation.Solution = newSolution; + + newSolution.Name = newSolutionName; + if (!mainFolder.AddContents(newSolution, projectCreateInformation, defaultLanguage, newSolution)) { newSolution.Dispose(); return null; } - string solutionLocation = Path.Combine(projectCreateInformation.SolutionPath, newSolutionName + ".sln"); // Save solution if (File.Exists(solutionLocation)) { diff --git a/src/Main/Base/Project/Src/Project/ProjectChangeWatcher.cs b/src/Main/Base/Project/Src/Project/ProjectChangeWatcher.cs new file mode 100644 index 0000000000..b38cce950e --- /dev/null +++ b/src/Main/Base/Project/Src/Project/ProjectChangeWatcher.cs @@ -0,0 +1,133 @@ +// 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.IO; +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop.Gui; + +namespace ICSharpCode.SharpDevelop.Project +{ + public sealed class ProjectChangeWatcher : IDisposable + { + FileSystemWatcher watcher; + string fileName; + bool enabled = true; + + public ProjectChangeWatcher(string fileName) + { + this.fileName = fileName; + WorkbenchSingleton.MainWindow.Activated += MainFormActivated; + } + + public void Enable() + { + enabled = true; + SetWatcher(); + } + + public void Disable() + { + enabled = false; + SetWatcher(); + } + + public void Rename(string newFileName) + { + fileName = newFileName; + } + + void SetWatcher() + { + WorkbenchSingleton.AssertMainThread(); + + if (watcher != null) { + watcher.EnableRaisingEvents = false; + } + + if (!enabled) + return; + + if (string.IsNullOrEmpty(fileName)) + return; + if (FileUtility.IsUrl(fileName)) + return; + if (!Path.IsPathRooted(fileName)) + return; + + try { + if (watcher == null) { + watcher = new FileSystemWatcher(); + if (WorkbenchSingleton.Workbench != null) + watcher.SynchronizingObject = WorkbenchSingleton.Workbench.SynchronizingObject; + watcher.Changed += OnFileChangedEvent; + watcher.Created += OnFileChangedEvent; + watcher.Renamed += OnFileChangedEvent; + } + watcher.Path = Path.GetDirectoryName(fileName); + watcher.Filter = Path.GetFileName(fileName); + watcher.EnableRaisingEvents = true; + } catch (PlatformNotSupportedException) { + if (watcher != null) { + watcher.Dispose(); + } + watcher = null; + } catch (FileNotFoundException) { + // can occur if directory was deleted externally + if (watcher != null) { + watcher.Dispose(); + } + watcher = null; + } catch (ArgumentException) { + // can occur if parent directory was deleted externally + if (watcher != null) { + watcher.Dispose(); + } + watcher = null; + } + } + + bool wasChangedExternally; + + void OnFileChangedEvent(object sender, FileSystemEventArgs e) + { + LoggingService.Debug("Solution was changed externally: " + e.ChangeType); + if (!wasChangedExternally) { + wasChangedExternally = true; + if (WorkbenchSingleton.Workbench.IsActiveWindow) { + // delay reloading message a bit, prevents showing two messages + // when the file changes twice in quick succession; and prevents + // trying to reload the file while it is still being written + WorkbenchSingleton.CallLater( + TimeSpan.FromSeconds(0.5), + delegate { MainFormActivated(this, EventArgs.Empty); } ); + } + } + } + + void MainFormActivated(object sender, EventArgs e) + { + if (wasChangedExternally) { + wasChangedExternally = false; + + if (MessageService.ShowCustomDialog( + MessageService.DefaultMessageBoxTitle, + "${res:ICSharpCode.SharpDevelop.Project.SolutionAlteredExternallyMessage}", 0, 1, + "${res:ICSharpCode.SharpDevelop.Project.ReloadSolution}", "${res:ICSharpCode.SharpDevelop.Project.KeepOldSolution}") + == 0) + { + ProjectService.LoadSolution(ProjectService.OpenSolution.FileName); + } + } + } + + bool disposed; + + public void Dispose() + { + if (!disposed) { + WorkbenchSingleton.MainWindow.Activated -= MainFormActivated; + } + disposed = true; + } + } +} diff --git a/src/Main/Base/Project/Src/Project/Solution/Solution.cs b/src/Main/Base/Project/Src/Project/Solution/Solution.cs index 5fe6e31ce2..cb33801e3a 100644 --- a/src/Main/Base/Project/Src/Project/Solution/Solution.cs +++ b/src/Main/Base/Project/Src/Project/Solution/Solution.cs @@ -25,11 +25,13 @@ namespace ICSharpCode.SharpDevelop.Project bool isLoading; string fileName = String.Empty; + ProjectChangeWatcher changeWatcher; - public Solution() + public Solution(ProjectChangeWatcher changeWatcher) { preferences = new SolutionPreferences(this); this.MSBuildProjectCollection = new Microsoft.Build.Evaluation.ProjectCollection(); + this.changeWatcher = changeWatcher; } public Microsoft.Build.Evaluation.ProjectCollection MSBuildProjectCollection { get; private set; } @@ -183,7 +185,10 @@ namespace ICSharpCode.SharpDevelop.Project return fileName; } set { + changeWatcher.Disable(); fileName = value; + changeWatcher.Rename(fileName); + changeWatcher.Enable(); } } @@ -288,8 +293,9 @@ namespace ICSharpCode.SharpDevelop.Project public void Save() { try { + changeWatcher.Disable(); Save(fileName); - return; + changeWatcher.Enable(); } catch (IOException ex) { MessageService.ShowErrorFormatted("${res:SharpDevelop.Solution.CannotSave.IOException}", fileName, ex.Message); } catch (UnauthorizedAccessException ex) { @@ -307,6 +313,8 @@ namespace ICSharpCode.SharpDevelop.Project public void Save(string fileName) { + changeWatcher.Disable(); + changeWatcher.Rename(fileName); this.fileName = fileName; string outputDirectory = Path.GetDirectoryName(fileName); if (!System.IO.Directory.Exists(outputDirectory)) { @@ -415,6 +423,7 @@ namespace ICSharpCode.SharpDevelop.Project sw.WriteLine("EndGlobal"); } + changeWatcher.Enable(); } static void SaveProjectSections(IEnumerable sections, StringBuilder projectSection) @@ -1159,7 +1168,7 @@ namespace ICSharpCode.SharpDevelop.Project public static Solution Load(string fileName) { - Solution newSolution = new Solution(); + Solution newSolution = new Solution(new ProjectChangeWatcher(fileName)); solutionBeingLoaded = newSolution; newSolution.Name = Path.GetFileNameWithoutExtension(fileName); diff --git a/src/Main/Base/Project/Src/Services/ProjectService/ProjectService.cs b/src/Main/Base/Project/Src/Services/ProjectService/ProjectService.cs index acbebe16b7..09ab6250ad 100644 --- a/src/Main/Base/Project/Src/Services/ProjectService/ProjectService.cs +++ b/src/Main/Base/Project/Src/Services/ProjectService/ProjectService.cs @@ -378,7 +378,7 @@ namespace ICSharpCode.SharpDevelop.Project return; } } - Solution solution = new Solution(); + Solution solution = new Solution(new ProjectChangeWatcher(solutionFile)); solution.Name = Path.GetFileNameWithoutExtension(fileName); IProjectBinding binding = ProjectBindingService.GetBindingPerProjectFile(fileName); IProject project; diff --git a/src/Main/Base/Test/WebReferences/WebReferenceTestHelper.cs b/src/Main/Base/Test/WebReferences/WebReferenceTestHelper.cs index b2f9bd8276..b9940b0c54 100644 --- a/src/Main/Base/Test/WebReferences/WebReferenceTestHelper.cs +++ b/src/Main/Base/Test/WebReferences/WebReferenceTestHelper.cs @@ -26,7 +26,7 @@ namespace ICSharpCode.SharpDevelop.Tests.WebReferences public TestProject(string languageName) : base(new ProjectCreateInformation { - Solution = new Solution(), + Solution = new Solution(new ProjectChangeWatcher("c:\\temp\\TestProject.csproj")), ProjectName = "TestProject", OutputProjectFileName = "c:\\temp\\TestProject.csproj" })