From 3ec594562ec02ff8bba51447c900efeaabeeca12 Mon Sep 17 00:00:00 2001 From: Christian Hornung Date: Fri, 6 Feb 2009 13:21:29 +0000 Subject: [PATCH] Fixed SD2-1519 - Forms designer moves deleted fields to designer code file. Designer now loads all source files that contain parts of the designed class. Added support for removing and replacing field declarations outside of the designer code file. (Changes are identical to the patch I posted in the tracker.) git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/3.0@3790 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../Project/Src/PythonDesignerGenerator.cs | 6 +- .../Test/Utils/MockDesignerGenerator.cs | 5 +- .../Project/Src/Gui/WixDialogDesigner.cs | 34 +-- .../Src/Gui/WixDialogDesignerGenerator.cs | 6 +- .../Project/FormsDesigner.csproj | 1 + .../AbstractDesignerGenerator.cs | 105 ++++++--- .../DesignerGenerator/IDesignerGenerator.cs | 10 +- .../DesignerGenerator/XmlDesignerGenerator.cs | 6 +- .../Project/Src/DesignerSourceCodeStorage.cs | 217 ++++++++++++++++++ .../Project/Src/DesignerViewContent.cs | 209 +++++++++-------- 10 files changed, 437 insertions(+), 162 deletions(-) create mode 100644 src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerSourceCodeStorage.cs diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonDesignerGenerator.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonDesignerGenerator.cs index 91c2d59847..4037b2e55b 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonDesignerGenerator.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonDesignerGenerator.cs @@ -9,6 +9,7 @@ using System; using System.CodeDom; using System.CodeDom.Compiler; using System.Collections; +using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Reflection; @@ -64,9 +65,10 @@ namespace ICSharpCode.PythonBinding this.viewContent = null; } - public OpenedFile DetermineDesignerCodeFile() + public IEnumerable GetSourceFiles(out OpenedFile designerCodeFile) { - return this.ViewContent.PrimaryFile; + designerCodeFile = this.ViewContent.PrimaryFile; + return new [] {designerCodeFile}; } public void MergeFormChanges(CodeCompileUnit unit) diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockDesignerGenerator.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockDesignerGenerator.cs index e8aa7b96d9..5a4d5fd54f 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockDesignerGenerator.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockDesignerGenerator.cs @@ -48,9 +48,10 @@ namespace PythonBinding.Tests.Utils this.viewContent = null; } - public ICSharpCode.SharpDevelop.OpenedFile DetermineDesignerCodeFile() + public System.Collections.Generic.IEnumerable GetSourceFiles(out ICSharpCode.SharpDevelop.OpenedFile designerCodeFile) { - return this.viewContent.DesignerCodeFile; + designerCodeFile = this.viewContent.DesignerCodeFile; + return new [] {designerCodeFile}; } public void MergeFormChanges(CodeCompileUnit unit) diff --git a/src/AddIns/BackendBindings/WixBinding/Project/Src/Gui/WixDialogDesigner.cs b/src/AddIns/BackendBindings/WixBinding/Project/Src/Gui/WixDialogDesigner.cs index 3d3b831c19..f6774fdfd6 100644 --- a/src/AddIns/BackendBindings/WixBinding/Project/Src/Gui/WixDialogDesigner.cs +++ b/src/AddIns/BackendBindings/WixBinding/Project/Src/Gui/WixDialogDesigner.cs @@ -7,7 +7,6 @@ using System; using System.Collections.ObjectModel; -using System.Diagnostics; using System.IO; using System.Xml; @@ -18,7 +17,6 @@ using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor; using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Project; using ICSharpCode.TextEditor; -using ICSharpCode.TextEditor.Document; namespace ICSharpCode.WixBinding { @@ -61,28 +59,6 @@ namespace ICSharpCode.WixBinding return null; } - // The FormsDesignerViewContent normally operates independently of any - // text editor. The following overrides connect the forms designer - // directly to the underlying XML text editor so that the caret positioning - // and text selection operations done by the WiX designer actually - // become visible in the text editor. - - public override IDocument PrimaryFileDocument { - get { return ((ITextEditorControlProvider)this.PrimaryViewContent).TextEditorControl.Document; } - } - - public override IDocument DesignerCodeFileDocument { - get { return this.PrimaryFileDocument; } - } - - public override System.Text.Encoding PrimaryFileEncoding { - get { return ((ITextEditorControlProvider)this.PrimaryViewContent).TextEditorControl.Encoding; } - } - - public override System.Text.Encoding DesignerCodeFileEncoding { - get { return this.PrimaryFileEncoding; } - } - /// /// Attempts to open the Wix dialog from the document currently /// associated with this designer. @@ -106,6 +82,16 @@ namespace ICSharpCode.WixBinding protected override void LoadInternal(OpenedFile file, Stream stream) { if (file == this.PrimaryFile) { + // The FormsDesignerViewContent normally operates independently of any + // text editor. The following statements connect the forms designer + // directly to the underlying XML text editor so that the caret positioning + // and text selection operations done by the WiX designer actually + // become visible in the text editor. + if (!this.SourceCodeStorage.ContainsFile(file)) { + TextEditorControl editor = ((ITextEditorControlProvider)this.PrimaryViewContent).TextEditorControl; + this.SourceCodeStorage.AddFile(file, editor.Document, editor.Encoding ?? ParserService.DefaultFileEncoding, true); + } + try { if (!ignoreDialogIdSelectedInTextEditor) { string dialogId = GetDialogIdSelectedInTextEditor(); diff --git a/src/AddIns/BackendBindings/WixBinding/Project/Src/Gui/WixDialogDesignerGenerator.cs b/src/AddIns/BackendBindings/WixBinding/Project/Src/Gui/WixDialogDesignerGenerator.cs index dae743f544..6ed4ebb03e 100644 --- a/src/AddIns/BackendBindings/WixBinding/Project/Src/Gui/WixDialogDesignerGenerator.cs +++ b/src/AddIns/BackendBindings/WixBinding/Project/Src/Gui/WixDialogDesignerGenerator.cs @@ -9,6 +9,7 @@ using System; using System.CodeDom; using System.CodeDom.Compiler; using System.Collections; +using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Reflection; @@ -58,9 +59,10 @@ namespace ICSharpCode.WixBinding get { return this.view; } } - public OpenedFile DetermineDesignerCodeFile() + public IEnumerable GetSourceFiles(out OpenedFile designerCodeFile) { - return this.view.PrimaryFile; + designerCodeFile = this.view.PrimaryFile; + return new [] {designerCodeFile}; } public void Attach(FormsDesignerViewContent viewContent) diff --git a/src/AddIns/DisplayBindings/FormsDesigner/Project/FormsDesigner.csproj b/src/AddIns/DisplayBindings/FormsDesigner/Project/FormsDesigner.csproj index 0fa8776d4d..793e2d364e 100644 --- a/src/AddIns/DisplayBindings/FormsDesigner/Project/FormsDesigner.csproj +++ b/src/AddIns/DisplayBindings/FormsDesigner/Project/FormsDesigner.csproj @@ -64,6 +64,7 @@ + ImageResourceEditorDialog.cs diff --git a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerGenerator/AbstractDesignerGenerator.cs b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerGenerator/AbstractDesignerGenerator.cs index bdd6f4f792..3f5786cc0f 100644 --- a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerGenerator/AbstractDesignerGenerator.cs +++ b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerGenerator/AbstractDesignerGenerator.cs @@ -12,6 +12,7 @@ using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.IO; +using System.Linq; using System.Reflection; using ICSharpCode.Core; @@ -70,7 +71,7 @@ namespace ICSharpCode.FormsDesigner this.viewContent = null; } - public OpenedFile DetermineDesignerCodeFile() + public IEnumerable GetSourceFiles(out OpenedFile designerCodeFile) { // get new initialize components ParseInformation info = ParserService.ParseFile(this.viewContent.PrimaryFileName, this.viewContent.PrimaryFileContent, false); @@ -80,9 +81,20 @@ namespace ICSharpCode.FormsDesigner this.currentClassPart = c; this.initializeComponents = FormsDesignerSecondaryDisplayBinding.GetInitializeComponents(c); if (this.initializeComponents != null) { - string designerFile = this.initializeComponents.DeclaringType.CompilationUnit.FileName; - if (designerFile != null) { - return FileService.GetOrCreateOpenedFile(designerFile); + string designerFileName = this.initializeComponents.DeclaringType.CompilationUnit.FileName; + if (designerFileName != null) { + + designerCodeFile = FileService.GetOrCreateOpenedFile(designerFileName); + + CompoundClass compound = c.GetCompoundClass() as CompoundClass; + if (compound == null) { + return new [] {designerCodeFile}; + } else { + return compound.Parts + .Select(cl => FileService.GetOrCreateOpenedFile(cl.CompilationUnit.FileName)) + .Distinct(); + } + } } } @@ -99,12 +111,20 @@ namespace ICSharpCode.FormsDesigner try { LoggingService.Info("Remove field declaration: "+fieldName); Reparse(); - IField field = GetField(formClass, fieldName); + + IField field = GetField(completeClass, fieldName); if (field != null) { - this.RemoveFieldDeclaration(this.ViewContent.DesignerCodeFileDocument, field); - } else if ((field = GetField(completeClass, fieldName)) != null) { - // TODO: Remove the field in the part where it is declared - LoggingService.Warn("Removing field declaration in non-designer part currently not supported"); + string fileName = field.DeclaringType.CompilationUnit.FileName; + LoggingService.Debug("-> Field is declared in file '" + fileName + "', Region: " + field.Region.ToString()); + OpenedFile file = FileService.GetOpenedFile(fileName); + if (file == null) throw new InvalidOperationException("The file where the field is declared is not open, although it belongs to the class."); + IDocument doc = this.ViewContent.GetDocumentForFile(file); + if (doc == null) throw new InvalidOperationException("Could not get document for file '" + file.FileName + "'."); + + this.RemoveFieldDeclaration(doc, field); + file.MakeDirty(); + } else { + LoggingService.Warn("-> Field '" + fieldName + "' not found in class"); } } catch (Exception ex) { MessageService.ShowError(ex); @@ -134,17 +154,19 @@ namespace ICSharpCode.FormsDesigner { try { Reparse(); - IField oldField = GetField(formClass, newField.Name); + IField oldField = GetField(completeClass, newField.Name); if (oldField != null) { - this.ReplaceFieldDeclaration(this.ViewContent.DesignerCodeFileDocument, oldField, GenerateFieldDeclaration(domGenerator, newField)); + string fileName = oldField.DeclaringType.CompilationUnit.FileName; + LoggingService.Debug("-> Old field is declared in file '" + fileName + "', Region: " + oldField.Region.ToString()); + OpenedFile file = FileService.GetOpenedFile(fileName); + if (file == null) throw new InvalidOperationException("The file where the field is declared is not open, although it belongs to the class."); + IDocument doc = this.ViewContent.GetDocumentForFile(file); + if (doc == null) throw new InvalidOperationException("Could not get document for file '" + file.FileName + "'."); + this.ReplaceFieldDeclaration(doc, oldField, GenerateFieldDeclaration(domGenerator, newField)); + file.MakeDirty(); } else { - if ((oldField = GetField(completeClass, newField.Name)) != null) { - // TODO: Replace the field in the part where it is declared - LoggingService.Warn("Field declaration replacement in non-designer part currently not supported"); - } else { - int endOffset = this.ViewContent.DesignerCodeFileDocument.PositionToOffset(new TextLocation(0, initializeComponents.BodyRegion.EndLine)); - this.ViewContent.DesignerCodeFileDocument.Insert(endOffset, tabs + GenerateFieldDeclaration(domGenerator, newField) + Environment.NewLine); - } + int endOffset = this.ViewContent.DesignerCodeFileDocument.PositionToOffset(new TextLocation(0, initializeComponents.BodyRegion.EndLine)); + this.ViewContent.DesignerCodeFileDocument.Insert(endOffset, tabs + GenerateFieldDeclaration(domGenerator, newField) + Environment.NewLine); } } catch (Exception ex) { MessageService.ShowError(ex); @@ -315,13 +337,18 @@ namespace ICSharpCode.FormsDesigner protected void Reparse() { + Dictionary parsings = new Dictionary(); ParseInformation info; ICompilationUnit cu; - // Reparse primary file to update currentClassPart + // Reparse all source files for the designed form + foreach (KeyValuePair entry in this.ViewContent.SourceFiles) { + parsings.Add(entry.Key, ParserService.ParseFile(entry.Key.FileName, entry.Value.TextContent, false)); + } + + // Update currentClassPart from PrimaryFile this.currentClassPart = null; - if (!String.IsNullOrEmpty(this.ViewContent.PrimaryFileName)) { - info = ParserService.ParseFile(this.ViewContent.PrimaryFileName, this.ViewContent.PrimaryFileContent, false); + if (this.ViewContent.PrimaryFile != null && parsings.TryGetValue(this.ViewContent.PrimaryFile, out info)) { cu = info.BestCompilationUnit; foreach (IClass c in cu.Classes) { if (FormsDesignerSecondaryDisplayBinding.BaseClassIsFormOrControl(c)) { @@ -331,19 +358,27 @@ namespace ICSharpCode.FormsDesigner } } } + if (this.currentClassPart == null) { + LoggingService.Warn("AbstractDesignerGenerator.Reparse: Could not find designed class in primary file '" + this.ViewContent.PrimaryFile.FileName + "'"); + } } else { LoggingService.Debug("AbstractDesignerGenerator.Reparse: Primary file is unavailable"); - info = null; } - // Reparse designer code file to update initializeComponents, - // completeClass and formClass - if (info == null || this.ViewContent.DesignerCodeFile != this.ViewContent.PrimaryFile) { - // Actual parsing is only necessary if the designer code file - // is not the same as the primary file. Otherwise we just - // reuse the parse info from above. - info = ParserService.ParseFile(this.ViewContent.DesignerCodeFile.FileName, this.ViewContent.DesignerCodeFileContent, false); + // Update initializeComponents, completeClass and formClass + // from designer code file + this.completeClass = null; + this.formClass = null; + this.initializeComponents = null; + if (this.ViewContent.DesignerCodeFile == null || + !parsings.TryGetValue(this.ViewContent.DesignerCodeFile, out info)) { + LoggingService.Warn("AbstractDesignerGenerator.Reparse: Designer source code file is unavailable"); + if (this.currentClassPart != null) { + this.completeClass = this.currentClassPart.GetCompoundClass(); + } + return; } + cu = info.BestCompilationUnit; foreach (IClass c in cu.Classes) { if (FormsDesignerSecondaryDisplayBinding.BaseClassIsFormOrControl(c)) { @@ -362,6 +397,10 @@ namespace ICSharpCode.FormsDesigner } } } + + if (this.completeClass == null || this.formClass == null) { + LoggingService.Warn("AbstractDesignerGenerator.Reparse: Could not find InitializeComponents in designer source code file '" + this.ViewContent.DesignerCodeFile.FileName + "'"); + } } protected static string GetIndentation(string line) @@ -396,10 +435,10 @@ namespace ICSharpCode.FormsDesigner foreach (IMethod method in completeClass.Methods) { if (method.Name == eventMethodName) { file = method.DeclaringType.CompilationUnit.FileName; - if (FileUtility.IsEqualFileName(file, this.ViewContent.PrimaryFileName)) { - position = GetCursorLine(this.ViewContent.PrimaryFileDocument, method); - } else if (FileUtility.IsEqualFileName(file, this.ViewContent.DesignerCodeFile.FileName)) { - position = GetCursorLine(this.ViewContent.DesignerCodeFileDocument, method); + OpenedFile openedFile = FileService.GetOpenedFile(file); + IDocument doc; + if (openedFile != null && (doc = this.ViewContent.GetDocumentForFile(openedFile)) != null) { + position = GetCursorLine(doc, method); } else { try { position = GetCursorLine(FindReferencesAndRenameHelper.GetDocumentInformation(file).CreateDocument(), method); diff --git a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerGenerator/IDesignerGenerator.cs b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerGenerator/IDesignerGenerator.cs index 9dbc0bf73a..b539c0c56a 100644 --- a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerGenerator/IDesignerGenerator.cs +++ b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerGenerator/IDesignerGenerator.cs @@ -9,6 +9,7 @@ using System; using System.CodeDom; using System.CodeDom.Compiler; using System.Collections; +using System.Collections.Generic; using System.ComponentModel; using System.Reflection; @@ -25,10 +26,13 @@ namespace ICSharpCode.FormsDesigner void Detach(); FormsDesignerViewContent ViewContent { get; } /// - /// Gets the OpenedFile for the file which contains the code to be modified by the forms designer. - /// This method must never return null. If it cannot find that file, it must throw an exception. + /// Gets the collection of OpenedFiles that contain code which belongs + /// to the designed form, not including resource files. /// - OpenedFile DetermineDesignerCodeFile(); + /// Receives the file which contains the code to be modified by the forms designer. + /// A collection of OpenedFiles that contain code which belongs to the designed form. + /// The returned collection must include the . + IEnumerable GetSourceFiles(out OpenedFile designerCodeFile); void MergeFormChanges(CodeCompileUnit unit); bool InsertComponentEvent(IComponent component, EventDescriptor edesc, string eventMethodName, string body, out string file, out int position); ICollection GetCompatibleMethods(EventDescriptor edesc); diff --git a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerGenerator/XmlDesignerGenerator.cs b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerGenerator/XmlDesignerGenerator.cs index 69de7b86c6..f5f0d5b577 100644 --- a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerGenerator/XmlDesignerGenerator.cs +++ b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerGenerator/XmlDesignerGenerator.cs @@ -9,6 +9,7 @@ using System; using System.CodeDom; using System.CodeDom.Compiler; using System.Collections; +using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Design; using System.Drawing; @@ -45,9 +46,10 @@ namespace ICSharpCode.FormsDesigner this.viewContent = null; } - public OpenedFile DetermineDesignerCodeFile() + public IEnumerable GetSourceFiles(out OpenedFile designerCodeFile) { - return this.viewContent.PrimaryFile; + designerCodeFile = this.viewContent.PrimaryFile; + return new [] {designerCodeFile}; } public void MergeFormChanges(CodeCompileUnit unit) diff --git a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerSourceCodeStorage.cs b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerSourceCodeStorage.cs new file mode 100644 index 0000000000..f1b163db9f --- /dev/null +++ b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerSourceCodeStorage.cs @@ -0,0 +1,217 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop; +using ICSharpCode.TextEditor.Document; +using ICSharpCode.TextEditor.Util; + +namespace ICSharpCode.FormsDesigner +{ + /// + /// Manages the source code files and their content associated with + /// an open forms designer view. + /// + public sealed class DesignerSourceCodeStorage : IEnumerable> + { + readonly Dictionary fileContents = new Dictionary(); + OpenedFile designerCodeFile; + + public DesignerSourceCodeStorage() + { + } + + public OpenedFile DesignerCodeFile { + get { return this.designerCodeFile; } + set { + if (value != null && !this.fileContents.ContainsKey(value)) { + throw new InvalidOperationException("The specified DesignerCodeFile '" + value.FileName + "' is not registered with this DesignerSourceCodeStorage instance."); + } + this.designerCodeFile = value; + } + } + + /// + /// Gets the associated with the specified file or + /// null if the file is not registered with the current instance. + /// + public IDocument this[OpenedFile file] { + get { + FileContent c; + if (this.fileContents.TryGetValue(file, out c)) { + return c.Document; + } else { + return null; + } + } + } + + public IEnumerator> GetEnumerator() + { + foreach (KeyValuePair entry in this.fileContents) { + yield return new KeyValuePair(entry.Key, entry.Value.Document); + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + public void LoadFile(OpenedFile file, Stream stream) + { + if (stream == null) + throw new ArgumentNullException("stream"); + if (file == null) + throw new ArgumentNullException("file"); + + FileContent c; + if (!this.fileContents.TryGetValue(file, out c)) { + c = new FileContent(); + this.fileContents.Add(file, c); + } + c.LoadFrom(stream); + } + + public void SaveFile(OpenedFile file, Stream stream) + { + if (stream == null) + throw new ArgumentNullException("stream"); + if (file == null) + throw new ArgumentNullException("file"); + + FileContent c; + if (!this.fileContents.TryGetValue(file, out c)) { + throw new InvalidOperationException("Cannot save file '" + file.FileName + "' because this file is not registered with this DesignerSourceCodeStorage instance."); + } + c.SaveTo(stream); + } + + /// + /// Adds a file with an empty document. + /// + public void AddFile(OpenedFile file) + { + this.fileContents.Add(file, new FileContent()); + } + + /// + /// Adds a file with an empty document and the specified encoding. + /// + public void AddFile(OpenedFile file, Encoding encoding) + { + this.fileContents.Add(file, new FileContent(encoding)); + } + + /// + /// Adds a file with the specified document and the specified encoding. + /// + public void AddFile(OpenedFile file, IDocument document, Encoding encoding) + { + this.fileContents.Add(file, new FileContent(document, encoding)); + } + + /// + /// Adds a file with the specified document and the specified encoding. + /// + /// When true, the SourceCodeStorage will not load the stream content into the document when the view is loaded. Use this when the document content is already managed elsewhere. + public void AddFile(OpenedFile file, IDocument document, Encoding encoding, bool doNotLoad) + { + this.fileContents.Add(file, new FileContent(document, encoding, doNotLoad)); + } + + public bool ContainsFile(OpenedFile file) + { + return this.fileContents.ContainsKey(file); + } + + public bool RemoveFile(OpenedFile file) + { + return this.fileContents.Remove(file); + } + + public Encoding GetFileEncoding(OpenedFile file) + { + if (file == null) + throw new ArgumentNullException("file"); + + FileContent c; + if (!this.fileContents.TryGetValue(file, out c)) { + throw new InvalidOperationException("Cannot get Encoding for file '" + file.FileName + "' because this file is not registered with this DesignerSourceCodeStorage instance."); + } + return c.Encoding; + } + + + /// + /// Stores the content of a file in an + /// along with its encoding. + /// + sealed class FileContent + { + static readonly DocumentFactory documentFactory = new DocumentFactory(); + + Encoding encoding; + readonly IDocument document; + readonly bool doNotLoad; + + public FileContent() + : this(ParserService.DefaultFileEncoding) + { + } + + public FileContent(Encoding encoding) + : this(documentFactory.CreateDocument(), encoding) + { + } + + public FileContent(IDocument document, Encoding encoding) + : this(document, encoding, false) + { + } + + public FileContent(IDocument document, Encoding encoding, bool doNotLoad) + { + if (document == null) + throw new ArgumentNullException("document"); + if (encoding == null) + throw new ArgumentNullException("encoding"); + this.document = document; + this.encoding = encoding; + this.doNotLoad = doNotLoad; + } + + public IDocument Document { + get { return this.document; } + } + + public Encoding Encoding { + get { return this.encoding; } + } + + public void LoadFrom(Stream stream) + { + if (this.doNotLoad) + return; + this.encoding = ParserService.DefaultFileEncoding; + this.Document.TextContent = FileReader.ReadFileContent(stream, ref this.encoding); + } + + public void SaveTo(Stream stream) + { + using(StreamWriter writer = new StreamWriter(stream, this.encoding)) { + writer.Write(this.Document.TextContent); + } + } + } + } +} diff --git a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerViewContent.cs b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerViewContent.cs index 4b018a64ac..514e7a7ad6 100644 --- a/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerViewContent.cs +++ b/src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerViewContent.cs @@ -13,6 +13,7 @@ using System.ComponentModel.Design; using System.ComponentModel.Design.Serialization; using System.Drawing; using System.IO; +using System.Linq; using System.Text; using System.Windows.Forms; @@ -23,7 +24,6 @@ using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor; using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.TextEditor.Document; -using ICSharpCode.TextEditor.Util; namespace ICSharpCode.FormsDesigner { @@ -41,11 +41,7 @@ namespace ICSharpCode.FormsDesigner FormsDesignerUndoEngine undoEngine; TypeResolutionService typeResolutionService; - Encoding primaryFileEncoding; - readonly IDocument primaryFileDocument = new DocumentFactory().CreateDocument(); - Encoding designerCodeFileEncoding; - OpenedFile designerCodeFile; - IDocument designerCodeFileDocument; + readonly DesignerSourceCodeStorage sourceCodeStorage; readonly Dictionary addedTypeDescriptionProviders = new Dictionary(); @@ -64,11 +60,11 @@ namespace ICSharpCode.FormsDesigner } public OpenedFile DesignerCodeFile { - get { return this.designerCodeFile; } + get { return this.sourceCodeStorage.DesignerCodeFile; } } - public virtual IDocument PrimaryFileDocument { - get { return this.primaryFileDocument; } + public IDocument PrimaryFileDocument { + get { return this.sourceCodeStorage[this.PrimaryFile]; } } public string PrimaryFileContent { @@ -76,12 +72,14 @@ namespace ICSharpCode.FormsDesigner set { this.PrimaryFileDocument.TextContent = value; } } - public virtual Encoding PrimaryFileEncoding { - get { return this.primaryFileEncoding; } - } - - public virtual IDocument DesignerCodeFileDocument { - get { return this.designerCodeFileDocument; } + public IDocument DesignerCodeFileDocument { + get { + if (this.sourceCodeStorage.DesignerCodeFile == null) { + return null; + } else { + return this.sourceCodeStorage[this.sourceCodeStorage.DesignerCodeFile]; + } + } } public string DesignerCodeFileContent { @@ -89,19 +87,17 @@ namespace ICSharpCode.FormsDesigner set { this.DesignerCodeFileDocument.TextContent = value; } } - public virtual Encoding DesignerCodeFileEncoding { - get { return this.designerCodeFileEncoding; } - } - public IDocument GetDocumentForFile(OpenedFile file) { - if (file == this.DesignerCodeFile) { - return this.DesignerCodeFileDocument; - } else if (file == this.PrimaryFile) { - return this.PrimaryFileDocument; - } else { - return null; - } + return this.sourceCodeStorage[file]; + } + + public IEnumerable> SourceFiles { + get { return this.sourceCodeStorage; } + } + + protected DesignerSourceCodeStorage SourceCodeStorage { + get { return this.sourceCodeStorage; } } public IViewContent PrimaryViewContent { @@ -131,6 +127,7 @@ namespace ICSharpCode.FormsDesigner this.UserControl = this.pleaseWaitLabel; + this.sourceCodeStorage = new DesignerSourceCodeStorage(); this.resourceStore = new ResourceStore(this); // null check is required to support running in unit test mode @@ -162,59 +159,73 @@ namespace ICSharpCode.FormsDesigner public FormsDesignerViewContent(IViewContent primaryViewContent, OpenedFile mockFile) : this(primaryViewContent) { - this.primaryFileDocument = new DocumentFactory().CreateDocument(); - this.designerCodeFileDocument = this.primaryFileDocument; - this.designerCodeFileEncoding = System.Text.Encoding.UTF8; - this.designerCodeFile = mockFile; + this.sourceCodeStorage.AddFile(mockFile, Encoding.UTF8); + this.sourceCodeStorage.DesignerCodeFile = mockFile; } + bool inMasterLoadOperation; + protected override void LoadInternal(OpenedFile file, System.IO.Stream stream) { - LoggingService.Debug("Forms designer: Load " + file.FileName); + LoggingService.Debug("Forms designer: Load " + file.FileName + "; inMasterLoadOperation=" + this.inMasterLoadOperation); - if (file == this.PrimaryFile) { + if (inMasterLoadOperation) { + + if (this.sourceCodeStorage.ContainsFile(file)) { + LoggingService.Debug("Forms designer: Loading " + file.FileName + " in source code storage"); + this.sourceCodeStorage.LoadFile(file, stream); + } else { + LoggingService.Debug("Forms designer: Loading " + file.FileName + " in resource store"); + this.resourceStore.Load(file, stream); + } - this.primaryFileEncoding = ParserService.DefaultFileEncoding; - this.primaryFileDocument.TextContent = FileReader.ReadFileContent(stream, ref this.primaryFileEncoding); + } else if (file == this.PrimaryFile || this.sourceCodeStorage.ContainsFile(file)) { - LoggingService.Debug("Forms designer: Determining designer code file for " + file.FileName); - OpenedFile newDesignerCodeFile = this.generator.DetermineDesignerCodeFile(); - if (newDesignerCodeFile == null) { - throw new InvalidOperationException("The designer code file could not be determined."); + if (this.loader != null && this.loader.Loading) { + throw new InvalidOperationException("Designer loading a source code file while DesignerLoader is loading and the view is not in a master load operation. This must not happen."); } - if (this.designerCodeFile != null && newDesignerCodeFile != this.designerCodeFile) { - this.Files.Remove(this.designerCodeFile); + if (this.designSurface != null) { + this.UnloadDesigner(); } - this.designerCodeFile = newDesignerCodeFile; - if (this.designerCodeFile == this.PrimaryFile) { + this.inMasterLoadOperation = true; + + try { - LoggingService.Debug("Forms designer: Designer code file is equal to primary file. Reloading designer."); + this.sourceCodeStorage.LoadFile(file, stream); + + LoggingService.Debug("Forms designer: Determining designer source files for " + file.FileName); + OpenedFile newDesignerCodeFile; + IEnumerable sourceFiles = this.generator.GetSourceFiles(out newDesignerCodeFile); + if (sourceFiles == null || newDesignerCodeFile == null) { + throw new FormsDesignerLoadException("The designer source files could not be determined."); + } + + // Unload all source files from the view which are no longer in the returned collection + foreach (OpenedFile f in this.Files.Except(sourceFiles).ToArray()) { + // Ensure that we only unload source files, but not resource files. + if (this.sourceCodeStorage.ContainsFile(f)) { + LoggingService.Debug("Forms designer: Unloading file '" + f.FileName + "' because it no longer belongs to the designed form"); + this.Files.Remove(f); + this.sourceCodeStorage.RemoveFile(f); + } + } + + // Load all files which are new in the returned collection + foreach (OpenedFile f in sourceFiles.Except(this.Files).ToArray()) { + this.sourceCodeStorage.AddFile(f); + this.Files.Add(f); + } + + this.sourceCodeStorage.DesignerCodeFile = newDesignerCodeFile; - this.UnloadDesigner(); - this.designerCodeFileEncoding = this.PrimaryFileEncoding; - this.designerCodeFileDocument = this.PrimaryFileDocument; this.LoadAndDisplayDesigner(); - } else if (!this.Files.Contains(this.designerCodeFile)) { - LoggingService.Debug("Forms designer: Adding designer code file " + this.designerCodeFile.FileName); - this.Files.Insert(1, this.designerCodeFile); - } else if (this.HasLoadError || this.designSurface == null || !this.designSurface.IsLoaded) { - LoggingService.Debug("Forms designer: Having a load error. Reloading designer."); - this.ReloadDesignerFromMemory(); + } finally { + this.inMasterLoadOperation = false; } - } else if (file == this.DesignerCodeFile) { - - LoggingService.Debug("Forms designer: Reloading designer because of LoadInternal on DesignerCodeFile"); - - this.UnloadDesigner(); - this.designerCodeFileEncoding = ParserService.DefaultFileEncoding; - this.designerCodeFileDocument = new DocumentFactory().CreateDocument(); - this.designerCodeFileDocument.TextContent = FileReader.ReadFileContent(stream, ref this.designerCodeFileEncoding); - this.LoadAndDisplayDesigner(); - } else { // Loading a resource file @@ -224,13 +235,19 @@ namespace ICSharpCode.FormsDesigner LoggingService.Debug("Forms designer: Reloading designer because of LoadInternal on resource file"); this.UnloadDesigner(); mustReload = true; + this.inMasterLoadOperation = true; } else { mustReload = false; } - LoggingService.Debug("Forms designer: Loading " + file.FileName + " in resource store"); - this.resourceStore.Load(file, stream); - if (mustReload) { - this.LoadAndDisplayDesigner(); + + try { + LoggingService.Debug("Forms designer: Loading " + file.FileName + " in resource store"); + this.resourceStore.Load(file, stream); + if (mustReload) { + this.LoadAndDisplayDesigner(); + } + } finally { + this.inMasterLoadOperation = false; } } @@ -242,14 +259,8 @@ namespace ICSharpCode.FormsDesigner if (hasUnmergedChanges) { this.MergeFormChanges(); } - if (file == this.DesignerCodeFile) { - using(StreamWriter writer = new StreamWriter(stream, this.DesignerCodeFileEncoding)) { - writer.Write(this.DesignerCodeFileContent); - } - } else if (file == this.PrimaryFile) { - using(StreamWriter writer = new StreamWriter(stream, this.PrimaryFileEncoding)) { - writer.Write(this.PrimaryFileContent); - } + if (this.sourceCodeStorage.ContainsFile(file)) { + this.sourceCodeStorage.SaveFile(file, stream); } else { this.resourceStore.Save(file, stream); } @@ -516,6 +527,7 @@ namespace ICSharpCode.FormsDesigner void DesignerFlushed(object sender, EventArgs e) { this.resourceStore.CommitAllResourceChanges(); + this.hasUnmergedChanges = false; } static string FormatLoadErrors(DesignSurface designSurface) @@ -530,8 +542,11 @@ namespace ICSharpCode.FormsDesigner public virtual void MergeFormChanges() { - if (this.HasLoadError) { + if (this.HasLoadError || this.designSurface == null) { + LoggingService.Debug("Forms designer: Cannot merge form changes because the designer is not loaded successfully or not loaded at all"); return; + } else if (this.DesignerCodeFile == null) { + throw new InvalidOperationException("Cannot merge form changes without a designer code file."); } bool isDirty = this.DesignerCodeFile.IsDirty; LoggingService.Info("Merging form changes..."); @@ -816,7 +831,7 @@ namespace ICSharpCode.FormsDesigner protected void ReloadDesignerFromMemory() { - using(MemoryStream ms = new MemoryStream(this.DesignerCodeFileEncoding.GetBytes(this.DesignerCodeFileContent), false)) { + using(MemoryStream ms = new MemoryStream(this.sourceCodeStorage.GetFileEncoding(this.DesignerCodeFile).GetBytes(this.DesignerCodeFileContent), false)) { this.Load(this.DesignerCodeFile, ms); } @@ -829,7 +844,7 @@ namespace ICSharpCode.FormsDesigner void FileServiceFileRemoving(object sender, FileCancelEventArgs e) { - if (!e.Cancel && this.DesignerCodeFile != null) { + if (!e.Cancel) { if (WorkbenchSingleton.InvokeRequired) { WorkbenchSingleton.SafeThreadAsyncCall(this.CheckForDesignerCodeFileDeletion, e); } else { @@ -840,27 +855,33 @@ namespace ICSharpCode.FormsDesigner void CheckForDesignerCodeFileDeletion(FileCancelEventArgs e) { - if (this.DesignerCodeFile == null || String.IsNullOrEmpty(this.DesignerCodeFile.FileName)) { - return; + OpenedFile file; + + if (e.IsDirectory) { + file = this.Files.SingleOrDefault( + f => FileUtility.IsBaseDirectory(e.FileName, f.FileName) + ); + } else { + file = this.Files.SingleOrDefault( + f => FileUtility.IsEqualFileName(f.FileName, e.FileName) + ); } - if ((!e.IsDirectory && FileUtility.IsEqualFileName(this.DesignerCodeFile.FileName, e.FileName)) || - (e.IsDirectory && FileUtility.IsBaseDirectory(e.FileName, this.DesignerCodeFile.FileName))) { - - LoggingService.Info("Forms designer: Handling deletion of open designer code file '" + this.DesignerCodeFile.FileName + "'"); - - // When our designer code file is deleted, - // unload the designer and remove the file from the - // file list so that the primary view is not closed - // because of this event. - + if (file == null || file == this.PrimaryFile) + return; + + LoggingService.Info("Forms designer: Handling deletion of open designer code file '" + file.FileName + "'"); + + if (file == this.sourceCodeStorage.DesignerCodeFile) { this.UnloadDesigner(); - this.Files.Remove(this.DesignerCodeFile); - this.designerCodeFile = null; - this.designerCodeFileDocument = null; - this.designerCodeFileEncoding = null; - + this.sourceCodeStorage.DesignerCodeFile = null; } + + // When any of our designer code files is deleted, + // remove the file from the file list so that + // the primary view is not closed because of this event. + this.Files.Remove(file); + this.sourceCodeStorage.RemoveFile(file); } #region Design surface manager (static)