//
//
//
//
// $Revision$
//
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.ComponentModel.Design.Serialization;
using System.Drawing;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
using ICSharpCode.FormsDesigner;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.RubyBinding
{
public interface IRubyDesignerGenerator : IDesignerGenerator
{
///
/// Updates the Ruby form or user control's InitializeComponent method with any
/// changes to the designed form or user control.
///
void MergeRootComponentChanges(IDesignerHost host, IDesignerSerializationManager serializationManager);
}
///
/// Form's designer generator for the Ruby language.
///
public class RubyDesignerGenerator : IRubyDesignerGenerator
{
FormsDesignerViewContent viewContent;
ITextEditorOptions textEditorOptions;
public RubyDesignerGenerator(ITextEditorOptions textEditorOptions)
{
this.textEditorOptions = textEditorOptions;
}
///
/// Gets the Ruby code dom provider.
///
public CodeDomProvider CodeDomProvider {
get { return null; }
}
public void Attach(FormsDesignerViewContent viewContent)
{
this.viewContent = viewContent;
}
public void Detach()
{
this.viewContent = null;
}
public IEnumerable GetSourceFiles(out OpenedFile designerCodeFile)
{
designerCodeFile = this.ViewContent.PrimaryFile;
return new [] {designerCodeFile};
}
public void MergeFormChanges(CodeCompileUnit unit)
{
}
public void NotifyFormRenamed(string newName)
{
}
///
/// Updates the InitializeComponent method's body with the generated code.
///
public void MergeRootComponentChanges(IDesignerHost host, IDesignerSerializationManager serializationManager)
{
ParseInformation parseInfo = ParseFile();
Merge(host, ViewContent.DesignerCodeFileDocument, parseInfo.CompilationUnit, textEditorOptions, serializationManager);
}
///
/// Merges the generated code into the specified document.
///
/// The designer host.
/// The document that the generated code will be merged into.
/// The current compilation unit for the .
public static void Merge(IDesignerHost host, IDocument document, ICompilationUnit compilationUnit, ITextEditorOptions textEditorOptions, IDesignerSerializationManager serializationManager)
{
// Get the document's initialize components method.
IMethod method = GetInitializeComponents(compilationUnit);
// Generate the Ruby source code.
RubyCodeDomSerializer serializer = new RubyCodeDomSerializer(textEditorOptions.IndentationString);
int indent = method.Region.BeginColumn;
if (textEditorOptions.ConvertTabsToSpaces) {
indent = (indent / textEditorOptions.IndentationSize);
if (textEditorOptions.IndentationSize > 1) {
indent += 1;
}
}
string rootNamespace = GetProjectRootNamespace(compilationUnit);
string methodBody = serializer.GenerateInitializeComponentMethodBody(host, serializationManager, rootNamespace, indent);
// Merge the code.
DomRegion methodRegion = GetBodyRegionInDocument(method);
int startOffset = GetStartOffset(document, methodRegion);
int endOffset = GetEndOffset(document, methodRegion);
document.Replace(startOffset, endOffset - startOffset, methodBody);
}
///
/// Inserts an event handler.
///
public bool InsertComponentEvent(IComponent component, EventDescriptor edesc, string eventMethodName, string body, out string file, out int position)
{
position = GetExistingEventHandler(eventMethodName);
if (position == -1) {
// Ensure the text editor has the latest version
// of the source code before we insert any new code.
viewContent.MergeFormChanges();
// Insert the event handler at the end of the class with an extra
// new line before it.
IDocument doc = viewContent.DesignerCodeFileDocument;
string eventHandler = CreateEventHandler(eventMethodName, body, textEditorOptions.IndentationString);
IDocumentLine classEndLine = GetClassEndLine(doc);
InsertEventHandlerBeforeLine(doc, eventHandler, classEndLine);
// Set position so it points to the line
// where the event handler was inserted.
position = classEndLine.LineNumber;
}
// Set the filename so it refers to the form being designed.
file = viewContent.DesignerCodeFile.FileName;
return true;
}
IDocumentLine GetClassEndLine(IDocument doc)
{
int line = doc.TotalNumberOfLines;
while (line > 0) {
IDocumentLine documentLine = doc.GetLine(line);
if (documentLine.Text.Trim() == "end") {
return documentLine;
}
line--;
}
return doc.GetLine(doc.TotalNumberOfLines);
}
void InsertEventHandlerBeforeLine(IDocument doc, string eventHandler, IDocumentLine classEndLine)
{
string newContent = "\r\n" + eventHandler + "\r\n";
int offset = classEndLine.Offset;
doc.Insert(offset, newContent);
}
///
/// Returns a list of method names that could be used as an
/// event handler with the specified event.
///
public ICollection GetCompatibleMethods(EventDescriptor edesc)
{
// Get the form or user control class.
ParseInformation parseInfo = ParseFile();
// Look at the form's methods and see which are compatible.
ArrayList methods = new ArrayList();
IClass c = GetClass(parseInfo.CompilationUnit);
foreach (IMethod method in c.Methods) {
if (method.Parameters.Count == 2) {
methods.Add(method.Name);
}
}
return methods;
}
///
/// Gets the non-generated InitializeComponents from the compilation unit.
///
public static IMethod GetInitializeComponents(ICompilationUnit unit)
{
foreach (IClass c in unit.Classes) {
if (FormsDesignerSecondaryDisplayBinding.BaseClassIsFormOrControl(c)) {
IMethod method = FormsDesignerSecondaryDisplayBinding.GetInitializeComponents(c);
if (method != null) {
return method;
}
}
}
return null;
}
///
/// Converts from the DOM region to a document region.
///
public static DomRegion GetBodyRegionInDocument(IMethod method)
{
DomRegion bodyRegion = method.BodyRegion;
return new DomRegion(bodyRegion.BeginLine + 1, 1, bodyRegion.EndLine, 1);
}
///
/// Gets the view content attached to this generator.
///
public FormsDesignerViewContent ViewContent {
get { return viewContent; }
}
///
/// The default implementation calls the ParserService.ParseFile. This
/// method is overridable so the class can be easily tested without
/// the ParserService being required.
///
protected virtual ParseInformation ParseFile(string fileName, string textContent)
{
return ParserService.ParseFile(fileName, new StringTextBuffer(textContent));
}
///
/// Returns the generated event handler.
///
/// The indent string to use for the event handler.
protected string CreateEventHandler(string eventMethodName, string body, string indentation)
{
if (String.IsNullOrEmpty(body)) {
body = String.Empty;
}
StringBuilder eventHandler = new StringBuilder();
eventHandler.Append(indentation);
eventHandler.Append("def ");
eventHandler.Append(eventMethodName);
eventHandler.Append("(sender, e)");
eventHandler.AppendLine();
eventHandler.Append(indentation);
eventHandler.Append(textEditorOptions.IndentationString);
eventHandler.Append(body);
eventHandler.AppendLine();
eventHandler.Append(indentation);
eventHandler.Append("end");
return eventHandler.ToString();
}
///
/// Gets the form or user control class from the compilation unit.
///
IClass GetClass(ICompilationUnit unit)
{
return unit.Classes[0];
}
///
/// Gets the start offset of the region.
///
static int GetStartOffset(IDocument document, DomRegion region)
{
return document.PositionToOffset(region.BeginLine, region.BeginColumn);
}
///
/// Gets the end offset of the region.
///
static int GetEndOffset(IDocument document, DomRegion region)
{
if (region.EndLine > document.TotalNumberOfLines) {
// At end of document.
return document.TextLength;
}
return document.PositionToOffset(region.EndLine, region.EndColumn);
}
///
/// Checks if the event handler already exists.
///
/// The line position of the first line of the existing event handler.
int GetExistingEventHandler(string methodName)
{
ParseInformation parseInfo = ParseFile();
IClass c = GetClass(parseInfo.CompilationUnit);
foreach (IMethod method in c.Methods) {
if ((method.Name == methodName) && (method.Parameters.Count == 2)) {
return method.Region.BeginLine;
}
}
return -1;
}
///
/// Parses the form or user control being designed.
///
ParseInformation ParseFile()
{
return ParseFile(this.ViewContent.DesignerCodeFile.FileName, this.ViewContent.DesignerCodeFileContent);
}
static string GetProjectRootNamespace(ICompilationUnit compilationUnit)
{
IProject project = compilationUnit.ProjectContent.Project as IProject;
if (project != null) {
return project.RootNamespace;
}
return String.Empty;
}
}
}