From f3939e325783ce47ba29c19eec7851be573e2165 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 4 Nov 2006 14:44:18 +0000 Subject: [PATCH] Add Custom Tools support. git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@2020 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- AddIns/ICSharpCode.SharpDevelop.addin | 4 + .../Src/PropertyGrid/DropDownEditor.cs | 67 +-- .../Project/Src/PropertyGrid/GuidEditor.cs | 1 + .../WixXmlAttributePropertyDescriptor.cs | 2 +- .../WixBinding/Project/WixBinding.csproj | 1 - .../Project/ResourceEditor.addin | 6 + .../Project/ResourceEditor.csproj | 7 + .../Project/Src/ResourceCodeGeneratorTool.cs | 57 ++ .../Project/SettingsDocument.cs | 76 +++ .../Project/SettingsEditor.csproj | 1 + .../SettingsEditor/Project/SettingsEntry.cs | 9 +- .../Project/SettingsView.Designer.cs | 2 + .../Project/SettingsViewContent.cs | 39 +- .../SettingsEditor/Project/SpecialTypes.cs | 13 +- .../Project/ICSharpCode.SharpDevelop.csproj | 1 + .../Project/Src/Gui/Dialogs/NewFileDialog.cs | 17 +- .../Base/Project/Src/Gui/IProgressMonitor.cs | 26 + .../MessageViewCategory.cs | 5 + .../Project/Src/Gui/WorkbenchSingleton.cs | 8 + .../Base/Project/Src/Project/CustomTool.cs | 496 ++++++++++++++++++ .../Src/Project/Items/FileProjectItem.cs | 20 +- .../ParserService/ParseProjectContent.cs | 4 +- src/Main/Base/Project/Src/Util/Linq.cs | 25 + .../DesignTimeSupport/DropDownEditor.cs | 78 +++ .../DropDownEditorListBox.cs | 24 +- .../ICSharpCode.SharpDevelop.Widgets.csproj | 3 + 26 files changed, 874 insertions(+), 118 deletions(-) create mode 100644 src/AddIns/DisplayBindings/ResourceEditor/Project/Src/ResourceCodeGeneratorTool.cs create mode 100644 src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsDocument.cs create mode 100644 src/Main/Base/Project/Src/Project/CustomTool.cs create mode 100644 src/Main/ICSharpCode.SharpDevelop.Widgets/Project/DesignTimeSupport/DropDownEditor.cs rename src/{AddIns/BackendBindings/WixBinding/Project/Src/PropertyGrid => Main/ICSharpCode.SharpDevelop.Widgets/Project/DesignTimeSupport}/DropDownEditorListBox.cs (72%) diff --git a/AddIns/ICSharpCode.SharpDevelop.addin b/AddIns/ICSharpCode.SharpDevelop.addin index 526c9e5a25..e7af6a2116 100644 --- a/AddIns/ICSharpCode.SharpDevelop.addin +++ b/AddIns/ICSharpCode.SharpDevelop.addin @@ -28,6 +28,7 @@ + @@ -383,6 +384,9 @@ + diff --git a/src/AddIns/BackendBindings/WixBinding/Project/Src/PropertyGrid/DropDownEditor.cs b/src/AddIns/BackendBindings/WixBinding/Project/Src/PropertyGrid/DropDownEditor.cs index 56385146c0..195071f0f9 100644 --- a/src/AddIns/BackendBindings/WixBinding/Project/Src/PropertyGrid/DropDownEditor.cs +++ b/src/AddIns/BackendBindings/WixBinding/Project/Src/PropertyGrid/DropDownEditor.cs @@ -6,76 +6,31 @@ // using System; +using System.Collections.Generic; using System.ComponentModel; using System.Drawing.Design; using System.Windows.Forms; using System.Windows.Forms.Design; +using ICSharpCode.SharpDevelop.Widgets.DesignTimeSupport; namespace ICSharpCode.WixBinding { - public class DropDownEditor : UITypeEditor + public class WixDropDownEditor : DropDownEditor { - /// - /// Returns the drop down style. - /// - public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) + protected override Control CreateDropDownControl(ITypeDescriptorContext context, IWindowsFormsEditorService editorService) { - return UITypeEditorEditStyle.DropDown; + return new DropDownEditorListBox(editorService, GetDropDownItems(context)); } - public override bool IsDropDownResizable { - get { - return false; - } - } - - /// - /// Shows the drop down editor control in the drop down so the user - /// can change the value. - /// - public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) + IEnumerable GetDropDownItems(ITypeDescriptorContext context) { - IWindowsFormsEditorService editorService = null; - - if (provider != null) { - editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService)); - } - - if (editorService != null) { - using (Control control = CreateDropDownControl(context, editorService)) { - SetValue(control, value); - editorService.DropDownControl(control); - value = GetValue(control); + if (context != null) { + WixXmlAttributePropertyDescriptor propertyDescriptor = context.PropertyDescriptor as WixXmlAttributePropertyDescriptor; + if (propertyDescriptor != null && propertyDescriptor.WixXmlAttribute.HasValues) { + return propertyDescriptor.WixXmlAttribute.Values; } } - - return value; - } - - /// - /// Creates the drop down control. - /// - protected virtual Control CreateDropDownControl(ITypeDescriptorContext context, IWindowsFormsEditorService editorService) - { - return new DropDownEditorListBox(context, editorService); - } - - /// - /// Sets the current value in the drop down control. - /// - protected virtual void SetValue(Control control, object value) - { - DropDownEditorListBox listBox = (DropDownEditorListBox)control; - listBox.Value = (string)value; - } - - /// - /// Gets the current value from the drop down control. - /// - protected virtual object GetValue(Control control) - { - DropDownEditorListBox listBox = (DropDownEditorListBox)control; - return listBox.Value; + return new string[0]; } } } diff --git a/src/AddIns/BackendBindings/WixBinding/Project/Src/PropertyGrid/GuidEditor.cs b/src/AddIns/BackendBindings/WixBinding/Project/Src/PropertyGrid/GuidEditor.cs index a462b99fd5..15b09b46aa 100644 --- a/src/AddIns/BackendBindings/WixBinding/Project/Src/PropertyGrid/GuidEditor.cs +++ b/src/AddIns/BackendBindings/WixBinding/Project/Src/PropertyGrid/GuidEditor.cs @@ -9,6 +9,7 @@ using System; using System.ComponentModel; using System.Windows.Forms; using System.Windows.Forms.Design; +using ICSharpCode.SharpDevelop.Widgets.DesignTimeSupport; namespace ICSharpCode.WixBinding { diff --git a/src/AddIns/BackendBindings/WixBinding/Project/Src/PropertyGrid/WixXmlAttributePropertyDescriptor.cs b/src/AddIns/BackendBindings/WixBinding/Project/Src/PropertyGrid/WixXmlAttributePropertyDescriptor.cs index 5371923d13..041e4e256e 100644 --- a/src/AddIns/BackendBindings/WixBinding/Project/Src/PropertyGrid/WixXmlAttributePropertyDescriptor.cs +++ b/src/AddIns/BackendBindings/WixBinding/Project/Src/PropertyGrid/WixXmlAttributePropertyDescriptor.cs @@ -118,7 +118,7 @@ namespace ICSharpCode.WixBinding break; case WixXmlAttributeType.Text: if (wixXmlAttribute.HasValues) { - attributes.Add(new EditorAttribute(typeof(DropDownEditor), typeof(UITypeEditor))); + attributes.Add(new EditorAttribute(typeof(WixDropDownEditor), typeof(UITypeEditor))); } break; } diff --git a/src/AddIns/BackendBindings/WixBinding/Project/WixBinding.csproj b/src/AddIns/BackendBindings/WixBinding/Project/WixBinding.csproj index d310fc651f..6c4471a81d 100644 --- a/src/AddIns/BackendBindings/WixBinding/Project/WixBinding.csproj +++ b/src/AddIns/BackendBindings/WixBinding/Project/WixBinding.csproj @@ -142,7 +142,6 @@ - diff --git a/src/AddIns/DisplayBindings/ResourceEditor/Project/ResourceEditor.addin b/src/AddIns/DisplayBindings/ResourceEditor/Project/ResourceEditor.addin index edaa21ddb7..7d773820e1 100644 --- a/src/AddIns/DisplayBindings/ResourceEditor/Project/ResourceEditor.addin +++ b/src/AddIns/DisplayBindings/ResourceEditor/Project/ResourceEditor.addin @@ -62,4 +62,10 @@ + + + + diff --git a/src/AddIns/DisplayBindings/ResourceEditor/Project/ResourceEditor.csproj b/src/AddIns/DisplayBindings/ResourceEditor/Project/ResourceEditor.csproj index 5a32b7de98..7e6b3952df 100644 --- a/src/AddIns/DisplayBindings/ResourceEditor/Project/ResourceEditor.csproj +++ b/src/AddIns/DisplayBindings/ResourceEditor/Project/ResourceEditor.csproj @@ -39,6 +39,7 @@ + @@ -84,6 +85,7 @@ Configuration\GlobalAssemblyInfo.cs + @@ -96,6 +98,11 @@ ICSharpCode.Core False + + {924EE450-603D-49C1-A8E5-4AFAA31CE6F3} + ICSharpCode.SharpDevelop.Dom + False + \ No newline at end of file diff --git a/src/AddIns/DisplayBindings/ResourceEditor/Project/Src/ResourceCodeGeneratorTool.cs b/src/AddIns/DisplayBindings/ResourceEditor/Project/Src/ResourceCodeGeneratorTool.cs new file mode 100644 index 0000000000..8b76ed52b6 --- /dev/null +++ b/src/AddIns/DisplayBindings/ResourceEditor/Project/Src/ResourceCodeGeneratorTool.cs @@ -0,0 +1,57 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections; +using ICSharpCode.SharpDevelop.Project; +using System.Resources; +using System.Resources.Tools; +using System.IO; + +namespace ResourceEditor +{ + public class ResourceCodeGeneratorTool : ICustomTool + { + public void GenerateCode(FileProjectItem item, CustomToolContext context) + { + /*context.GenerateCodeDomAsync(item, context.GetOutputFileName(item, ".Designer"), + delegate { + return GenerateCodeDom(); + });*/ + string inputFilePath = item.FileName; + + IResourceReader reader; + if (Path.GetExtension(inputFilePath) == ".resx") { + reader = new ResXResourceReader(inputFilePath); + } else { + reader = new ResourceReader(inputFilePath); + } + + Hashtable resources = new Hashtable(); + foreach (DictionaryEntry de in reader) { + resources.Add(de.Key, de.Value); + } + + string[] unmatchable = null; + + context.WriteCodeDomToFile( + item, + context.GetOutputFileName(item, ".Designer"), + StronglyTypedResourceBuilder.Create( + resources, // resourceList + "Resources", // baseName + context.OutputNamespace, // generatedCodeNamespace + context.OutputNamespace, // resourcesNamespace + context.Project.LanguageProperties.CodeDomProvider, // codeProvider + true, // internal class + out unmatchable + )); + } + + + } +} diff --git a/src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsDocument.cs b/src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsDocument.cs new file mode 100644 index 0000000000..651c5fc2b7 --- /dev/null +++ b/src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsDocument.cs @@ -0,0 +1,76 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Xml; +using System.Collections.Generic; + +namespace ICSharpCode.SettingsEditor +{ + public class SettingsDocument + { + string generatedClassNamespace = ""; + string generatedClassName = ""; + List entries = new List(); + + public string GeneratedClassNamespace { + get { return generatedClassNamespace; } + set { generatedClassNamespace = value ?? ""; } + } + + public string GeneratedClassName { + get { return generatedClassName; } + set { generatedClassName = value ?? ""; } + } + + public List Entries { + get { return entries; } + } + + public SettingsDocument() + { + } + + const string XmlNamespace = "http://schemas.microsoft.com/VisualStudio/2004/01/settings"; + + public SettingsDocument(XmlElement settingsFile, ISettingsEntryHost host) + { + generatedClassNamespace = settingsFile.GetAttribute("GeneratedClassNamespace"); + generatedClassName = settingsFile.GetAttribute("GeneratedClassName"); + + XmlElement settings = settingsFile["Settings"]; + + foreach (XmlNode node in settings.ChildNodes) { + if (node is XmlElement) { + entries.Add(new SettingsEntry(host, node as XmlElement)); + } + } + } + + public void Save(XmlWriter writer) + { + writer.WriteStartElement("SettingsFile", XmlNamespace); + writer.WriteAttributeString("CurrentProfile", "(Default)"); + writer.WriteAttributeString("GeneratedClassNamespace", generatedClassNamespace); + writer.WriteAttributeString("GeneratedClassName", generatedClassName); + + writer.WriteStartElement("Profiles"); + writer.WriteStartElement("Profile"); + writer.WriteAttributeString("Name", "(Default)"); + writer.WriteEndElement(); // Profile + writer.WriteEndElement(); // Profiles + + writer.WriteStartElement("Settings"); + foreach (SettingsEntry e in entries) { + e.WriteTo(writer); + } + writer.WriteEndElement(); // Settings + + writer.WriteEndElement(); // SettingsFile + } + } +} diff --git a/src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsEditor.csproj b/src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsEditor.csproj index c0c184f7a2..5f9a2962d4 100644 --- a/src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsEditor.csproj +++ b/src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsEditor.csproj @@ -63,6 +63,7 @@ + diff --git a/src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsEntry.cs b/src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsEntry.cs index c51eb1bc64..e2e1d04973 100644 --- a/src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsEntry.cs +++ b/src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsEntry.cs @@ -8,6 +8,7 @@ */ using System; +using System.Collections.Generic; using System.ComponentModel; using System.Xml; using System.Configuration; @@ -70,13 +71,7 @@ namespace ICSharpCode.SettingsEditor scope = SettingScope.User; } type = GetType(element.GetAttribute("Type")); - if (type != null && type != typeof(string)) { - SettingsPropertyValue v = GetSettingConverter(type, name); - v.SerializedValue = element["Value"].InnerText; - this.value = v.PropertyValue; - } else { - this.value = element["Value"].InnerText; - } + this.SerializedValue = element["Value"].InnerText; } static SettingsPropertyValue GetSettingConverter(Type type, string name) diff --git a/src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsView.Designer.cs b/src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsView.Designer.cs index 67a7a49d89..645f31b16f 100644 --- a/src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsView.Designer.cs +++ b/src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsView.Designer.cs @@ -97,6 +97,7 @@ namespace ICSharpCode.SettingsEditor this.TypeColumn.HeaderText = "Type"; this.TypeColumn.MinimumWidth = 50; this.TypeColumn.Name = "TypeColumn"; + this.TypeColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic; // // ScopeColumn // @@ -105,6 +106,7 @@ namespace ICSharpCode.SettingsEditor this.ScopeColumn.HeaderText = "Scope"; this.ScopeColumn.MinimumWidth = 30; this.ScopeColumn.Name = "ScopeColumn"; + this.ScopeColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic; // // ValueColumn // diff --git a/src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsViewContent.cs b/src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsViewContent.cs index c0f6a83bf4..4b4f977274 100644 --- a/src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsViewContent.cs +++ b/src/AddIns/DisplayBindings/SettingsEditor/Project/SettingsViewContent.cs @@ -23,6 +23,7 @@ namespace ICSharpCode.SettingsEditor { SettingsView view = new SettingsView(); PropertyContainer propertyContainer = new PropertyContainer(); + SettingsDocument setDoc = new SettingsDocument(); public SettingsViewContent() { @@ -48,43 +49,35 @@ namespace ICSharpCode.SettingsEditor try { XmlDocument doc = new XmlDocument(); doc.Load(filename); - XmlElement settings = doc.DocumentElement["Settings"]; - List entries = new List(); - foreach (XmlNode node in settings.ChildNodes) { - if (node is XmlElement) { - entries.Add(new SettingsEntry(view, node as XmlElement)); - } - } - view.ShowEntries(entries); + + setDoc = new SettingsDocument(doc.DocumentElement, view); + view.ShowEntries(setDoc.Entries); } catch (XmlException ex) { - MessageService.ShowMessage(ex.Message); + ShowLoadError(ex.Message); } IsDirty = false; } - const string XmlNamespace = "http://schemas.microsoft.com/VisualStudio/2004/01/settings"; + void ShowLoadError(string message) + { + MessageService.ShowMessage(message); + if (this.WorkbenchWindow != null) { + this.WorkbenchWindow.CloseWindow(true); + } + } + public override void Save(string fileName) { using (XmlTextWriter writer = new XmlTextWriter(fileName, Encoding.UTF8)) { writer.Formatting = Formatting.Indented; writer.WriteStartDocument(); - writer.WriteStartElement("SettingsFile", XmlNamespace); - writer.WriteAttributeString("CurrentProfile", "(Default)"); - writer.WriteStartElement("Profiles"); - writer.WriteStartElement("Profile"); - writer.WriteAttributeString("Name", "(Default)"); - writer.WriteEndElement(); // Profile - writer.WriteEndElement(); // Profiles + setDoc.Entries.Clear(); + setDoc.Entries.AddRange(view.GetAllEntries()); - writer.WriteStartElement("Settings"); - foreach (SettingsEntry e in view.GetAllEntries()) { - e.WriteTo(writer); - } - writer.WriteEndElement(); // Settings + setDoc.Save(writer); - writer.WriteEndElement(); // SettingsFile writer.WriteEndDocument(); } IsDirty = false; diff --git a/src/AddIns/DisplayBindings/SettingsEditor/Project/SpecialTypes.cs b/src/AddIns/DisplayBindings/SettingsEditor/Project/SpecialTypes.cs index 83c03d7a21..13f489d9d1 100644 --- a/src/AddIns/DisplayBindings/SettingsEditor/Project/SpecialTypes.cs +++ b/src/AddIns/DisplayBindings/SettingsEditor/Project/SpecialTypes.cs @@ -8,6 +8,7 @@ using System; using System.ComponentModel; using System.Reflection; +using System.Configuration; namespace ICSharpCode.SettingsEditor { @@ -15,11 +16,13 @@ namespace ICSharpCode.SettingsEditor { internal string name; internal Type type; + internal SpecialSetting specialSetting; - internal SpecialTypeDescriptor(string name, Type type) + internal SpecialTypeDescriptor(string name, Type type, SpecialSetting specialSetting) { this.name = name; this.type = type; + this.specialSetting = specialSetting; } public string GetString(object value) @@ -38,8 +41,12 @@ namespace ICSharpCode.SettingsEditor } internal static readonly SpecialTypeDescriptor[] Descriptors = { - new SpecialTypeDescriptor("(Web Service URL)", typeof(WebServiceUrlDummyType)), - new SpecialTypeDescriptor("(Connection string)", typeof(ConnectionStringDummyType)), + new SpecialTypeDescriptor("(Web Service URL)", + typeof(WebServiceUrlDummyType), + SpecialSetting.WebServiceUrl), + new SpecialTypeDescriptor("(Connection string)", + typeof(ConnectionStringDummyType), + SpecialSetting.ConnectionString) }; } diff --git a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj index 9b28d9c0e8..4d01683db6 100644 --- a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj +++ b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj @@ -688,6 +688,7 @@ + diff --git a/src/Main/Base/Project/Src/Gui/Dialogs/NewFileDialog.cs b/src/Main/Base/Project/Src/Gui/Dialogs/NewFileDialog.cs index d26a2ad6e1..41ad96956b 100644 --- a/src/Main/Base/Project/Src/Gui/Dialogs/NewFileDialog.cs +++ b/src/Main/Base/Project/Src/Gui/Dialogs/NewFileDialog.cs @@ -396,7 +396,7 @@ namespace ICSharpCode.SharpDevelop.Gui createdFiles.Add(new KeyValuePair(parsedFileName, newfile.CreateMSBuildProperties())); } - string GenerateValidClassName(string className) + internal static string GenerateValidClassName(string className) { int idx = 0; while (idx < className.Length && className[idx] != '_' && !Char.IsLetter(className[idx])) { @@ -449,20 +449,7 @@ namespace ICSharpCode.SharpDevelop.Gui fileName = Path.GetFullPath(fileName); IProject project = ProjectService.CurrentProject; if (project != null) { - string relPath = FileUtility.GetRelativePath(project.Directory, Path.GetDirectoryName(fileName)); - string[] subdirs = relPath.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); - StringBuilder standardNameSpace = new StringBuilder(project.RootNamespace); - foreach(string subdir in subdirs) { - if (subdir == "." || subdir == ".." || subdir.Length == 0) - continue; - if (subdir.Equals("src", StringComparison.OrdinalIgnoreCase)) - continue; - if (subdir.Equals("source", StringComparison.OrdinalIgnoreCase)) - continue; - standardNameSpace.Append('.'); - standardNameSpace.Append(GenerateValidClassName(subdir)); - } - StringParser.Properties["StandardNamespace"] = standardNameSpace.ToString(); + StringParser.Properties["StandardNamespace"] = CustomToolsService.GetDefaultNamespace(project, fileName); } } StringParser.Properties["FullName"] = fileName; diff --git a/src/Main/Base/Project/Src/Gui/IProgressMonitor.cs b/src/Main/Base/Project/Src/Gui/IProgressMonitor.cs index 4f2cb60786..7c2669da63 100644 --- a/src/Main/Base/Project/Src/Gui/IProgressMonitor.cs +++ b/src/Main/Base/Project/Src/Gui/IProgressMonitor.cs @@ -27,4 +27,30 @@ namespace ICSharpCode.SharpDevelop.Gui set; } } + + internal class DummyProgressMonitor : IProgressMonitor + { + int workDone; + string taskName; + + public int WorkDone { + get { return workDone; } + set { workDone = value; } + } + + public string TaskName { + get { return taskName; } + set { taskName = value; } + } + + public void BeginTask(string name, int totalWork) + { + taskName = name; + workDone = 0; + } + + public void Done() + { + } + } } diff --git a/src/Main/Base/Project/Src/Gui/Pads/CompilerMessageView/MessageViewCategory.cs b/src/Main/Base/Project/Src/Gui/Pads/CompilerMessageView/MessageViewCategory.cs index 6287f275c7..d76c54d190 100644 --- a/src/Main/Base/Project/Src/Gui/Pads/CompilerMessageView/MessageViewCategory.cs +++ b/src/Main/Base/Project/Src/Gui/Pads/CompilerMessageView/MessageViewCategory.cs @@ -50,6 +50,11 @@ namespace ICSharpCode.SharpDevelop.Gui this.displayCategory = displayCategory; } + public void AppendLine(string text) + { + AppendText(text + Environment.NewLine); + } + public void AppendText(string text) { lock (textBuilder) { diff --git a/src/Main/Base/Project/Src/Gui/WorkbenchSingleton.cs b/src/Main/Base/Project/Src/Gui/WorkbenchSingleton.cs index 278bd21ca7..356fb6beaf 100644 --- a/src/Main/Base/Project/Src/Gui/WorkbenchSingleton.cs +++ b/src/Main/Base/Project/Src/Gui/WorkbenchSingleton.cs @@ -71,6 +71,7 @@ namespace ICSharpCode.SharpDevelop.Gui StatusBarService.Initialize(); DomHostCallback.Register(); // must be called after StatusBarService.Initialize() ParserService.InitializeParserService(); + Project.CustomToolsService.Initialize(); workbench = new DefaultWorkbench(); MessageService.MainForm = workbench; @@ -125,6 +126,13 @@ namespace ICSharpCode.SharpDevelop.Gui } } + internal static void AssertMainThread() + { + if (InvokeRequired) { + throw new InvalidOperationException("This operation can be called on the main thread only."); + } + } + /// /// Makes a call GUI threadsafe. WARNING: This method waits for the result of the /// operation, which can result in a dead-lock when the main thread waits for a lock diff --git a/src/Main/Base/Project/Src/Project/CustomTool.cs b/src/Main/Base/Project/Src/Project/CustomTool.cs new file mode 100644 index 0000000000..dc56046cf3 --- /dev/null +++ b/src/Main/Base/Project/Src/Project/CustomTool.cs @@ -0,0 +1,496 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.CodeDom; +using System.CodeDom.Compiler; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; +using System.Text; + +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop.Gui; + +namespace ICSharpCode.SharpDevelop.Project +{ + /// + /// Registered in /SharpDevelop/CustomTools/ + /// + public interface ICustomTool + { + void GenerateCode(FileProjectItem item, CustomToolContext context); + } + + #region CustomToolContext + /// + /// Provides ProgressMonitor and MessageView to custom tools. + /// Also provides helper methods that are useful for custom tools. + /// + public sealed class CustomToolContext + { + IProject project; + IProgressMonitor progressMonitor; + string outputNamespace; + internal bool RunningSeparateThread; + + public CustomToolContext(IProject project) + : this(project, new DummyProgressMonitor()) + { + } + + public CustomToolContext(IProject project, IProgressMonitor progressMonitor) + { + if (project == null) + throw new ArgumentNullException("project"); + this.project = project; + this.ProgressMonitor = progressMonitor; + } + + /// + /// Returns the project the custom tool is being run for. The IProject interface + /// is not thread-safe! + /// + public IProject Project { + get { return project; } + } + + public string OutputNamespace { + get { return outputNamespace; } + set { outputNamespace = value; } + } + + /// + /// Runs a method asynchronously. Prevents another CustomTool invocation + /// on the same file while action is running. + /// + public void RunAsync(Action action) + { + RunningSeparateThread = true; + System.Threading.ThreadPool.QueueUserWorkItem( + delegate { + try { + action(); + } catch (Exception ex) { + MessageService.ShowError(ex); + } finally { + CustomToolsService.NotifyAsyncFinish(this); + } + }); + } + + static object lockObject = new object(); + static volatile MessageViewCategory customToolMessageView; + + internal static MessageViewCategory StaticMessageView { + get { + if (customToolMessageView == null) { + lock (lockObject) { + if (customToolMessageView == null) { + customToolMessageView = new MessageViewCategory("Custom Tool"); + CompilerMessageView.Instance.AddCategory(customToolMessageView); + } + } + } + return customToolMessageView; + } + } + + /// + /// Returns the message view where custom tools can write to. This member is thread-safe. + /// + public MessageViewCategory MessageView { + get { + return StaticMessageView; + } + } + + public string GetOutputFileName(FileProjectItem baseItem, string additionalExtension) + { + if (baseItem == null) + throw new ArgumentNullException("baseItem"); + if (baseItem.Project != project) + throw new ArgumentException("baseItem is not from project this CustomToolContext belongs to"); + + string newExtension = null; + if (project.LanguageProperties.CodeDomProvider != null) { + newExtension = project.LanguageProperties.CodeDomProvider.FileExtension; + } + if (string.IsNullOrEmpty(newExtension)) { + if (string.IsNullOrEmpty(additionalExtension)) { + newExtension = ".unknown"; + } else { + newExtension = additionalExtension; + additionalExtension = ""; + } + } + if (!newExtension.StartsWith(".")) { + newExtension = "." + newExtension; + } + + return Path.ChangeExtension(baseItem.FileName, additionalExtension + newExtension); + } + + public FileProjectItem EnsureOutputFileIsInProject(FileProjectItem baseItem, string outputFileName) + { + WorkbenchSingleton.AssertMainThread(); + FileProjectItem outputItem = CustomToolsService.FindProjectItem(project, outputFileName); + if (outputItem == null) { + outputItem = new FileProjectItem(project, ItemType.Compile); + outputItem.FileName = outputFileName; + outputItem.DependentUpon = Path.GetFileName(baseItem.FileName); + ProjectService.AddProjectItem(project, outputItem); + ProjectBrowserPad.Instance.ProjectBrowserControl.RefreshView(); + } + return outputItem; + } + + public void WriteCodeDomToFile(FileProjectItem baseItem, string outputFileName, CodeCompileUnit ccu) + { + WorkbenchSingleton.AssertMainThread(); + CodeDomProvider provider = project.LanguageProperties.CodeDomProvider; + CodeGeneratorOptions options = new CodeDOMGeneratorUtility().CreateCodeGeneratorOptions; + + NamedFileOperationDelegate method = delegate(string fileName) { + using (StreamWriter writer = new StreamWriter(fileName, false, System.Text.Encoding.UTF8)) { + if (provider == null) { + writer.WriteLine("No CodeDom provider was found for this language."); + } else { + provider.GenerateCodeFromCompileUnit(ccu, writer, options); + } + } + }; + FileUtility.ObservedSave(method, outputFileName, FileErrorPolicy.Inform); + EnsureOutputFileIsInProject(baseItem, outputFileName); + } + + public void GenerateCodeDomAsync(FileProjectItem baseItem, string outputFileName, Func func) + { + RunAsync(delegate { + CodeCompileUnit ccu = func(); + WorkbenchSingleton.SafeThreadAsyncCall(WriteCodeDomToFile, baseItem, outputFileName, ccu); + }); + } + + public IProgressMonitor ProgressMonitor { + get { return progressMonitor; } + set { + if (value == null) + throw new ArgumentNullException("value"); + progressMonitor = value; + } + } + } + #endregion + + #region CustomToolDescriptor + sealed class CustomToolDescriptor + { + string name; + string fileNamePattern; + string className; + ICustomTool tool; + AddIn addIn; + + public string Name { + get { return name; } + } + + public ICustomTool Tool { + get { + if (tool == null) { + tool = (ICustomTool)addIn.CreateObject(className); + } + return tool; + } + } + + public bool CanRunOnFile(string fileName) + { + if (string.IsNullOrEmpty(fileNamePattern)) // no regex specified + return true; + return Regex.IsMatch(fileName, fileNamePattern, RegexOptions.IgnoreCase); + } + + public CustomToolDescriptor(string name, string fileNamePattern, string className, AddIn addIn) + { + this.name = name; + this.fileNamePattern = fileNamePattern; + this.className = className; + this.addIn = addIn; + } + } + #endregion + + #region CustomToolDoozer + /// + /// Creates CustomToolDescriptor objects. + /// + /// + /// ID used to identify the custom tool. + /// + /// + /// Name of the ICustomTool class. + /// + /// + /// Regular expression that specifies the file names for which the custom tool + /// can be used. Example: "\.res(x|ources)$" + /// + /// Only in /SharpDevelop/CustomTools + /// + /// An CustomToolDescriptor object that wraps a ICustomTool object. + /// + /// + /// <Path name = "/SharpDevelop/CustomTools"> + /// <CustomTool id = "ResXFileCodeGenerator" + /// class = "ResourceEditor.ResourceCodeGeneratorTool" + /// fileNamePattern = "\.res(x|ources)$"/> + /// </Path> + /// + public sealed class CustomToolDoozer : IDoozer + { + /// + /// Gets if the doozer handles codon conditions on its own. + /// If this property return false, the item is excluded when the condition is not met. + /// + public bool HandleConditions { + get { + return false; + } + } + + /// + /// Creates an item with the specified sub items. And the current + /// Condition status for this item. + /// + public object BuildItem(object caller, Codon codon, ArrayList subItems) + { + return new CustomToolDescriptor(codon.Id, codon.Properties["fileNamePattern"], + codon.Properties["class"], codon.AddIn); + } + } + #endregion + + #region CustomToolsService + public static class CustomToolsService + { + class CustomToolRun { + internal CustomToolContext context; + internal string file; + internal FileProjectItem baseItem; + internal ICustomTool customTool; + internal bool showMessageBoxOnErrors; + + public CustomToolRun(CustomToolContext context, string file, FileProjectItem baseItem, ICustomTool customTool, bool showMessageBoxOnErrors) + { + this.context = context; + this.file = file; + this.baseItem = baseItem; + this.customTool = customTool; + this.showMessageBoxOnErrors = showMessageBoxOnErrors; + } + } + + static bool initialized; + static List toolRuns = new List(); + static Dictionary toolDict; + static List customToolList; + static CustomToolRun activeToolRun; + + internal static void Initialize() + { + customToolList = AddInTree.BuildItems("/SharpDevelop/CustomTools", null, false); + toolDict = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (CustomToolDescriptor desc in customToolList) { + toolDict[desc.Name] = desc; + } + + if (!initialized) { + initialized = true; + FileUtility.FileSaved += OnFileSaved; + } + } + + static void OnFileSaved(object sender, FileNameEventArgs e) + { + Solution solution = ProjectService.OpenSolution; + if (solution == null) return; + IProject project = solution.FindProjectContainingFile(e.FileName); + if (project == null) return; + FileProjectItem item = FindProjectItem(project, e.FileName); + if (item == null) return; + if (!string.IsNullOrEmpty(item.CustomTool)) { + RunCustomTool(item, false); + } + } + + internal static FileProjectItem FindProjectItem(IProject project, string fileName) + { + return Linq.Find(Linq.OfType(project.Items), + delegate(FileProjectItem item) { + return FileUtility.IsEqualFileName(item.FileName, fileName); + }); + // return project.Items.OfType().Find( + // item => FileUtility.IsEqualFileName(item.FileName, outputFileName)); + } + + public static IEnumerable GetCustomToolNames() + { + return customToolList.ConvertAll(delegate(CustomToolDescriptor desc) { + return desc.Name; + }); + } + + public static IEnumerable GetCompatibleCustomToolNames(FileProjectItem item) + { + string fileName = item.FileName; + foreach (CustomToolDescriptor desc in customToolList) { + if (desc.CanRunOnFile(fileName)) { + yield return desc.Name; + } + } + } + + public static ICustomTool GetCustomTool(string name) + { + lock (toolDict) { + CustomToolDescriptor tool; + if (toolDict.TryGetValue(name, out tool)) + return tool.Tool; + else + return null; + } + } + + /// + /// Runs the custom tool specified by the base items' CustomTool property on the base item. + /// + public static void RunCustomTool(FileProjectItem baseItem, bool showMessageBoxOnErrors) + { + if (baseItem == null) + throw new ArgumentNullException("baseItem"); + + if (string.IsNullOrEmpty(baseItem.CustomTool)) + return; + + ICustomTool customTool = GetCustomTool(baseItem.CustomTool); + if (customTool == null) { + string message = "Cannot find custom tool '" + baseItem.CustomTool + "'."; + CustomToolContext.StaticMessageView.AppendLine(message); + if (showMessageBoxOnErrors) { + MessageService.ShowError(message); + } + } else { + RunCustomTool(baseItem, customTool, showMessageBoxOnErrors); + } + } + + /// + /// Runs the specified custom tool on the base item. + /// + public static void RunCustomTool(FileProjectItem baseItem, ICustomTool customTool, bool showMessageBoxOnErrors) + { + if (baseItem == null) + throw new ArgumentNullException("baseItem"); + if (customTool == null) + throw new ArgumentNullException("customTool"); + WorkbenchSingleton.AssertMainThread(); + + string fileName = baseItem.FileName; + if (toolRuns.Exists(delegate(CustomToolRun run) { + return FileUtility.IsEqualFileName(run.file, fileName); + })) + { + // file already in queue, do not enqueue it again + return; + } + CustomToolContext context = new CustomToolContext(baseItem.Project); + if (string.IsNullOrEmpty(baseItem.CustomToolNamespace)) { + context.OutputNamespace = GetDefaultNamespace(baseItem.Project, baseItem.FileName); + } else { + context.OutputNamespace = baseItem.CustomToolNamespace; + } + RunCustomTool(new CustomToolRun(context, fileName, baseItem, customTool, showMessageBoxOnErrors)); + } + + /// + /// Gets the namespace the file should have in the specified project. + /// + public static string GetDefaultNamespace(IProject project, string fileName) + { + if (project == null) + throw new ArgumentNullException("project"); + if (fileName == null) + throw new ArgumentNullException("fileName"); + + string relPath = FileUtility.GetRelativePath(project.Directory, Path.GetDirectoryName(fileName)); + string[] subdirs = relPath.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + StringBuilder standardNameSpace = new StringBuilder(project.RootNamespace); + foreach(string subdir in subdirs) { + if (subdir == "." || subdir == ".." || subdir.Length == 0) + continue; + if (subdir.Equals("src", StringComparison.OrdinalIgnoreCase)) + continue; + if (subdir.Equals("source", StringComparison.OrdinalIgnoreCase)) + continue; + standardNameSpace.Append('.'); + standardNameSpace.Append(NewFileDialog.GenerateValidClassName(subdir)); + } + return standardNameSpace.ToString(); + } + + static void RunCustomTool(CustomToolRun run) + { + if (activeToolRun != null) { + toolRuns.Add(run); + } else { + try { + run.customTool.GenerateCode(run.baseItem, run.context); + } catch (Exception ex) { + LoggingService.Error(ex); + run.context.MessageView.AppendLine("Custom tool '" + run.baseItem.CustomTool + "' failed."); + if (run.showMessageBoxOnErrors) { + MessageService.ShowError("Custom tool '" + run.baseItem.CustomTool + + "'failed:" + Environment.NewLine + ex.ToString()); + } + } + if (run.context.RunningSeparateThread) { + activeToolRun = run; + } + } + } + + internal static void NotifyAsyncFinish(CustomToolContext context) + { + WorkbenchSingleton.SafeThreadAsyncCall( + delegate { + activeToolRun = null; + CustomToolRun nextRun = toolRuns[0]; + toolRuns.RemoveAt(0); + RunCustomTool(nextRun); + }); + } + } + #endregion + + #region ExecuteCustomToolCommand + public sealed class ExecuteCustomToolCommand : AbstractMenuCommand + { + public override void Run() + { + FileNode node = Owner as FileNode; + if (node != null) { + FileProjectItem item = node.ProjectItem as FileProjectItem; + if (item != null) { + CustomToolsService.RunCustomTool(item, true); + } + } + } + } + #endregion +} diff --git a/src/Main/Base/Project/Src/Project/Items/FileProjectItem.cs b/src/Main/Base/Project/Src/Project/Items/FileProjectItem.cs index f2cbed63ca..94009915a2 100644 --- a/src/Main/Base/Project/Src/Project/Items/FileProjectItem.cs +++ b/src/Main/Base/Project/Src/Project/Items/FileProjectItem.cs @@ -8,9 +8,12 @@ using System; using System.ComponentModel; using System.IO; - +using System.Drawing.Design; +using System.Windows.Forms; +using System.Windows.Forms.Design; using ICSharpCode.Core; using ICSharpCode.SharpDevelop.Gui; +using ICSharpCode.SharpDevelop.Widgets.DesignTimeSupport; namespace ICSharpCode.SharpDevelop.Project { @@ -67,6 +70,7 @@ namespace ICSharpCode.SharpDevelop.Project [LocalizedProperty("${res:ICSharpCode.SharpDevelop.Internal.Project.ProjectFile.CustomTool}", Description ="${res:ICSharpCode.SharpDevelop.Internal.Project.ProjectFile.CustomTool.Description}")] + [Editor(typeof(CustomToolEditor), typeof(UITypeEditor))] public string CustomTool { get { return base.Properties["Generator"]; @@ -76,6 +80,19 @@ namespace ICSharpCode.SharpDevelop.Project } } + sealed class CustomToolEditor : DropDownEditor + { + protected override Control CreateDropDownControl(ITypeDescriptorContext context, IWindowsFormsEditorService editorService) + { + FileProjectItem item = context.Instance as FileProjectItem; + if (item != null) { + return new DropDownEditorListBox(editorService, CustomToolsService.GetCompatibleCustomToolNames(item)); + } else { + return new DropDownEditorListBox(editorService, CustomToolsService.GetCustomToolNames()); + } + } + } + [LocalizedProperty("${res:ICSharpCode.SharpDevelop.Internal.Project.ProjectFile.CustomToolNamespace}", Description ="${res:ICSharpCode.SharpDevelop.Internal.Project.ProjectFile.CustomToolNamespace.Description}")] public string CustomToolNamespace { @@ -84,6 +101,7 @@ namespace ICSharpCode.SharpDevelop.Project } set { base.Properties["CustomToolNamespace"] = value; + CustomToolsService.RunCustomTool(this, false); } } diff --git a/src/Main/Base/Project/Src/Services/ParserService/ParseProjectContent.cs b/src/Main/Base/Project/Src/Services/ParserService/ParseProjectContent.cs index f70578a4a6..3a167bf855 100644 --- a/src/Main/Base/Project/Src/Services/ParserService/ParseProjectContent.cs +++ b/src/Main/Base/Project/Src/Services/ParserService/ParseProjectContent.cs @@ -157,7 +157,9 @@ namespace ICSharpCode.SharpDevelop UpdateDefaultImports(project.Items.ToArray()); break; case ItemType.Compile: - ParserService.EnqueueForParsing(e.ProjectItem.FileName); + if (System.IO.File.Exists(e.ProjectItem.FileName)) { + ParserService.EnqueueForParsing(e.ProjectItem.FileName); + } break; } } diff --git a/src/Main/Base/Project/Src/Util/Linq.cs b/src/Main/Base/Project/Src/Util/Linq.cs index 4c20dbd547..ecc0407438 100644 --- a/src/Main/Base/Project/Src/Util/Linq.cs +++ b/src/Main/Base/Project/Src/Util/Linq.cs @@ -6,6 +6,7 @@ // using System; +using System.Collections; using System.Collections.Generic; namespace ICSharpCode.SharpDevelop @@ -38,5 +39,29 @@ namespace ICSharpCode.SharpDevelop yield return element; } } + + /// + /// Returns the elements of type T. + /// + public static IEnumerable OfType(IEnumerable input) + { + foreach (object element in input) { + if (element is T) + yield return (T)element; + } + } + + /// + /// Returns the first element in input for which filter is true. + /// Returns default(T) if no element matches the filter. + /// + public static T Find(IEnumerable input, Predicate filter) + { + foreach (T element in input) { + if (filter(element)) + return element; + } + return default(T); + } } } diff --git a/src/Main/ICSharpCode.SharpDevelop.Widgets/Project/DesignTimeSupport/DropDownEditor.cs b/src/Main/ICSharpCode.SharpDevelop.Widgets/Project/DesignTimeSupport/DropDownEditor.cs new file mode 100644 index 0000000000..1be0d21291 --- /dev/null +++ b/src/Main/ICSharpCode.SharpDevelop.Widgets/Project/DesignTimeSupport/DropDownEditor.cs @@ -0,0 +1,78 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.ComponentModel; +using System.Drawing.Design; +using System.Windows.Forms; +using System.Windows.Forms.Design; + +namespace ICSharpCode.SharpDevelop.Widgets.DesignTimeSupport +{ + public abstract class DropDownEditor : UITypeEditor + { + /// + /// Returns the drop down style. + /// + public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) + { + return UITypeEditorEditStyle.DropDown; + } + + public override bool IsDropDownResizable { + get { + return false; + } + } + + /// + /// Shows the drop down editor control in the drop down so the user + /// can change the value. + /// + public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) + { + IWindowsFormsEditorService editorService = null; + + if (provider != null) { + editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService)); + } + + if (editorService != null) { + using (Control control = CreateDropDownControl(context, editorService)) { + SetValue(control, value); + editorService.DropDownControl(control); + value = GetValue(control); + } + } + + return value; + } + + /// + /// Creates the drop down control. + /// + protected abstract Control CreateDropDownControl(ITypeDescriptorContext context, IWindowsFormsEditorService editorService); + + /// + /// Sets the current value in the drop down control. + /// + protected virtual void SetValue(Control control, object value) + { + DropDownEditorListBox listBox = (DropDownEditorListBox)control; + listBox.Value = (string)value; + } + + /// + /// Gets the current value from the drop down control. + /// + protected virtual object GetValue(Control control) + { + DropDownEditorListBox listBox = (DropDownEditorListBox)control; + return listBox.Value; + } + } +} diff --git a/src/AddIns/BackendBindings/WixBinding/Project/Src/PropertyGrid/DropDownEditorListBox.cs b/src/Main/ICSharpCode.SharpDevelop.Widgets/Project/DesignTimeSupport/DropDownEditorListBox.cs similarity index 72% rename from src/AddIns/BackendBindings/WixBinding/Project/Src/PropertyGrid/DropDownEditorListBox.cs rename to src/Main/ICSharpCode.SharpDevelop.Widgets/Project/DesignTimeSupport/DropDownEditorListBox.cs index 877152cf33..8ce802405e 100644 --- a/src/AddIns/BackendBindings/WixBinding/Project/Src/PropertyGrid/DropDownEditorListBox.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Widgets/Project/DesignTimeSupport/DropDownEditorListBox.cs @@ -6,25 +6,31 @@ // using System; +using System.Collections.Generic; using System.ComponentModel; using System.Windows.Forms; using System.Windows.Forms.Design; -namespace ICSharpCode.WixBinding +namespace ICSharpCode.SharpDevelop.Widgets.DesignTimeSupport { public class DropDownEditorListBox : ListBox { IWindowsFormsEditorService editorService; string dropDownValue = String.Empty; - WixXmlAttributePropertyDescriptor propertyDescriptor; + IEnumerable dropDownItems; - public DropDownEditorListBox(ITypeDescriptorContext context, IWindowsFormsEditorService editorService) + public DropDownEditorListBox(IWindowsFormsEditorService editorService, IEnumerable dropDownItems) { + if (editorService == null) + throw new ArgumentNullException("editorService"); + if (dropDownItems == null) + throw new ArgumentNullException("dropDownItems"); + this.editorService = editorService; + this.dropDownItems = dropDownItems; + BorderStyle = BorderStyle.None; - if (context != null) { - propertyDescriptor = context.PropertyDescriptor as WixXmlAttributePropertyDescriptor; - } + AddDropDownItems(); } @@ -61,10 +67,8 @@ namespace ICSharpCode.WixBinding void AddDropDownItems() { - if (propertyDescriptor != null && propertyDescriptor.WixXmlAttribute.HasValues) { - foreach (string item in propertyDescriptor.WixXmlAttribute.Values) { - Items.Add(item); - } + foreach (string item in dropDownItems) { + Items.Add(item); } } diff --git a/src/Main/ICSharpCode.SharpDevelop.Widgets/Project/ICSharpCode.SharpDevelop.Widgets.csproj b/src/Main/ICSharpCode.SharpDevelop.Widgets/Project/ICSharpCode.SharpDevelop.Widgets.csproj index 5247a67087..7ddc54b837 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Widgets/Project/ICSharpCode.SharpDevelop.Widgets.csproj +++ b/src/Main/ICSharpCode.SharpDevelop.Widgets/Project/ICSharpCode.SharpDevelop.Widgets.csproj @@ -64,12 +64,15 @@ + + + \ No newline at end of file