Browse Source

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
shortcuts
Christian Hornung 17 years ago
parent
commit
3ec594562e
  1. 6
      src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonDesignerGenerator.cs
  2. 5
      src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockDesignerGenerator.cs
  3. 34
      src/AddIns/BackendBindings/WixBinding/Project/Src/Gui/WixDialogDesigner.cs
  4. 6
      src/AddIns/BackendBindings/WixBinding/Project/Src/Gui/WixDialogDesignerGenerator.cs
  5. 1
      src/AddIns/DisplayBindings/FormsDesigner/Project/FormsDesigner.csproj
  6. 101
      src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerGenerator/AbstractDesignerGenerator.cs
  7. 10
      src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerGenerator/IDesignerGenerator.cs
  8. 6
      src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerGenerator/XmlDesignerGenerator.cs
  9. 217
      src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerSourceCodeStorage.cs
  10. 187
      src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerViewContent.cs

6
src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonDesignerGenerator.cs

@ -9,6 +9,7 @@ using System;
using System.CodeDom; using System.CodeDom;
using System.CodeDom.Compiler; using System.CodeDom.Compiler;
using System.Collections; using System.Collections;
using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Drawing; using System.Drawing;
using System.Reflection; using System.Reflection;
@ -64,9 +65,10 @@ namespace ICSharpCode.PythonBinding
this.viewContent = null; this.viewContent = null;
} }
public OpenedFile DetermineDesignerCodeFile() public IEnumerable<OpenedFile> GetSourceFiles(out OpenedFile designerCodeFile)
{ {
return this.ViewContent.PrimaryFile; designerCodeFile = this.ViewContent.PrimaryFile;
return new [] {designerCodeFile};
} }
public void MergeFormChanges(CodeCompileUnit unit) public void MergeFormChanges(CodeCompileUnit unit)

5
src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockDesignerGenerator.cs

@ -48,9 +48,10 @@ namespace PythonBinding.Tests.Utils
this.viewContent = null; this.viewContent = null;
} }
public ICSharpCode.SharpDevelop.OpenedFile DetermineDesignerCodeFile() public System.Collections.Generic.IEnumerable<ICSharpCode.SharpDevelop.OpenedFile> GetSourceFiles(out ICSharpCode.SharpDevelop.OpenedFile designerCodeFile)
{ {
return this.viewContent.DesignerCodeFile; designerCodeFile = this.viewContent.DesignerCodeFile;
return new [] {designerCodeFile};
} }
public void MergeFormChanges(CodeCompileUnit unit) public void MergeFormChanges(CodeCompileUnit unit)

34
src/AddIns/BackendBindings/WixBinding/Project/Src/Gui/WixDialogDesigner.cs

