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)