@ -7,7 +7,6 @@
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Xml; using System.Xml;
@ -18,7 +17,6 @@ using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor;
using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project; using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.TextEditor; using ICSharpCode.TextEditor;
using ICSharpCode.TextEditor.Document;
namespace ICSharpCode.WixBinding namespace ICSharpCode.WixBinding
{ {
@ -61,28 +59,6 @@ namespace ICSharpCode.WixBinding
return null; 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; }
}
/// <summary> /// <summary>
/// Attempts to open the Wix dialog from the document currently /// Attempts to open the Wix dialog from the document currently
/// associated with this designer. /// associated with this designer.
@ -106,6 +82,16 @@ namespace ICSharpCode.WixBinding
protected override void LoadInternal(OpenedFile file, Stream stream) protected override void LoadInternal(OpenedFile file, Stream stream)
{ {
if (file == this.PrimaryFile) { 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 { try {
if (!ignoreDialogIdSelectedInTextEditor) { if (!ignoreDialogIdSelectedInTextEditor) {
string dialogId = GetDialogIdSelectedInTextEditor(); string dialogId = GetDialogIdSelectedInTextEditor();

6
src/AddIns/BackendBindings/WixBinding/Project/Src/Gui/WixDialogDesignerGenerator.cs

@ -9,6 +9,7 @@ using System;
using System.CodeDom; using System.CodeDom;
using System.CodeDom.Compiler; using System.CodeDom.Compiler;
using System.Collections; using System.Collections;
using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
@ -58,9 +59,10 @@ namespace ICSharpCode.WixBinding
get { return this.view; } get { return this.view; }
} }
public OpenedFile DetermineDesignerCodeFile() public IEnumerable<OpenedFile> GetSourceFiles(out OpenedFile designerCodeFile)
{ {
return this.view.PrimaryFile; designerCodeFile = this.view.PrimaryFile;
return new [] {designerCodeFile};
} }
public void Attach(FormsDesignerViewContent viewContent) public void Attach(FormsDesignerViewContent viewContent)

1
src/AddIns/DisplayBindings/FormsDesigner/Project/FormsDesigner.csproj

@ -64,6 +64,7 @@
<Compile Include="Src\DesignerLoader\AbstractCodeDomDesignerLoader.cs" /> <Compile Include="Src\DesignerLoader\AbstractCodeDomDesignerLoader.cs" />
<Compile Include="Src\DesignerLoader\NRefactoryDesignerLoader.cs" /> <Compile Include="Src\DesignerLoader\NRefactoryDesignerLoader.cs" />
<Compile Include="Src\DesignerLoader\XmlDesignerLoader.cs" /> <Compile Include="Src\DesignerLoader\XmlDesignerLoader.cs" />
<Compile Include="Src\DesignerSourceCodeStorage.cs" />
<Compile Include="Src\Gui\ImageResourceEditorDialog.cs" /> <Compile Include="Src\Gui\ImageResourceEditorDialog.cs" />
<Compile Include="Src\Gui\ImageResourceEditorDialog.Designer.cs"> <Compile Include="Src\Gui\ImageResourceEditorDialog.Designer.cs">
<DependentUpon>ImageResourceEditorDialog.cs</DependentUpon> <DependentUpon>ImageResourceEditorDialog.cs</DependentUpon>

101
src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerGenerator/AbstractDesignerGenerator.cs

@ -12,6 +12,7 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
using ICSharpCode.Core; using ICSharpCode.Core;
@ -70,7 +71,7 @@ namespace ICSharpCode.FormsDesigner
this.viewContent = null; this.viewContent = null;
} }
public OpenedFile DetermineDesignerCodeFile() public IEnumerable<OpenedFile> GetSourceFiles(out OpenedFile designerCodeFile)
{ {
// get new initialize components // get new initialize components
ParseInformation info = ParserService.ParseFile(this.viewContent.PrimaryFileName, this.viewContent.PrimaryFileContent, false); ParseInformation info = ParserService.ParseFile(this.viewContent.PrimaryFileName, this.viewContent.PrimaryFileContent, false);
@ -80,9 +81,20 @@ namespace ICSharpCode.FormsDesigner
this.currentClassPart = c; this.currentClassPart = c;
this.initializeComponents = FormsDesignerSecondaryDisplayBinding.GetInitializeComponents(c); this.initializeComponents = FormsDesignerSecondaryDisplayBinding.GetInitializeComponents(c);
if (this.initializeComponents != null) { if (this.initializeComponents != null) {
string designerFile = this.initializeComponents.DeclaringType.CompilationUnit.FileName; string designerFileName = this.initializeComponents.DeclaringType.CompilationUnit.FileName;
if (designerFile != null) { if (designerFileName != null) {
return FileService.GetOrCreateOpenedFile(designerFile);
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 { try {
LoggingService.Info("Remove field declaration: "+fieldName); LoggingService.Info("Remove field declaration: "+fieldName);
Reparse(); Reparse();
IField field = GetField(formClass, fieldName);
IField field = GetField(completeClass, fieldName);
if (field != null) { if (field != null) {
this.RemoveFieldDeclaration(this.ViewContent.DesignerCodeFileDocument, field); string fileName = field.DeclaringType.CompilationUnit.FileName;
} else if ((field = GetField(completeClass, fieldName)) != null) { LoggingService.Debug("-> Field is declared in file '" + fileName + "', Region: " + field.Region.ToString());
// TODO: Remove the field in the part where it is declared OpenedFile file = FileService.GetOpenedFile(fileName);
LoggingService.Warn("Removing field declaration in non-designer part currently not supported"); 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) { } catch (Exception ex) {
MessageService.ShowError(ex); MessageService.ShowError(ex);
@ -134,18 +154,20 @@ namespace ICSharpCode.FormsDesigner
{ {
try { try {
Reparse(); Reparse();
IField oldField = GetField(formClass, newField.Name); IField oldField = GetField(completeClass, newField.Name);
if (oldField != null) { if (oldField != null) {
this.ReplaceFieldDeclaration(this.ViewContent.DesignerCodeFileDocument, oldField, GenerateFieldDeclaration(domGenerator, newField)); string fileName = oldField.DeclaringType.CompilationUnit.FileName;
} else { LoggingService.Debug("-> Old field is declared in file '" + fileName + "', Region: " + oldField.Region.ToString());
if ((oldField = GetField(completeClass, newField.Name)) != null) { OpenedFile file = FileService.GetOpenedFile(fileName);
// TODO: Replace the field in the part where it is declared if (file == null) throw new InvalidOperationException("The file where the field is declared is not open, although it belongs to the class.");
LoggingService.Warn("Field declaration replacement in non-designer part currently not supported"); 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 { } else {
int endOffset = this.ViewContent.DesignerCodeFileDocument.PositionToOffset(new TextLocation(0, initializeComponents.BodyRegion.EndLine)); int endOffset = this.ViewContent.DesignerCodeFileDocument.PositionToOffset(new TextLocation(0, initializeComponents.BodyRegion.EndLine));
this.ViewContent.DesignerCodeFileDocument.Insert(endOffset, tabs + GenerateFieldDeclaration(domGenerator, newField) + Environment.NewLine); this.ViewContent.DesignerCodeFileDocument.Insert(endOffset, tabs + GenerateFieldDeclaration(domGenerator, newField) + Environment.NewLine);
} }
}
} catch (Exception ex) { } catch (Exception ex) {
MessageService.ShowError(ex); MessageService.ShowError(ex);
} }
@ -315,13 +337,18 @@ namespace ICSharpCode.FormsDesigner
protected void Reparse() protected void Reparse()
{ {
Dictionary<OpenedFile, ParseInformation> parsings = new Dictionary<OpenedFile, ParseInformation>();
ParseInformation info; ParseInformation info;
ICompilationUnit cu; ICompilationUnit cu;
// Reparse primary file to update currentClassPart // Reparse all source files for the designed form
foreach (KeyValuePair<OpenedFile, IDocument> 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; this.currentClassPart = null;
if (!String.IsNullOrEmpty(this.ViewContent.PrimaryFileName)) { if (this.ViewContent.PrimaryFile != null && parsings.TryGetValue(this.ViewContent.PrimaryFile, out info)) {
info = ParserService.ParseFile(this.ViewContent.PrimaryFileName, this.ViewContent.PrimaryFileContent, false);
cu = info.BestCompilationUnit; cu = info.BestCompilationUnit;
foreach (IClass c in cu.Classes) { foreach (IClass c in cu.Classes) {
if (FormsDesignerSecondaryDisplayBinding.BaseClassIsFormOrControl(c)) { 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 { } else {
LoggingService.Debug("AbstractDesignerGenerator.Reparse: Primary file is unavailable"); LoggingService.Debug("AbstractDesignerGenerator.Reparse: Primary file is unavailable");
info = null;
} }
// Reparse designer code file to update initializeComponents, // Update initializeComponents, completeClass and formClass
// completeClass and formClass // from designer code file
if (info == null || this.ViewContent.DesignerCodeFile != this.ViewContent.PrimaryFile) { this.completeClass = null;
// Actual parsing is only necessary if the designer code file this.formClass = null;
// is not the same as the primary file. Otherwise we just this.initializeComponents = null;
// reuse the parse info from above. if (this.ViewContent.DesignerCodeFile == null ||
info = ParserService.ParseFile(this.ViewContent.DesignerCodeFile.FileName, this.ViewContent.DesignerCodeFileContent, false); !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; cu = info.BestCompilationUnit;
foreach (IClass c in cu.Classes) { foreach (IClass c in cu.Classes) {
if (FormsDesignerSecondaryDisplayBinding.BaseClassIsFormOrControl(c)) { 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) protected static string GetIndentation(string line)
@ -396,10 +435,10 @@ namespace ICSharpCode.FormsDesigner
foreach (IMethod method in completeClass.Methods) { foreach (IMethod method in completeClass.Methods) {
if (method.Name == eventMethodName) { if (method.Name == eventMethodName) {
file = method.DeclaringType.CompilationUnit.FileName; file = method.DeclaringType.CompilationUnit.FileName;
if (FileUtility.IsEqualFileName(file, this.ViewContent.PrimaryFileName)) { OpenedFile openedFile = FileService.GetOpenedFile(file);
position = GetCursorLine(this.ViewContent.PrimaryFileDocument, method); IDocument doc;
} else if (FileUtility.IsEqualFileName(file, this.ViewContent.DesignerCodeFile.FileName)) { if (openedFile != null && (doc = this.ViewContent.GetDocumentForFile(openedFile)) != null) {
position = GetCursorLine(this.ViewContent.DesignerCodeFileDocument, method); position = GetCursorLine(doc, method);
} else { } else {
try { try {
position = GetCursorLine(FindReferencesAndRenameHelper.GetDocumentInformation(file).CreateDocument(), method); position = GetCursorLine(FindReferencesAndRenameHelper.GetDocumentInformation(file).CreateDocument(), method);

10
src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerGenerator/IDesignerGenerator.cs

@ -9,6 +9,7 @@ using System;
using System.CodeDom; using System.CodeDom;
using System.CodeDom.Compiler; using System.CodeDom.Compiler;
using System.Collections; using System.Collections;
using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Reflection; using System.Reflection;
@ -25,10 +26,13 @@ namespace ICSharpCode.FormsDesigner
void Detach(); void Detach();
FormsDesignerViewContent ViewContent { get; } FormsDesignerViewContent ViewContent { get; }
/// <summary> /// <summary>
/// Gets the OpenedFile for the file which contains the code to be modified by the forms designer. /// Gets the collection of OpenedFiles that contain code which belongs
/// This method must never return null. If it cannot find that file, it must throw an exception. /// to the designed form, not including resource files.
/// </summary> /// </summary>
OpenedFile DetermineDesignerCodeFile(); /// <param name="designerCodeFile">Receives the file which contains the code to be modified by the forms designer.</param>
/// <returns>A collection of OpenedFiles that contain code which belongs to the designed form.</returns>
/// <remarks>The returned collection must include the <paramref name="designerCodeFile"/>.</remarks>
IEnumerable<OpenedFile> GetSourceFiles(out OpenedFile designerCodeFile);
void MergeFormChanges(CodeCompileUnit unit); void MergeFormChanges(CodeCompileUnit unit);
bool InsertComponentEvent(IComponent component, EventDescriptor edesc, string eventMethodName, string body, out string file, out int position); bool InsertComponentEvent(IComponent component, EventDescriptor edesc, string eventMethodName, string body, out string file, out int position);
ICollection GetCompatibleMethods(EventDescriptor edesc); ICollection GetCompatibleMethods(EventDescriptor edesc);

6
src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerGenerator/XmlDesignerGenerator.cs

@ -9,6 +9,7 @@ using System;
using System.CodeDom; using System.CodeDom;
using System.CodeDom.Compiler; using System.CodeDom.Compiler;
using System.Collections; using System.Collections;
using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.ComponentModel.Design; using System.ComponentModel.Design;
using System.Drawing; using System.Drawing;
@ -45,9 +46,10 @@ namespace ICSharpCode.FormsDesigner
this.viewContent = null; this.viewContent = null;
} }
public OpenedFile DetermineDesignerCodeFile() public IEnumerable<OpenedFile> GetSourceFiles(out OpenedFile designerCodeFile)
{ {
return this.viewContent.PrimaryFile; designerCodeFile = this.viewContent.PrimaryFile;
return new [] {designerCodeFile};
} }
public void MergeFormChanges(CodeCompileUnit unit) public void MergeFormChanges(CodeCompileUnit unit)

217
src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerSourceCodeStorage.cs

@ -0,0 +1,217 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Christian Hornung" email="chhornung@googlemail.com"/>
// <version>$Revision$</version>
// </file>
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
{
/// <summary>
/// Manages the source code files and their content associated with
/// an open forms designer view.
/// </summary>
public sealed class DesignerSourceCodeStorage : IEnumerable<KeyValuePair<OpenedFile, IDocument>>
{
readonly Dictionary<OpenedFile, FileContent> fileContents = new Dictionary<OpenedFile, FileContent>();
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;
}
}
/// <summary>
/// Gets the <see cref="IDocument"/> associated with the specified file or
/// <c>null</c> if the file is not registered with the current instance.
/// </summary>
public IDocument this[OpenedFile file] {
get {
FileContent c;
if (this.fileContents.TryGetValue(file, out c)) {
return c.Document;
} else {
return null;
}
}
}
public IEnumerator<KeyValuePair<OpenedFile, IDocument>> GetEnumerator()
{
foreach (KeyValuePair<OpenedFile, FileContent> entry in this.fileContents) {
yield return new KeyValuePair<OpenedFile, IDocument>(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);
}
/// <summary>
/// Adds a file with an empty document.
/// </summary>
public void AddFile(OpenedFile file)
{
this.fileContents.Add(file, new FileContent());
}
/// <summary>
/// Adds a file with an empty document and the specified encoding.
/// </summary>
public void AddFile(OpenedFile file, Encoding encoding)
{
this.fileContents.Add(file, new FileContent(encoding));
}
/// <summary>
/// Adds a file with the specified document and the specified encoding.
/// </summary>
public void AddFile(OpenedFile file, IDocument document, Encoding encoding)
{
this.fileContents.Add(file, new FileContent(document, encoding));
}
/// <summary>
/// Adds a file with the specified document and the specified encoding.
/// </summary>
/// <param name="doNotLoad">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.</param>
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;
}
/// <summary>
/// Stores the content of a file in an <see cref="IDocument"/>
/// along with its encoding.
/// </summary>
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);
}
}
}
}
}

187
src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerViewContent.cs

@ -13,6 +13,7 @@ using System.ComponentModel.Design;
using System.ComponentModel.Design.Serialization; using System.ComponentModel.Design.Serialization;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
@ -23,7 +24,6 @@ using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor; using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor;
using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.TextEditor.Document; using ICSharpCode.TextEditor.Document;
using ICSharpCode.TextEditor.Util;
namespace ICSharpCode.FormsDesigner namespace ICSharpCode.FormsDesigner
{ {
@ -41,11 +41,7 @@ namespace ICSharpCode.FormsDesigner
FormsDesignerUndoEngine undoEngine; FormsDesignerUndoEngine undoEngine;
TypeResolutionService typeResolutionService; TypeResolutionService typeResolutionService;
Encoding primaryFileEncoding; readonly DesignerSourceCodeStorage sourceCodeStorage;
readonly IDocument primaryFileDocument = new DocumentFactory().CreateDocument();
Encoding designerCodeFileEncoding;
OpenedFile designerCodeFile;
IDocument designerCodeFileDocument;
readonly Dictionary<Type, TypeDescriptionProvider> addedTypeDescriptionProviders = new Dictionary<Type, TypeDescriptionProvider>(); readonly Dictionary<Type, TypeDescriptionProvider> addedTypeDescriptionProviders = new Dictionary<Type, TypeDescriptionProvider>();
@ -64,11 +60,11 @@ namespace ICSharpCode.FormsDesigner
} }
public OpenedFile DesignerCodeFile { public OpenedFile DesignerCodeFile {
get { return this.designerCodeFile; } get { return this.sourceCodeStorage.DesignerCodeFile; }
} }
public virtual IDocument PrimaryFileDocument { public IDocument PrimaryFileDocument {
get { return this.primaryFileDocument; } get { return this.sourceCodeStorage[this.PrimaryFile]; }
} }
public string PrimaryFileContent { public string PrimaryFileContent {
@ -76,12 +72,14 @@ namespace ICSharpCode.FormsDesigner
set { this.PrimaryFileDocument.TextContent = value; } set { this.PrimaryFileDocument.TextContent = value; }
} }
public virtual Encoding PrimaryFileEncoding { public IDocument DesignerCodeFileDocument {
get { return this.primaryFileEncoding; } get {
if (this.sourceCodeStorage.DesignerCodeFile == null) {
return null;
} else {
return this.sourceCodeStorage[this.sourceCodeStorage.DesignerCodeFile];
}
} }
public virtual IDocument DesignerCodeFileDocument {
get { return this.designerCodeFileDocument; }
} }
public string DesignerCodeFileContent { public string DesignerCodeFileContent {
@ -89,19 +87,17 @@ namespace ICSharpCode.FormsDesigner
set { this.DesignerCodeFileDocument.TextContent = value; } set { this.DesignerCodeFileDocument.TextContent = value; }
} }
public virtual Encoding DesignerCodeFileEncoding {
get { return this.designerCodeFileEncoding; }
}
public IDocument GetDocumentForFile(OpenedFile file) public IDocument GetDocumentForFile(OpenedFile file)
{ {
if (file == this.DesignerCodeFile) { return this.sourceCodeStorage[file];
return this.DesignerCodeFileDocument; }
} else if (file == this.PrimaryFile) {
return this.PrimaryFileDocument; public IEnumerable<KeyValuePair<OpenedFile, IDocument>> SourceFiles {
} else { get { return this.sourceCodeStorage; }
return null;
} }
protected DesignerSourceCodeStorage SourceCodeStorage {
get { return this.sourceCodeStorage; }
} }
public IViewContent PrimaryViewContent { public IViewContent PrimaryViewContent {
@ -131,6 +127,7 @@ namespace ICSharpCode.FormsDesigner
this.UserControl = this.pleaseWaitLabel; this.UserControl = this.pleaseWaitLabel;
this.sourceCodeStorage = new DesignerSourceCodeStorage();
this.resourceStore = new ResourceStore(this); this.resourceStore = new ResourceStore(this);
// null check is required to support running in unit test mode // null check is required to support running in unit test mode
@ -162,59 +159,73 @@ namespace ICSharpCode.FormsDesigner
public FormsDesignerViewContent(IViewContent primaryViewContent, OpenedFile mockFile) public FormsDesignerViewContent(IViewContent primaryViewContent, OpenedFile mockFile)
: this(primaryViewContent) : this(primaryViewContent)
{ {
this.primaryFileDocument = new DocumentFactory().CreateDocument(); this.sourceCodeStorage.AddFile(mockFile, Encoding.UTF8);
this.designerCodeFileDocument = this.primaryFileDocument; this.sourceCodeStorage.DesignerCodeFile = mockFile;
this.designerCodeFileEncoding = System.Text.Encoding.UTF8;
this.designerCodeFile = mockFile;
} }
bool inMasterLoadOperation;
protected override void LoadInternal(OpenedFile file, System.IO.Stream stream) 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) {
this.primaryFileEncoding = ParserService.DefaultFileEncoding; if (this.sourceCodeStorage.ContainsFile(file)) {
this.primaryFileDocument.TextContent = FileReader.ReadFileContent(stream, ref this.primaryFileEncoding); 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);
}
LoggingService.Debug("Forms designer: Determining designer code file for " + file.FileName); } else if (file == this.PrimaryFile || this.sourceCodeStorage.ContainsFile(file)) {
OpenedFile newDesignerCodeFile = this.generator.DetermineDesignerCodeFile();
if (newDesignerCodeFile == null) { if (this.loader != null && this.loader.Loading) {
throw new InvalidOperationException("The designer code file could not be determined."); 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) { if (this.designSurface != null) {
this.Files.Remove(this.designerCodeFile); this.UnloadDesigner();
} }
this.designerCodeFile = newDesignerCodeFile;
if (this.designerCodeFile == this.PrimaryFile) { this.inMasterLoadOperation = true;
LoggingService.Debug("Forms designer: Designer code file is equal to primary file. Reloading designer."); try {
this.UnloadDesigner(); this.sourceCodeStorage.LoadFile(file, stream);
this.designerCodeFileEncoding = this.PrimaryFileEncoding;
this.designerCodeFileDocument = this.PrimaryFileDocument;
this.LoadAndDisplayDesigner();
} else if (!this.Files.Contains(this.designerCodeFile)) { LoggingService.Debug("Forms designer: Determining designer source files for " + file.FileName);
LoggingService.Debug("Forms designer: Adding designer code file " + this.designerCodeFile.FileName); OpenedFile newDesignerCodeFile;
this.Files.Insert(1, this.designerCodeFile); IEnumerable<OpenedFile> sourceFiles = this.generator.GetSourceFiles(out newDesignerCodeFile);
} else if (this.HasLoadError || this.designSurface == null || !this.designSurface.IsLoaded) { if (sourceFiles == null || newDesignerCodeFile == null) {
LoggingService.Debug("Forms designer: Having a load error. Reloading designer."); throw new FormsDesignerLoadException("The designer source files could not be determined.");
this.ReloadDesignerFromMemory();
} }
} else if (file == this.DesignerCodeFile) { // 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);
}
LoggingService.Debug("Forms designer: Reloading designer because of LoadInternal on DesignerCodeFile"); this.sourceCodeStorage.DesignerCodeFile = newDesignerCodeFile;
this.UnloadDesigner();
this.designerCodeFileEncoding = ParserService.DefaultFileEncoding;
this.designerCodeFileDocument = new DocumentFactory().CreateDocument();
this.designerCodeFileDocument.TextContent = FileReader.ReadFileContent(stream, ref this.designerCodeFileEncoding);
this.LoadAndDisplayDesigner(); this.LoadAndDisplayDesigner();
} finally {
this.inMasterLoadOperation = false;
}
} else { } else {
// Loading a resource file // Loading a resource file
@ -224,14 +235,20 @@ namespace ICSharpCode.FormsDesigner
LoggingService.Debug("Forms designer: Reloading designer because of LoadInternal on resource file"); LoggingService.Debug("Forms designer: Reloading designer because of LoadInternal on resource file");
this.UnloadDesigner(); this.UnloadDesigner();
mustReload = true; mustReload = true;
this.inMasterLoadOperation = true;
} else { } else {
mustReload = false; mustReload = false;
} }
try {
LoggingService.Debug("Forms designer: Loading " + file.FileName + " in resource store"); LoggingService.Debug("Forms designer: Loading " + file.FileName + " in resource store");
this.resourceStore.Load(file, stream); this.resourceStore.Load(file, stream);
if (mustReload) { if (mustReload) {
this.LoadAndDisplayDesigner(); this.LoadAndDisplayDesigner();
} }
} finally {
this.inMasterLoadOperation = false;
}
} }
} }
@ -242,14 +259,8 @@ namespace ICSharpCode.FormsDesigner
if (hasUnmergedChanges) { if (hasUnmergedChanges) {
this.MergeFormChanges(); this.MergeFormChanges();
} }
if (file == this.DesignerCodeFile) { if (this.sourceCodeStorage.ContainsFile(file)) {
using(StreamWriter writer = new StreamWriter(stream, this.DesignerCodeFileEncoding)) { this.sourceCodeStorage.SaveFile(file, stream);
writer.Write(this.DesignerCodeFileContent);
}
} else if (file == this.PrimaryFile) {
using(StreamWriter writer = new StreamWriter(stream, this.PrimaryFileEncoding)) {
writer.Write(this.PrimaryFileContent);
}
} else { } else {
this.resourceStore.Save(file, stream); this.resourceStore.Save(file, stream);
} }
@ -516,6 +527,7 @@ namespace ICSharpCode.FormsDesigner
void DesignerFlushed(object sender, EventArgs e) void DesignerFlushed(object sender, EventArgs e)
{ {
this.resourceStore.CommitAllResourceChanges(); this.resourceStore.CommitAllResourceChanges();
this.hasUnmergedChanges = false;
} }
static string FormatLoadErrors(DesignSurface designSurface) static string FormatLoadErrors(DesignSurface designSurface)
@ -530,8 +542,11 @@ namespace ICSharpCode.FormsDesigner
public virtual void MergeFormChanges() 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; return;
} else if (this.DesignerCodeFile == null) {
throw new InvalidOperationException("Cannot merge form changes without a designer code file.");
} }
bool isDirty = this.DesignerCodeFile.IsDirty; bool isDirty = this.DesignerCodeFile.IsDirty;
LoggingService.Info("Merging form changes..."); LoggingService.Info("Merging form changes...");
@ -816,7 +831,7 @@ namespace ICSharpCode.FormsDesigner
protected void ReloadDesignerFromMemory() 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); this.Load(this.DesignerCodeFile, ms);
} }
@ -829,7 +844,7 @@ namespace ICSharpCode.FormsDesigner
void FileServiceFileRemoving(object sender, FileCancelEventArgs e) void FileServiceFileRemoving(object sender, FileCancelEventArgs e)
{ {
if (!e.Cancel && this.DesignerCodeFile != null) { if (!e.Cancel) {
if (WorkbenchSingleton.InvokeRequired) { if (WorkbenchSingleton.InvokeRequired) {
WorkbenchSingleton.SafeThreadAsyncCall(this.CheckForDesignerCodeFileDeletion, e); WorkbenchSingleton.SafeThreadAsyncCall(this.CheckForDesignerCodeFileDeletion, e);
} else { } else {
@ -840,27 +855,33 @@ namespace ICSharpCode.FormsDesigner
void CheckForDesignerCodeFileDeletion(FileCancelEventArgs e) void CheckForDesignerCodeFileDeletion(FileCancelEventArgs e)
{ {
if (this.DesignerCodeFile == null || String.IsNullOrEmpty(this.DesignerCodeFile.FileName)) { OpenedFile file;
return;
}
if ((!e.IsDirectory && FileUtility.IsEqualFileName(this.DesignerCodeFile.FileName, e.FileName)) || if (e.IsDirectory) {
(e.IsDirectory && FileUtility.IsBaseDirectory(e.FileName, this.DesignerCodeFile.FileName))) { file = this.Files.SingleOrDefault(
f => FileUtility.IsBaseDirectory(e.FileName, f.FileName)
);
} else {
file = this.Files.SingleOrDefault(
f => FileUtility.IsEqualFileName(f.FileName, e.FileName)
);
}
LoggingService.Info("Forms designer: Handling deletion of open designer code file '" + this.DesignerCodeFile.FileName + "'"); if (file == null || file == this.PrimaryFile)
return;
// When our designer code file is deleted, LoggingService.Info("Forms designer: Handling deletion of open designer code file '" + file.FileName + "'");
// 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 == this.sourceCodeStorage.DesignerCodeFile) {
this.UnloadDesigner(); this.UnloadDesigner();
this.Files.Remove(this.DesignerCodeFile); this.sourceCodeStorage.DesignerCodeFile = null;
this.designerCodeFile = null;
this.designerCodeFileDocument = null;
this.designerCodeFileEncoding = 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) #region Design surface manager (static)

Loading…
Cancel
Save